Skip to content

Check the IPv4 address that NAT64 and 6to4 addresses carry - #133

Merged
andrew merged 1 commit into
git-pkgs:mainfrom
simonchrz:fix/safehttp-embedded-ipv4
Nov 10, 2026
Merged

andrew merged 1 commit into
git-pkgs:mainfrom
simonchrz:fix/safehttp-embedded-ipv4

Conversation

@simonchrz

@simonchrz simonchrz commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

The dial-time gate checked IPv6 addresses only as IPv6. net.IP treats IPv4-mapped ::ffff:a.b.c.d as IPv4, but not the other forms that carry an IPv4 address, so on a network with NAT64 64:ff9b::a9fe:a9fe passed the gate and reached 169.254.169.254. A redirect or a URL in upstream metadata pointing at such an address could get past the block list.

Now:

  • addresses under the NAT64 well-known prefix 64:ff9b::/96, 6to4 2002::/16 and IPv4-compatible ::a.b.c.d are also checked as the IPv4 address they carry, so a public one such as 64:ff9b::808:808 still passes;
  • the local-use NAT64 prefix 64:ff9b:1::/48 (RFC 8215) counts as private, since where the IPv4 address sits in it depends on the network.

AllowPrivate and AllowPrivateHosts apply to the embedded address as they do to the address itself.

Summary by CodeRabbit

  • Bug Fixes
    • Dial-time IP checks now also inspect IPv4 addresses embedded in NAT64, 6to4, and IPv4-compatible IPv6 addresses. Blocked embedded addresses are rejected with context identifying the containing IPv6 address.
    • Local-use NAT64 addresses are rejected when private addresses are disallowed. When private addresses are allowed, embedded private IPv4 addresses remain permitted; public embedded addresses continue to pass.

The dial-time gate checked IPv6 addresses only as IPv6, so on a network
with NAT64, 64:ff9b::a9fe:a9fe passed and reached 169.254.169.254.
Addresses under the NAT64 well-known prefix, 6to4 and IPv4-compatible
addresses are now also checked as the IPv4 address they carry, and the
local-use NAT64 prefix 64:ff9b:1::/48 counts as private.
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The gate now checks IPv4 addresses embedded in NAT64, 6to4, and IPv4-compatible IPv6 addresses. It also rejects local-use NAT64 addresses when private addresses are disallowed. Tests cover blocked categories, public addresses, and the AllowPrivate option.

Changes: Embedded IPv4 gate

Layer / File(s) Summary
IPv6 embedded-address recognition
safehttp/safehttp.go
The package documentation lists the supported IPv6 formats. Prefix definitions and embeddedIPv4 identify embedded IPv4 addresses, excluding IPv4-mapped addresses and compatible addresses ending in zero.
Dial-time checks and tests
safehttp/safehttp.go, safehttp/safehttp_test.go
The gate checks extracted IPv4 addresses with checkHost and includes the containing IPv6 address in rejection context. It also rejects local-use NAT64 addresses when private addresses are disallowed. Tests cover blocked categories, public addresses, and AllowPrivate behavior.

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix

Suggested labels: go

Suggested reviewers: andrew

Merge Risk: Minimal · up to 83400

The examined address formats revealed no actionable gap in the outbound gate. The change is mergeable after normal checks.

Security architecture risk: Low · up to 83400

The change strengthens destination restrictions without an identified new bypass or privilege expansion. Remaining uncertainty concerns deployment-specific address translation and proxy resolution, rather than an observed regression.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Attacker-controlled URLs, redirects, metadata download URLs, or destination DNS answers can supply addresses to this boundary. The relevant exposure is network access available to the fetching process or forward proxy, potentially including internal services and cloud metadata where translation and routing permit it. The change reduces accepted destinations; repository evidence does not establish deployment-specific tenant, credential, or network reach.

Security Findings and Attack Paths

  • inferred — Local-use NAT64 addresses are not decoded. AllowPrivate or a matching host exception bypasses their new private-prefix rejection, so an actual translator could still map an accepted address to a destination whose IPv4 category would otherwise be blocked. The base also accepted these addresses; this is a residual, deployment-dependent limitation, not an introduced or worsened PR concern.
  • observed — The unchanged proxy path permits destination names that cannot be resolved locally, leaving their resolution to the forward proxy. Remote resolution is therefore not verified by the local address gate. This pre-existing limitation was not expanded by the embedded-address change; its effective exposure depends on proxy resolution and destination restrictions.

Trust Boundaries and Controls

  • observed — For decoded formats, private-host exceptions are evaluated using the original hostname. AllowPrivate and host exceptions do not disable embedded link-local, multicast, or unspecified rejection, and loopback permission remains separately controlled. Direct DNS dialing uses the checked IP rather than resolving the hostname again.
Pre-merge checks: Passed 5

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: the gate now checks the IPv4 address embedded in NAT64 and 6to4 IPv6 addresses. The raw summary and objectives confirm this is the primary change, and the test file co…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: the gate now checks the IPv4 address embedded in NAT64 and 6to4 IPv6 addresses. The raw summary and objectives confirm this is the primary change, and the test file covers the same prefixes. The title is short and specific, so it passes.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 2 files.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: eac3667e-5f36-4bf4-82f3-8978ebef9f03
Commits

Reviewing files that changed from the base of the PR and between 670a8ef and 8340073.

Files selected for processing: 2

  • safehttp/safehttp.go
  • safehttp/safehttp_test.go

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

@andrew
andrew merged commit c4c9bbf into git-pkgs:main Nov 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants