Skip to content

Harden web fetch against DNS rebinding#322

Merged
oratis merged 2 commits into
mainfrom
codex/ssrf-api-contract
Jul 26, 2026
Merged

Harden web fetch against DNS rebinding#322
oratis merged 2 commits into
mainfrom
codex/ssrf-api-contract

Conversation

@oratis

@oratis oratis commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • resolve every hostname and fail closed when any A/AAAA answer is private, loopback, link-local, multicast, documentation, benchmark, or otherwise reserved
  • pin each outbound connection to the exact address that passed validation, eliminating DNS rebinding between policy check and socket connect
  • repeat resolution and pinning on every redirect while continuing to strip credentials and request bodies across origins
  • reject embedded URL credentials and preserve explicit external-content fencing through a separately testable renderer

Security properties

  • literal IPv4/IPv6 and mapped IPv6 forms are checked with CIDR-aware parsing
  • mixed public/private DNS answer sets are rejected as a whole
  • redirect responses are cancelled before the next hop
  • the production transport uses one undici version for both Agent and fetch, with Happy Eyeballs disabled so it cannot resolve a second, unvalidated address

Tradeoff

Strict fail-closed handling rejects VPN/TUN DNS modes that synthesize public hostnames into the reserved 198.18.0.0/15 benchmark range (for example Clash fake-IP mode). That is intentional for the secure default: allowing the range would re-open access to locally routed reserved addresses. Operators using fake-IP DNS should use a real-IP/redir-host mode for LISA's web fetch path.

Validation

  • npm run typecheck
  • node --import tsx --test src/tools/web_fetch.test.ts (44 pass)
  • npm test (1477 pass, 1 environment skip, 0 fail)
  • npm run build
  • live transport smoke reached Cloudflare through a validated pinned literal address; its redirect hostname was then rejected because this workstation's TUN DNS returned 198.18.0.0/15, confirming the fail-closed redirect path

Stack

Depends on #321. Review commit a2f10bb only.

@oratis
oratis changed the base branch from codex/tenant-activity-runtime to main July 26, 2026 07:07
@oratis
oratis marked this pull request as ready for review July 26, 2026 07:10
@oratis
oratis merged commit b351e0e into main Jul 26, 2026
1 check 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.

1 participant