fix(browse): block the whole IPv4 link-local range (169.254.0.0/16) in navigation URL validation - #2811
Open
gabrielrondon wants to merge 1 commit into
Open
Conversation
…n navigation URL validation validateNavigationUrl blocked the cloud metadata IP 169.254.169.254 by exact match but allowed the rest of the IPv4 link-local range 169.254.0.0/16. The same file already blocks IPv6 link-local (fe80::/10) and ULA (fc00::/7) as whole ranges, and session-persist.ts blocks the whole IPv4 range for cookie domains (/^169\.254\./, "whole link-local block, not just metadata"). The navigation gate, the primary control against a prompt-injected page reaching an internal address, was the weaker one. The gap leaves the container credential endpoints reachable: 169.254.170.2 (AWS ECS/Fargate task credentials) and 169.254.170.23 (EKS Pod Identity). Block the whole /16 via isBlockedIpv4LinkLocal(), covering dotted-quad and the IPv4-mapped IPv6 form the URL parser normalizes to (::ffff:a9fe:xxxx). Wired into the navigation gate and both branches of the DNS-rebinding check. Numeric IPv4 forms are covered because the parser normalizes them first. RFC1918 private ranges and loopback stay allowed, so local QA is unchanged. Claude-Session: https://claude.ai/code/session_01KjBowH1wmRk6TZUwWGfo3b
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
validateNavigationUrlblocks the cloud metadata IP169.254.169.254by exact match but allows the rest of the IPv4 link-local range169.254.0.0/16. This blocks the whole /16, matching the range treatment the same file already gives IPv6 link-local (fe80::/10) and ULA (fc00::/7), and the cookie-domain guard insession-persist.ts.Why
The navigation gate is the control that stops a prompt-injected page from steering the daemon's Chrome to an internal address (threat note in
browse/test/url-validation.test.tsandbrowse/src/write-commands.ts). Today it covers IPv4 unevenly:BLOCKED_IPV6_PREFIXESinurl-validation.ts).169.254.169.254(BLOCKED_METADATA_HOSTS).session-persist.tsalready blocks the whole range for cookie domains:if (/^169\.254\./.test(d)) return true; // link-local incl. cloud metadata, with a test asserting "whole link-local block, not just metadata".So the primary navigation control is weaker than a guard next to it, and out of line with the project's own policy. The gap leaves other link-local endpoints reachable, notably the container credential services that hand out IAM credentials:
169.254.170.2(AWS ECS / Fargate task credentials,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI)169.254.170.23(EKS Pod Identity)Impact is defense-in-depth: it matters when the browse daemon runs in a cloud or container environment that exposes a link-local credentials service (CI on ECS/Fargate, for example). The unconditional part is the inconsistency itself.
Fix
isBlockedIpv4LinkLocal()matches169.254.0.0/16in dotted-quad form and in the IPv4-mapped IPv6 form the URL parser normalizes to (::ffff:a9fe:xxxx, wherea9fe==169.254). It is wired into the three places the IPv6 range check already runs: the direct navigation gate, and both the A and AAAA branches of the DNS-rebinding check. Numeric IPv4 forms (hex, octal, decimal) are covered because the URL parser normalizes them to dotted-quad before the check.Only dotted-quad literals match, so real hostnames like
169.254.example.comare left to DNS resolution rather than blocked as strings. RFC1918 private ranges (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) and loopback stay allowed, so the local-QA use case is unchanged.Tests
browse/test/url-validation.test.ts: the ECS and EKS credential endpoints, a generic in-range address, hex and IPv4-mapped forms, plus two negatives (a169.254.-prefixed hostname is not string-blocked, and private dev servers still pass). Full file: 59 pass, 0 fail.