Repository navigation
Make cors-worker runtime-portable: Node shell + connect-time SSRF guard (#124) - #125
Merged
Merged
Conversation
… guard (#124) - node.js: node:http server over the portable core; config from env (PROXY_TOKENS, PUBLIC, ALLOWED_ORIGINS, PORT, HOST); no deps, no cache. - guarded-fetch.js: node:http(s) fetch whose DNS lookup refuses any non-public resolved address before connecting; IP literals checked too. Core's manual redirects send every hop back through it. - ssrf-policy.js: pure isBlockedAddress(ip) incl. CGNAT, metadata, IPv4-mapped/compatible, NAT64, 6to4 and Teredo forms. - core.js: injectable `fetch` and setup-page `setupSecretStep` options; Cloudflare defaults unchanged. - Tests: shared shell-cases.mjs suite runs against both shells; new DNS-rebinding tests (direct and via redirect) use an injected resolver. - Dockerfile + cors-proxy compose service; .env gitignored; docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- guarded-fetch: cut off chunked bodies past 25 MiB (core can only refuse an advertised length); honest error for unsupported protocols. - node.js: a request path starting with // stays a path. - ADR 0003: record Node as the second entry and the split SSRF check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Deploy Preview for savrlist ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Co-Authored-By: Claude Opus 5.5 <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.
Closes #124.
The CORS proxy can now be self-hosted without a Cloudflare account, as a Node server or Docker container. When self-hosted, it also refuses hostnames that resolve to private addresses.
Changes
infra/cors-worker/node.jsis a Node shell for the portable core, with no dependencies. It readsPROXY_TOKENS,PUBLIC,ALLOWED_ORIGINS,PORTandHOSTfrom the environment and has no cache. Its setup page explains how to setPROXY_TOKENSin the environment.guarded-fetch.jsis afetchbuilt onnode:http/httpswith a guarded DNSlookup. It refuses the connection if any resolved address isn't public, and checks IP literals as well. Core follows redirects itself, so every hop goes back through the guard. Bodies pass through untouched, and chunked bodies over 25 MiB are cut off.ssrf-policy.jsholds the pureisBlockedAddress(ip), which future shells can reuse. It covers loopback, RFC 1918, CGNAT, link-local/metadata, reserved and documentation ranges, and IPv6 ULA, link-local and multicast. It also covers IPv6 forms that embed a private IPv4 address: mapped, compatible, NAT64, 6to4 and Teredo.core.jsgains two injectable options,fetchandsetupSecretStep. Their defaults keep the Cloudflare behaviour.shell-cases.mjsholds the shared behaviour suite and runs against both shells. New DNS-rebinding tests (direct and via a redirect) use an injected resolver, so none of them depend on external DNS.cors-proxycompose service, and.envadded to.gitignore.docs/ENVIRONMENT_VARIABLES.md,docs/DEVELOPMENT.md, the changelog, and an update to ADR 0003.Verification
node --test infra/cors-worker/*.test.mjs infra/cors-stats/*.test.mjspasses 119/119 on Node 20 and Node 24.docker compose up cors-proxy, run with a token:127.0.0.1.nip.iois refused.Known gaps
fetch. The guarded client's response handling is covered by its own tests only.justrecipe, because the repo has no Justfile.🤖 Generated with Claude Code