feat(api): add proxyUrl for residential/custom proxy egress on the API block#5865
feat(api): add proxyUrl for residential/custom proxy egress on the API block#5865mzxchandra wants to merge 1 commit into
Conversation
|
@mzxchandra is attempting to deploy a commit to the Sim Team on Vercel. A member of the Team first needs to authorize it. |
PR SummaryHigh Risk Overview Wiring: Security: New Deps & tests: Adds Reviewed by Cursor Bugbot for commit 6361b31. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR adds custom HTTP proxy egress to the API block. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (2): Last reviewed commit: "feat(api): add proxyUrl for residential/..." | Re-trigger Greptile |
…I block The HTTP/API block egresses from the app runtime's fixed datacenter IPs via secureFetchWithPinnedIP, so targets behind Cloudflare/WAF that block datacenter IPs (e.g. state .gov license portals) return 403/429 even when the identical request works from a browser. There was no way to route a request through a residential/custom proxy. Add an optional `proxyUrl` field (Advanced) to the API block. When set, the request routes through the given http:// proxy so it egresses from that proxy's IP. Security: - validateAndPinProxyUrl resolves the proxy host's DNS and blocks private/reserved/loopback IPs (same SSRF guard as target URLs), then pins the connection by rewriting the host to the resolved IP (creds/port preserved), closing the DNS-rebinding window. - Restricted to the http: proxy scheme (https/socks rejected) so host pinning is safe without breaking TLS-to-proxy SNI. - Target-IP pinning is intentionally bypassed when a proxy is active (the proxy resolves the target); target URL validation still runs. Threaded block field -> http tool param -> formatRequestParams -> executeToolRequest (validate + pin) -> secureFetchWithPinnedIP, which swaps its pinned Node agent for HttpsProxyAgent/HttpProxyAgent (keyed off target protocol) when proxyUrl is set.
d3555ef to
6361b31
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6361b31. Configure here.
Summary
Adds an optional Proxy URL field to the API block so a request can egress through a residential/custom
http://proxy.The HTTP/API block egresses from the app runtime's fixed datacenter IPs via
secureFetchWithPinnedIP. Targets behind Cloudflare/WAF that block datacenter IPs (e.g. state.govlicense-verification portals) return 403/429 even when the identical request succeeds from a browser, and there was no first-class way to route around it. Users were hand-threading proxy URLs through request bodies and env vars as a workaround.When Proxy URL (under the block's Advanced fields) is set, the request routes through that proxy so it egresses from the proxy's IP.
Security model (proxy is the new SSRF surface once target-IP pinning no longer governs egress):
validateAndPinProxyUrlresolves the proxy host's DNS and blocks private/reserved/loopback IPs — the same guard applied to target URLs (blockslocalhost,127.0.0.1,169.254.169.254,10.x, …).http:proxy scheme (https:/socks*rejected) so host-pinning is safe without breaking TLS-to-proxy SNI. The proxy scheme is independent of the target scheme: anhttps://target still tunnels via CONNECT over the plain-http proxy.validateUrlWithDNS; only its IP pinning is bypassed when a proxy is active (the proxy resolves the target).Wiring: block field → http tool param →
formatRequestParams→executeToolRequest(validate + pin) →secureFetchWithPinnedIP, which swaps its pinned Node agent forHttpsProxyAgent/HttpProxyAgent(keyed off the target protocol) whenproxyUrlis set. Redirects carry the already-pinned proxy URL through, so no hop reopens the rebinding window.Type of Change
Testing
Unit tests added and passing (604 tests across the affected suites), typecheck clean,
check:api-validation:strictand Biome clean:validateAndPinProxyUrl: rejects non-http:scheme, malformed URL, and private/reserved/metadata proxy hosts; accepts a public host and returns a pinned URL with the host rewritten to the resolved IP and credentials/port preserved.executeToolRequest: given aproxyUrlparam,validateAndPinProxyUrlis called and its pinned URL is passed asoptions.proxyUrltosecureFetchWithPinnedIP; an invalid proxy surfacesInvalid proxy URL: ….formatRequestParams:proxyUrlpasses through (trimmed) for a non-empty string; omitted for blank/whitespace/absent.Manual E2E against a local logging proxy (requiring Basic auth) with the app running:
200, proxy logs a CONNECT tunnel; clearing the Proxy URL →200with no proxy log line (direct egress) — confirms the proxy is used only when set.HttpProxyAgent(absolute-URI) path.https://…/socks5://…proxy → rejected with the scheme error;http://127.0.0.1:9999→ connection error; wrong creds → proxy407surfaced as a block error;http://169.254.169.254→ blocked-IP error.Reviewers should focus on the security model in
apps/sim/lib/core/security/input-validation.server.ts(validateAndPinProxyUrl+ the agent branch insecureFetchWithPinnedIP).Checklist
Screenshots/Videos
The new Proxy URL field appears under the API block's Advanced fields with placeholder
http://user:pass@proxy.host:port.