Skip to content

Navigation floor misses every IPv6 spelling of a private address, including cloud metadata #25

Description

@beardthelion

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions