0.134.93

Security

  • SSRF-валидация api_base для cloud-провайдеров: ResolveAndValidateAPIBase в новом internal/providers/ssrf.go блокирует RFC 1918 / loopback / link-local / cloud-metadata (169.254.169.254) для cloud-провайдеров перед любым outbound-запросом. Локальные провайдеры (ollama, lmstudio, vllm) exempt только если api_base действительно loopback (IP-литерал 127.0.0.0/8/::1 или localhost) — закрыт bypass через регистрацию Provider:"ollama" с api_base на метадату AWS. На rejection логируется host (без userinfo) серверсайд, клиенту возвращается generic-сообщение (не раскрывает blocklist-топологию).
  • DNS-rebinding / TOCTOU для model-list: listModelsOpenAICompat и listModelsOllama переведены с голого &http.Client{} на safeHTTPClientForSpec (transport с dial-time IP re-check из security.SSRFGuard.SafeHTTPClient). Local-spec использует loopback-whitelisted guard, cloud-spec — активный. pre-call ValidateURL остаётся как defence-in-depth.
  • Anti-exfiltration для api_base override: в Test (/providers/{id}/test) при переопределении api_base из формы сохранённый расшифрованный api_key НЕ фолбэчится — иначе API-клиент мог бы перенаправить credential на произвольный хост.

Fixed

  • Валидация api_key в TestConfig: пустой api_key для cloud-провайдеров теперь 400 (раньше молча тестировался → misleading 401 или ложный success). Локальные провайдеры (ollama/lmstudio/vllm) остаются exempt.
  • 413 вместо 400 на oversized body: MaxBytesReader-ошибки в Test/TestConfig теперь возвращают 413 Request Entity Too Large (через errors.As(*http.MaxBytesError)), а не вводящий в заблуждение 400 invalid json.
  • writeJSONError вместо http.Error: invalid-id / not-found пути в Test переведены на writeJSONError для консистентной JSON-формы ответа.
  • hasOverride на per-field checks: дискриминатор «тело содержит override» переписан с хрупкого body != providerTestBody{} на явную проверку body.Provider != "" || body.APIKey != "" || body.APIBase != "", устойчивую к будущим полям.

Changed

  • maxProviderTestBodyBytes (1 « 16) как именованная константа вместо дублирующегося магического числа в Test/TestConfig.
  • IsLocalSpec — единый source of truth для классификации local/cloud; ListModelsForSpec и ResolveAndValidateAPIBase делегируют к нему.
  • SetSSRFGuard на atomic.Pointer: потокобезопасная установка/чтение guard’а (хотя контракт остаётся «вызов ровно один раз из main.go»).
  • Stale resolveAPIBase удалён; runDiagnostics теперь использует ResolveAndValidateAPIBase, чтобы диагностики сообщали URL, который фактически валидировался.
  • Комментарий в +page.svelte переведён на английский и обновлён: fallback api_key отключён, если форма переопределяет api_base.

Added

  • Тесты: internal/providers/ssrf_test.go (16 тестов — SSRF для cloud/local/nil-spec, loopback-enforcement, DNS-bypass, error disclosure, atomic.Pointer contract), TestTest_APIBaseOverrideDoesNotExfiltrateSavedKey, TestTestConfig_MissingAPIKey (+ local-exempt), TestTestConfig_LocalProviderWithoutAPIKey, TestTest_InvalidID_UsesJSONError, TestTest_BodyTooLarge, TestTestConfig_BodyTooLarge.