Skip to content

feat(api): add proxyUrl for residential/custom proxy egress on the API block#5865

Open
mzxchandra wants to merge 1 commit into
simstudioai:stagingfrom
mzxchandra:feat/api-proxy-url
Open

feat(api): add proxyUrl for residential/custom proxy egress on the API block#5865
mzxchandra wants to merge 1 commit into
simstudioai:stagingfrom
mzxchandra:feat/api-proxy-url

Conversation

@mzxchandra

Copy link
Copy Markdown
Contributor

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 .gov license-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):

  • validateAndPinProxyUrl resolves the proxy host's DNS and blocks private/reserved/loopback IPs — the same guard applied to target URLs (blocks localhost, 127.0.0.1, 169.254.169.254, 10.x, …).
  • The proxy connection is then pinned by rewriting the host to the resolved IP (credentials/port preserved), closing the DNS-rebinding (TOCTOU) window.
  • Restricted to the 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: an https:// target still tunnels via CONNECT over the plain-http proxy.
  • The target URL is still validated with validateUrlWithDNS; only its IP pinning is bypassed when a proxy is active (the proxy resolves the target).

Wiring: block field → http tool param → formatRequestParamsexecuteToolRequest (validate + pin) → secureFetchWithPinnedIP, which swaps its pinned Node agent for HttpsProxyAgent/HttpProxyAgent (keyed off the target protocol) when proxyUrl is set. Redirects carry the already-pinned proxy URL through, so no hop reopens the rebinding window.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

Unit tests added and passing (604 tests across the affected suites), typecheck clean, check:api-validation:strict and 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 a proxyUrl param, validateAndPinProxyUrl is called and its pinned URL is passed as options.proxyUrl to secureFetchWithPinnedIP; an invalid proxy surfaces Invalid proxy URL: ….
  • formatRequestParams: proxyUrl passes 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:

  • HTTPS target + Proxy URL → 200, proxy logs a CONNECT tunnel; clearing the Proxy URL → 200 with no proxy log line (direct egress) — confirms the proxy is used only when set.
  • HTTP target → routed via the HttpProxyAgent (absolute-URI) path.
  • https://… / socks5://… proxy → rejected with the scheme error; http://127.0.0.1:9999 → connection error; wrong creds → proxy 407 surfaced 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 in secureFetchWithPinnedIP).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

The new Proxy URL field appears under the API block's Advanced fields with placeholder http://user:pass@proxy.host:port.

@mzxchandra
mzxchandra requested a review from a team as a code owner July 22, 2026 22:31
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@mzxchandra is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes outbound HTTP security and egress: user-controlled proxies bypass target IP pinning and expand the attack surface, though mitigated by scheme limits, DNS/IP blocking, and pinning.

Overview
Adds an optional Proxy URL (Advanced) on the API block so outbound calls can egress through a user-supplied http:// proxy (e.g. residential IP) instead of only the app’s datacenter IPs.

Wiring: proxyUrl flows from the block → HTTP tool params → formatRequestParams (trimmed; omitted when blank) → external executeTool path, which calls validateAndPinProxyUrl and passes the pinned URL into secureFetchWithPinnedIP.

Security: New validateAndPinProxyUrl treats the proxy as the SSRF surface: http: only (no https/socks), DNS + private/reserved IP checks via existing URL validation, then rewrites the proxy host to the resolved IP (IPv6 bracketed) while keeping creds/port. With a proxy set, fetch uses HttpProxyAgent / HttpsProxyAgent by target scheme and skips target IP pinning (the proxy resolves the destination); the target URL is still validated as before.

Deps & tests: Adds http-proxy-agent and https-proxy-agent; unit tests for proxy validation, tool execution, and param formatting; testing mock extended for validateAndPinProxyUrl.

Reviewed by Cursor Bugbot for commit 6361b31. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds custom HTTP proxy egress to the API block. The main changes are:

  • Adds a user-only Proxy URL field to the API block and HTTP tool.
  • Validates proxy hosts against private and reserved addresses.
  • Pins proxy connections to resolved IPv4 or IPv6 addresses.
  • Routes HTTP and HTTPS targets through the matching proxy agent.
  • Preserves the pinned proxy across redirects.

Confidence Score: 5/5

This looks safe to merge.

  • The IPv6 fix keeps the validated proxy address pinned.
  • Redirects continue using the same pinned proxy URL.
  • No blocking issue remains in the updated code.

Important Files Changed

Filename Overview
apps/sim/lib/core/security/input-validation.server.ts Adds proxy validation, IP pinning, proxy-agent routing, and the IPv6 hostname correction.
apps/sim/lib/core/security/input-validation.test.ts Adds proxy validation tests, including coverage for pinned IPv6 proxy URLs.
apps/sim/tools/index.ts Validates and pins the supplied proxy URL before secure request execution.
apps/sim/tools/utils.ts Trims and forwards non-empty proxy URL parameters.

Reviews (2): Last reviewed commit: "feat(api): add proxyUrl for residential/..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/security/input-validation.server.ts Outdated
Comment thread apps/sim/tools/utils.ts
…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.
@mzxchandra
mzxchandra force-pushed the feat/api-proxy-url branch from d3555ef to 6361b31 Compare July 22, 2026 22:41
@mzxchandra

Copy link
Copy Markdown
Contributor Author

@greptile

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@mzxchandra

Copy link
Copy Markdown
Contributor Author

@waleedlatif1

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