Paste a URL the agent or backend is about to fetch. The analyzer decodes the host (decimal, hex, octal, alias), checks against cloud metadata IPs, RFC 1918, link-local, loopback, and unusual URL schemes, and surfaces eight SSRF-relevant findings.
Runs the canonical 4-stage hardening flow on the server: parse → pre-flight rule check → DNS resolve + re-check every IP (DNS-rebinding defence) → bounded fetch. Each stage's pass/fail is shown below.
169.254.169.254 (AWS, GCP, Azure) and 100.100.100.200 (Alibaba) are the SSRF target. Reading IAM credentials, service account tokens, and user-data from these endpoints has been the root cause of Capital One, multiple GCP misconfigurations, and many bug-bounty payouts. On AWS, only IMDSv2 (with required token) blocks naive SSRF. On all three, host-level firewalling of link-local from application processes is the durable fix.
169.254.169.254
standards: OWASP Top 10 A10 (SSRF) · CWE-918 · AWS IMDSv2 guidance · Capital One 2019 (post-mortem)
CRITICALSSRF08
Cloud metadata path pattern
Path matches a known cloud metadata URL (AWS /latest/meta-data, GCP /computeMetadata/v1, Azure /metadata/instance). Even if the host validator passed, the path strongly suggests an exfiltration attempt.
Pick one of 10 catalog payloads. Two fetchers receive the same request: a naive one (substring blocklist on the raw URL) and a hardened one (scheme allowlist → CRLF check → header strip → host canonicalisation → IP blocklist → IP-pinned fetch). The deterministic transcripts show what each would put on the wire and which hardened rule blocks the call. Reproduce any scenario over the wire with POST /api/ssrf-fetch { "scenarioId": "...", "mode": "..." }.
169.254.169.254 expressed as the decimal 32-bit integer 2852039166. URL parsers in many languages happily resolve this; string-match validators that look for '169.254' miss it.
GET http://2852039166/latest/meta-data/iam/security-credentials/
Dial the validated IP; never re-resolve the hostname.
BLOCK [H-IPRANGE]
169.254.169.254 is cloud metadata
hardened fetcher finished
request refused before any DNS lookup or socket open; no data left the application
What this proves
Substring blocklists on the raw URL fail against every encoded-host, IPv6, header-smuggle, and DNS-rebinding scenario in the catalog. The naive fetcher would put the malicious bytes on the wire in all 10.
The hardened rule chain (scheme → CRLF → strip-headers → canonicalise → blocklist → pin-IP) refuses every catalog request before any socket open. Each block is tagged with the rule id (H-SCHEME, H-CRLF, H-HEADERS, H-CANON, H-IPRANGE, H-IPV6, H-PINIP) so you can map it back to the implementation in lib/ssrf.ts.
Both fetchers are deterministic transcripts. The hardened-rule implementation is the same code a real backend would ship; only the wire transmission is faked, because emitting real metadata-IP / gopher:// / Redis-CRLF traffic from a public service would be both irresponsible and blocked by every modern hosting platform's egress firewall.