security: block cloud-metadata SSRF targets in snapshot navigation + discovery (PER-8614)#2333
Open
Shivanshu-07 wants to merge 2 commits into
Open
security: block cloud-metadata SSRF targets in snapshot navigation + discovery (PER-8614)#2333Shivanshu-07 wants to merge 2 commits into
Shivanshu-07 wants to merge 2 commits into
Conversation
…discovery (PER-8614) Snapshot URLs and discovered subresource requests were navigated/fetched by Chromium without validating the target host, allowing SSRF to cloud instance-metadata endpoints (AWS/Azure/GCP IMDS, ECS task metadata, Alibaba, GCP metadata hostnames) to exfiltrate short-lived cloud credentials. Add isMetadataTarget/assertNotMetadataTarget helpers that block only known metadata endpoints (literal IPs incl. IPv6, metadata hostnames, and any hostname that resolves to a metadata IP to defeat DNS rebinding). Applied before top-level navigation (discovery.js) and before issuing outbound subresource requests (network.js). Loopback and general RFC1918 hosts remain allowed so localhost/internal-staging snapshotting is unaffected. DNS resolution failures are non-fatal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a discovery spec exercising the subresource cloud-metadata block path in network.js (instrumentation + Fetch.failRequest) and a utils spec covering the canonicalHost fallback when a resolved address is not a parseable IPv6 literal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
Snapshot URLs and discovered subresource requests were navigated/fetched by Chromium without validating the target host. A malicious or compromised snapshot URL (or a subresource it references) could point Chromium at a cloud instance-metadata endpoint and exfiltrate short-lived cloud credentials — an SSRF (F-016 / PER-8614).
This PR adds a surgical block of cloud-metadata endpoints only. It deliberately does not block RFC1918 / loopback / link-local ranges in general, because snapshotting
http://localhost:3000and internal staging hosts is a core Percy use case.What is blocked
isMetadataTarget(url)/assertNotMetadataTarget(url)(inpackages/core/src/utils.js) return/throw when the URL host is a known cloud instance-metadata endpoint:169.254.169.254(AWS/Azure/GCP IMDS),169.254.170.2(ECS task metadata),100.100.100.200(Alibaba), and IPv6fd00:ec2::254(AWS IMDS v6).metadata.google.internal,metadata.goog.dns.promises.lookup, all addresses); the request is blocked if any resolved address is a metadata IP. Resolution failures are non-fatal — only a positive metadata match blocks — so offline/localhost snapshots keep working.Where the check is applied
assertNotMetadataTarget(snapshot.url)beforepage.goto(snapshot.url, ...)inpackages/core/src/discovery.js.isMetadataTarget(url)insendResponseResource(packages/core/src/network.js), gating the outboundFetch.continueRequestpath so a page cannot pivot to IMDS via a subresource. On a match the request is failed (Aborted) with a clear warn log:Refusing to fetch resource from cloud metadata endpoint: <host>. Cache hits and root resources are served earlier and never reach this branch, so no behavior changes for them.What is intentionally still allowed
127.0.0.1,localhost,[::1].192.168.x.x,10.x.x.x(internal staging).No behavior is altered for any non-metadata host.
Tests
Added unit tests in
packages/core/test/utils.test.jscovering: metadata IPs (v4 + v6) blocked, metadata hostnames (case-insensitive, trailing dot) blocked, DNS-rebinding to a metadata IP blocked, andlocalhost/127.0.0.1/[::1]/192.168.1.10/10.0.0.5/ a public host all still allowed, plus DNS-failure and unparseable-URL cases.All 10 new specs pass (
yarn workspace @percy/core test --node).node --checkpasses on all edited source files.🤖 Generated with Claude Code