v0.0.18
🔒 Security
- Fixed Server-Side Request Forgery (SSRF) in vision API
image_url(common/image/image.go):GetImageFromUrlnow resolves the URL host before any HTTP request and rejects targets that resolve to any private / reserved IP range: IPv4 loopback, RFC1918, link-local (169.254.169.254cloud metadata), CGNAT100.64.0.0/10, IPv6 ULAfc00::/7, IPv6 link-localfe80::/10, IPv4-mapped IPv6::ffff:127.0.0.1, and so on.- Replaced the bare
http.Get(url)withclient.UserContentRequestHTTPClient.Get(url)so the image fetch honorsUSER_CONTENT_REQUEST_PROXYandUSER_CONTENT_REQUEST_TIMEOUTlike the other two URL fetches in the same file — closing a small audit-bypass where this one call would otherwise bypass the configured outbound proxy. - Only
http/httpsschemes are allowed; other schemes (file://,gopher://, etc.) are rejected.
- New
IsPrivateIP(ip net.IP) boolhelper (common/network/ip.go):- Covers IPv4 loopback / RFC1918 / link-local / CGNAT /
0.0.0.0/8/ multicast / reserved; IPv6 loopback / ULA / link-local / multicast; and IPv4-mapped IPv6. - Branched on
ip.To4()so that a plain IPv4 input is not accidentally matched against the IPv4-mapped IPv6 CIDR (::ffff:0:0/96) — that mistake would have wrongly flagged172.15.255.255(public) as private.
- Covers IPv4 loopback / RFC1918 / link-local / CGNAT /
- New
IsPrivateIPunit tests (common/network/ip_test.go): cover loopback, RFC1918, link-local (cloud metadata), CGNAT, IPv6 ULA, IPv4-mapped IPv6 private ranges, and confirm that public IPs and boundary addresses (172.15.x,172.32.x,100.63.x) are allowed.
⚠️ Upgrade Notes
- Zero database migration: backend-only logic fix; no schema changes.
- A backend restart is required:
GetImageFromUrlandIsPrivateIPonly take effect after the binary is rebuilt and theone-api-proprocess is restarted. - Behavior change:
- Any vision-API call (Anthropic / Ollama / Gemini adaptors etc.) whose
image_urlresolves to an internal / private address (127.0.0.1,10.x.x.x,192.168.x.x,172.16-31.x.x,169.254.169.254, IPv6 ULA, IPv4-mapped IPv6 of these, etc.) is now silently rejected, consistent with the existing_ , _ , _ := image.GetImageFromUrl(...)error-discarding convention used by all callers. - If you have a legitimate image source on an internal network (e.g. a self-hosted CDN), the default policy will block it. An allow-list configuration is intentionally not part of this release (kept minimal per upstream PR #2390). Reach out if you need one.
- Any vision-API call (Anthropic / Ollama / Gemini adaptors etc.) whose
- Build & test:
go build ./...passes.go test ./common/network/... ./common/image/...is green (26 newIsPrivateIPassertions; pre-existingTestIsIpInSubnetstill green).
- Blast radius: four callers consume
GetImageFromUrl/GetImageSize—relay/adaptor/anthropic/main.go:136,relay/adaptor/provider/ollama/main.go:48,relay/adaptor/provider/gemini/main.go:118,relay/adaptor/openai/token.go:206. None of their signatures change; rejections are transparent to them thanks to the existing silent-discard semantics.
参考 / References: