◐ Off-By-One · answer catalog

js-canonical-url-parser-differential-ssrf-defense

1 answer(s)jsnode20

js-canonical-url-parser-differential-ssrf-defense

📦 Source in repository (JSON)

Answer

Done. The solution is written to ~/solution/SOLUTION.md (also copied to ~/SOLUTION.md), with the runnable implementation in ssrf-guard.js and the verified suite in test.js.

What I diagnosed

The failure class has six distinct root causes, all stemming from letting an attacker-controlled string be interpreted differently at validation time vs. socket-connect time:

  1. Parser differentials — 2130706433, 0x7f.0.0.1, 0177.0.0.1, 127.1, %31%32%<ip-address>, <ip-address>\@evil.com all mean <ip-address> to WHATWG but something else to naive string checks.
  2. IPv6-wrapped IPv4 — ::ffff:<ip-address>, <ip-address> (NAT64), <ip-address> (6to4), Teredo.
  3. Split-horizon / name bypass — public-looking hosts resolving to RFC1918, or mixed public+private answers.
  4. Redirect bypass — first hop public, 302 Location: http://<ip-address>/ internal.
  5. DNS rebinding (TOCTOU) — resolving once to validate and again to connect.
  6. Credential/scheme tricks — userinfo @, file:/gopher:, zone IDs.

The fix

A dependency-free ssrf-guard.js that: - Canonicalizes only with WHATWG new URL, allowlists http/https, and rejects userinfo. - Parses hosts to raw bytes and classifies full reserved ranges, recursing into every IPv4-embedding IPv6 form (mapped/compatible/NAT64/6to4/Teredo). - Resolves all A/AAAA answers and rejects if any is non-public. - Re-validates every redirect hop (manual loop, max 5). - Pins the validated literal IP into the socket while preserving Host + TLS SNI/cert hostname — closing the rebinding window — and re-checks remoteAddress after connect.

Verification

node test.js → PASSED: 92, FAILED: 0, covering all 6 groups including a rebinding resolver that is called exactly once and a fake transport that confirms the pinned public IP is used, plus real loopback sockets proving zero connections on blocked targets and a live https://example.com/ fetch.

I also verified the markdown is truly self-contained: extracting the code fences from SOLUTION.md reproduces ssrf-guard.js and test.js byte-for-byte and the suite passes from those extracted blocks.

Evidence & signatures

# Evidence
- Problem class: js-canonical-url-parser-differential-ssrf-defense
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-10T16:40:26.234Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Build a URL admission layer for a server-side fetcher that must reject private-network targets despite parser differences across WHATWG URLs, DNS resolution, redirects, IPv4-mapped IPv6, and credential or percent-encoding tricks. Given adversarial request chains and changing DNS answers, decide safely at every hop while permitting public CDN redirects and preventing time-of-check/time-of-use rebinding.", "environment": "node20", "language": "js", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "js-canonical-url-parser-differential-ssrf-defense", "provider": "openrouter", "solved_at": "2026-10-10T16:40:26.234Z", "version": "20"}
Generated from the verified corpus · MIT licensedBack to the catalog