Problem
ipv6IsPrivateOrLocal (src/review/content-lane/safe-url.ts:58-81) is the shared SSRF guard used by hostIsPrivateOrLocal → isSafeHttpUrl/isSafeEndpointUrl, consumed by src/review/visual/shot.ts (visual-capture browser navigation), src/orb/relay.ts (relay URL validation), and src/review/content-lane/index.ts/source-evidence.ts (server-side HEAD/GET fetches verifying submitted source URLs).
It correctly recognizes the IPv4-mapped IPv6 form (::ffff:a.b.c.d, serialized by new URL() as ::ffff:7f00:1) via its hex regex. It misses the older, distinct IPv4-compatible IPv6 form (::a.b.c.d, no ffff:), which WHATWG's URL parser also accepts and normalizes to a bracket-free hex suffix without the ffff: marker (e.g. ::127.0.0.1 → ::7f00:1). That normalized form matches none of the three checks in ipv6IsPrivateOrLocal.
Verified empirically (extracted the real TS logic and ran it against Node's actual new URL() normalization):
https://[::127.0.0.1]/x => hostname "[::7f00:1]" -> isSafeHttpUrl = true (BYPASS - loopback)
https://[::169.254.169.254]/x => hostname "[::a9fe:a9fe]" -> isSafeHttpUrl = true (BYPASS - cloud metadata IP)
https://[::ffff:127.0.0.1]/x => correctly rejected (false) - the handled sibling form, for comparison
A submitted content-lane URL field (or any isSafeHttpUrl/isSafeEndpointUrl consumer) using https://[::169.254.169.254]/... or https://[::127.0.0.1]/... passes the SSRF guard as "safe," letting source-evidence.ts's server-side fetcher (or the visual-capture browser) reach the instance's own loopback/cloud-metadata service.
Area
src/review/content-lane/safe-url.ts (ipv6IsPrivateOrLocal).
Fix
The file already solves this exact problem for the ::ffff: case two lines above — generalize that same hex-pair-to-IPv4 conversion to also cover the bracket-free/non-ffff:-prefixed form, so ::a.b.c.d normalizes and is checked the same way ::ffff:a.b.c.d already is.
Notes
Filed maintainer-only per the maintainer's own call: currently self-host only (no live hosted/multi-tenant exposure), and the vulnerable code is already public in this OSS repo either way. Fix directly rather than opening to contributors.
Resources
src/review/content-lane/safe-url.ts:58-81
- Consumers:
src/review/visual/shot.ts, src/orb/relay.ts, src/review/content-lane/index.ts, src/review/content-lane/source-evidence.ts
Problem
ipv6IsPrivateOrLocal(src/review/content-lane/safe-url.ts:58-81) is the shared SSRF guard used byhostIsPrivateOrLocal→isSafeHttpUrl/isSafeEndpointUrl, consumed bysrc/review/visual/shot.ts(visual-capture browser navigation),src/orb/relay.ts(relay URL validation), andsrc/review/content-lane/index.ts/source-evidence.ts(server-side HEAD/GET fetches verifying submitted source URLs).It correctly recognizes the IPv4-mapped IPv6 form (
::ffff:a.b.c.d, serialized bynew URL()as::ffff:7f00:1) via itshexregex. It misses the older, distinct IPv4-compatible IPv6 form (::a.b.c.d, noffff:), which WHATWG's URL parser also accepts and normalizes to a bracket-free hex suffix without theffff:marker (e.g.::127.0.0.1→::7f00:1). That normalized form matches none of the three checks inipv6IsPrivateOrLocal.Verified empirically (extracted the real TS logic and ran it against Node's actual
new URL()normalization):A submitted content-lane URL field (or any
isSafeHttpUrl/isSafeEndpointUrlconsumer) usinghttps://[::169.254.169.254]/...orhttps://[::127.0.0.1]/...passes the SSRF guard as "safe," lettingsource-evidence.ts's server-side fetcher (or the visual-capture browser) reach the instance's own loopback/cloud-metadata service.Area
src/review/content-lane/safe-url.ts(ipv6IsPrivateOrLocal).Fix
The file already solves this exact problem for the
::ffff:case two lines above — generalize that same hex-pair-to-IPv4 conversion to also cover the bracket-free/non-ffff:-prefixed form, so::a.b.c.dnormalizes and is checked the same way::ffff:a.b.c.dalready is.Notes
Filed
maintainer-onlyper the maintainer's own call: currently self-host only (no live hosted/multi-tenant exposure), and the vulnerable code is already public in this OSS repo either way. Fix directly rather than opening to contributors.Resources
src/review/content-lane/safe-url.ts:58-81src/review/visual/shot.ts,src/orb/relay.ts,src/review/content-lane/index.ts,src/review/content-lane/source-evidence.ts