Skip to content

fix: allow opt-in proxy env passthrough in ctx_fetch_and_index (CTX_FETCH_ALLOW_PROXY) - #1041

Open
vishnujayvel wants to merge 2 commits into
mksglu:mainfrom
vishnujayvel:fix/1039-proxy-env
Open

fix: allow opt-in proxy env passthrough in ctx_fetch_and_index (CTX_FETCH_ALLOW_PROXY)#1041
vishnujayvel wants to merge 2 commits into
mksglu:mainfrom
vishnujayvel:fix/1039-proxy-env

Conversation

@vishnujayvel

Copy link
Copy Markdown

What / Why / How

ctx_fetch_and_index embeds a subprocess script (buildFetchCode) that, since #476, deletes every proxy env var before fetch() runs. That is a deliberate CWE-918 / CWE-441 control: a configured HTTP_PROXY / HTTPS_PROXY / ALL_PROXY would move DNS resolution to the proxy, so the connect-time dns.lookup / dnsPromises.lookup re-validation never sees a rebound IP. Corporate and audited environments that must route egress through a proxy therefore see silent direct egress — reported in #1039 by @DenisBalan.

This PR does not reverse the #476 default. Unconditional preservation would re-open the SSRF control that #476 closed. Instead it implements DenisBalan's proposed option 1: explicit operator opt-in.

  • Default (unchanged): generated source still strips HTTP_PROXY / HTTPS_PROXY / ALL_PROXY (and lowercase / npm_config_* siblings). The fix(security): close DNS rebinding gap in ctx_fetch_and_index SSRF guard #476 pinning test remains green.
  • Opt-in: when the process that generates the subprocess script has CTX_FETCH_ALLOW_PROXY=1, the strip block is omitted so Node/undici EnvHttpProxyAgent can honor the configured proxy. Gate style matches the existing CTX_FETCH_STRICT === "1" precedent in server.ts.

Why parent-side resolve alone is not enough

A single parent-side ssrfGuard DNS lookup is insufficient against rebinding: an attacker can return a public IP for the pre-flight check and a blocked IP (e.g. IMDS 169.254.169.254) for the subprocess connect. The compensating control is the connect-time dns.lookup re-validation inside the generated script. A proxy moves that resolution out of the subprocess's sight, so preserving proxy vars must be operator-consented, not default.

Existing carve-out

The original design already allows legit proxy use on ctx_execute (executor #buildSafeEnv does not put proxy keys on the DENIED set). The strip lived only in the fetch-subprocess path. Operators who need corporate egress for ctx_fetch_and_index can now set CTX_FETCH_ALLOW_PROXY=1 without weakening the default sandbox posture for everyone else.

Fixes #1039. Credit: @DenisBalan for the report, the exact repro against the embedded delete block, and the opt-in design option this implements.

Limitations

  • When CTX_FETCH_ALLOW_PROXY=1 is active there is no runtime signal that the rebinding defense is relaxed; a stderr note is offered as a follow-up if maintainers want operators to see the weaker posture.

Affected platforms

  • All platforms

(MCP-level fetch path; not adapter-specific.)

Test plan

Checklist

  • Tests added/updated (default pin + opt-in preservation)
  • npm test passes (full suite not run; tests/core/server.test.ts + typecheck green)
  • npm run typecheck passes
  • Docs updated if needed — N/A (env gate documented in code comments; README mention optional follow-up)
  • No Windows path regressions (forward slashes only)
  • Targets next branch (unless hotfix) — staged off main at base 3bad0f4; retarget to next on ship if preferred
Cross-platform notes

No path/hook changes. Env var names are the standard cross-platform proxy set (HTTP(S)PROXY, ALL_PROXY, npm_config*). Opt-in flag: CTX_FETCH_ALLOW_PROXY=1.

…#1039)

ctx_fetch_and_index embeds a subprocess via buildFetchCode that strips
proxy env vars before fetch() so connect-time DNS rebinding guards stay
on-path (mksglu#476 / CWE-918,441). Corporate networks that set HTTP_PROXY
etc. therefore saw silent direct egress (reported by @DenisBalan).

Do not reverse the mksglu#476 default. Gate preservation behind explicit
operator consent:

- Default: keep stripping HTTP_PROXY / HTTPS_PROXY / ALL_PROXY (and
  lowercase / npm_config_* siblings) — mksglu#476 pinning preserved.
- When CTX_FETCH_ALLOW_PROXY=1: skip the strip so undici EnvHttpProxyAgent
  can honor corporate proxies (DenisBalan option 1). Style matches the
  existing CTX_FETCH_STRICT === "1" gate.

Parent-side ssrfGuard still pre-flights every URL. Connect-time
dns.lookup patches cover the no-proxy path; a proxy moves resolution
out of their sight, which is why preservation must be opt-in.

Tests: default strip pin + CTX_FETCH_ALLOW_PROXY=1 preservation.

Validation: npx vitest run tests/core/server.test.ts (500 pass);
npm run typecheck clean.
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.

[Bug]: ctx_fetch_and_index silently ignores HTTP_PROXY/HTTPS_PROXY — fetch subprocess strips all proxy env vars

1 participant