checkNavigationTarget matches exact hostname strings and dotted-quad IPv4, so the same destinations written as IPv6 literals go straight past it. The one that matters is cloud metadata, which docs/architecture.md:160 says is refused under every configuration.
Verified against main at 93ff1b1 by calling checkNavigationTarget directly, both values of allowPrivateHosts:
| target |
allowPrivate=false |
allowPrivate=true |
http://169.254.169.254/ |
denied |
denied |
http://metadata.google.internal/ |
denied |
denied |
http://[::ffff:169.254.169.254]/ |
allowed |
allowed |
http://[fd00:ec2::254]/ (AWS IMDS over IPv6) |
allowed |
allowed |
http://metadata.google.internal./ (root dot) |
allowed |
allowed |
http://[::ffff:127.0.0.1]/ |
allowed |
allowed |
http://[fe80::1]/, http://[fc00::1]/ |
allowed |
allowed |
isPrivateIpv4 returns false for anything that is not four dotted parts, and the two hostname sets compare strings, so no IPv6 form reaches either check. The private-host opt-in cannot save the metadata rows: they are supposed to be refused ahead of it.
Two more forms get there as well, both of which resolve to the IPv4 address on a real network: http://[64:ff9b::169.254.169.254]/, the well-known NAT64 prefix, translated by the gateway on an IPv6-only network, and http://[::169.254.169.254]/, the deprecated IPv4-compatible form.
The same function backs checkAgentEndpoint (server/src/agents/endpoint.ts:43), so this is not only a browser problem. An agent endpoint is a URL this server POSTs to on every run, and on the laptop configuration where the private-host opt-in is on, http://[::ffff:169.254.169.254]/ag-ui currently registers as a valid agent.
I have a fix ready and will open a PR against this issue: reduce the hostname to one form before comparing anything (drop the root dot, unwrap the IPv4 carried in the low 32 bits under ::ffff:0:0/96, 64:ff9b::/96 and ::/96), then classify IPv6 loopback, link-local and unique-local the way RFC1918 already is, so they sit behind the same opt-in. :: and ::1 keep their existing handling rather than being read as 0.0.0.0 and 0.0.0.1.
checkNavigationTargetmatches exact hostname strings and dotted-quad IPv4, so the same destinations written as IPv6 literals go straight past it. The one that matters is cloud metadata, whichdocs/architecture.md:160says is refused under every configuration.Verified against
mainat 93ff1b1 by callingcheckNavigationTargetdirectly, both values ofallowPrivateHosts:http://169.254.169.254/http://metadata.google.internal/http://[::ffff:169.254.169.254]/http://[fd00:ec2::254]/(AWS IMDS over IPv6)http://metadata.google.internal./(root dot)http://[::ffff:127.0.0.1]/http://[fe80::1]/,http://[fc00::1]/isPrivateIpv4returns false for anything that is not four dotted parts, and the two hostname sets compare strings, so no IPv6 form reaches either check. The private-host opt-in cannot save the metadata rows: they are supposed to be refused ahead of it.Two more forms get there as well, both of which resolve to the IPv4 address on a real network:
http://[64:ff9b::169.254.169.254]/, the well-known NAT64 prefix, translated by the gateway on an IPv6-only network, andhttp://[::169.254.169.254]/, the deprecated IPv4-compatible form.The same function backs
checkAgentEndpoint(server/src/agents/endpoint.ts:43), so this is not only a browser problem. An agent endpoint is a URL this server POSTs to on every run, and on the laptop configuration where the private-host opt-in is on,http://[::ffff:169.254.169.254]/ag-uicurrently registers as a valid agent.I have a fix ready and will open a PR against this issue: reduce the hostname to one form before comparing anything (drop the root dot, unwrap the IPv4 carried in the low 32 bits under
::ffff:0:0/96,64:ff9b::/96and::/96), then classify IPv6 loopback, link-local and unique-local the way RFC1918 already is, so they sit behind the same opt-in.::and::1keep their existing handling rather than being read as0.0.0.0and0.0.0.1.