0.136.5

Fixed

  • Local-spec на non-loopback приватном IP больше не блокируется ( follow-up): после v0.136.4 startup-блокировки исчезли, но проверка провайдера Ollama из UI всё равно падала с request failed: Get "http://192.168.2.57:11434/api/tags": blocked internal/private IP: 192.168.2.57. Причина — два уровня SSRF-проверки рассинхронизировались для local-spec на non-loopback: валидация (pre-call) проходила (видела global whitelist через SetSSRFGuard), а dial (runtime) использовал loopbackSSRFGuard с whitelist ["127.0.0.0/8", "::1/128"] — без global подсетей. Поэтому local-spec dial по любому non-loopback приватному IP резался независимо от настроек. Заменён loopbackSSRFGuard (singleton, loopback-only) на localSpecGuard (atomic.Pointer, combined = loopbackCIDRs + global.AllowedCIDRs), который rebuild’ится в SetSSRFGuard и в init(). Теперь Ollama на 192.168.2.57:11434 проходит и валидацию, и dial.

Changed

  • security.SSRFGuard.AllowedCIDRs(): новый публичный метод для чтения whitelist guard’а (defensive copy, nil для no-whitelist). Нужен, чтобы providers.rebuildLocalSpecGuard мог построить combined guard (loopback + global).
  • providers.rebuildLocalSpecGuard(global): строит localSpecGuard = NewSSRFGuard(loopbackCIDRs + global.AllowedCIDRs()) и кеширует localSpecClient (atomic.Pointer). Вызывается из init() и SetSSRFGuard. Security-свойства: loopback (127.0.0.0/8,::1/128) всегда reachable (процесс-local модели — не SSRF-цель); non-loopback private IP reachable iff global whitelist авторизует подсеть; cloud-metadata (169.254.169.254), multicast, reserved, TEST-NET ВСЕГДА блокируются через security.blockedNetworks (blacklist precedence > whitelist).
  • SetSSRFGuard теперь rebuild’ит и localSpecGuard/localSpecClient, не только globalCloudClient. Без этого UI-смена ssrf_whitelist + restart не применялась бы к local-spec dial-пути.
  • slog.Warn once на fallback hit: если localSpecClient или globalCloudClient неожиданно nil при lookup (regression), WARN пишется ровно один раз через sync.Once, чтобы оператор видел деградацию в логах вместо silent per-call аллокации.
  • Уточнён docstring localSpecClient: убрано преувеличенное “defence in depth” заявление — rebuild в SetSSRFGuard является load-bearing update’ом, не опциональным. Transport через getter видит новый guard только если клиент построен ПОСЛЕ rebuild’а; cached-клиент требует явного swap.