Skip to content

SSRF bypass in safe-url.ts: IPv4-compatible IPv6 notation (::a.b.c.d) not recognized as private/local #7777

Description

@JSONbored

Problem

ipv6IsPrivateOrLocal (src/review/content-lane/safe-url.ts:58-81) is the shared SSRF guard used by hostIsPrivateOrLocalisSafeHttpUrl/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

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions