Skip to content

fix(net): respect HTTP proxy env vars in web_fetch SSRF dispatcher#25573

Open
makew0rld wants to merge 3 commits intoopenclaw:mainfrom
eqtylab:fix/web-fetch-proxy-support-v2
Open

fix(net): respect HTTP proxy env vars in web_fetch SSRF dispatcher#25573
makew0rld wants to merge 3 commits intoopenclaw:mainfrom
eqtylab:fix/web-fetch-proxy-support-v2

Conversation

@makew0rld
Copy link

@makew0rld makew0rld commented Feb 24, 2026

Summary

  • Problem: web_fetch (and all fetchWithSsrFGuard callers) ignore HTTP_PROXY/HTTPS_PROXY env vars because createPinnedDispatcher creates a plain undici Agent that overrides the global dispatcher, bypassing all proxy settings.
  • Why it matters: Users behind corporate proxies, VPNs, or restricted networks cannot use web_fetch at all, even when proxy env vars are correctly configured and work for other HTTP clients (e.g., Anthropic API calls, curl).
  • What changed: createPinnedDispatcher now detects proxy env vars and returns an undici EnvHttpProxyAgent (which reads HTTP_PROXY, HTTPS_PROXY, NO_PROXY automatically) instead of a plain Agent. Added 4 tests covering all env var variants.
  • What did NOT change (scope boundary): SSRF hostname/IP pre-checks in resolvePinnedHostnameWithPolicy still run before the dispatcher is created. The non-proxy code path (plain Agent with DNS pinning) is untouched. web_search bare fetch() calls are not in scope.

This is a resubmission of #24625, which was closed via automated stale-fix triage because #2102 was closed as NOT_PLANNED. The bug is still present on current main — createPinnedDispatcher still creates a plain Agent with no proxy awareness.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

web_fetch (and other tools using fetchWithSsrFGuard) now route through the user's configured HTTP proxy when HTTP_PROXY/HTTPS_PROXY (or lowercase variants) env vars are set. NO_PROXY is also respected via EnvHttpProxyAgent.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? Yes — when proxy env vars are set, web_fetch HTTP requests are routed through the proxy instead of directly. This is user-initiated behavior (requires explicit proxy env var configuration).
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Risk + mitigation: DNS pinning is skipped when proxying (the proxy handles DNS). SSRF hostname and resolved-IP pre-checks in resolvePinnedHostnameWithPolicy still execute before the dispatcher is created, maintaining protection against requests to private/internal addresses. The proxy path only activates when the user has explicitly configured proxy env vars.

Repro + Verification

Environment

  • OS: Linux (tested on VM behind HTTP proxy)
  • Runtime/container: Node.js 22+
  • Integration/channel: N/A (agent tool)
  • Relevant config: HTTP_PROXY=http://proxy:8080, HTTPS_PROXY=http://proxy:8080

Proxy setup

For systemd-managed OpenClaw (e.g. gateway running as a service), env vars can be set globally via a drop-in override:

sudo systemctl edit openclaw-gateway

Then add:

[Service]
Environment=HTTP_PROXY=http://proxy-host:port
Environment=HTTPS_PROXY=http://proxy-host:port
Environment=NODE_EXTRA_CA_CERTS=/path/to/ca.pem

Then sudo systemctl daemon-reload && sudo systemctl restart openclaw-gateway.

Steps

  1. Set HTTP_PROXY/HTTPS_PROXY env vars pointing to a proxy server (via systemd override, shell, etc.)
  2. Run OpenClaw gateway with these env vars
  3. Use web_fetch to fetch any URL

Expected

Request is routed through the configured proxy.

Actual (before fix)

Request bypasses proxy — the custom undici Agent dispatcher overrides global proxy settings.

Evidence

  • Failing test/log before + passing after
  • 4 new unit tests verify EnvHttpProxyAgent is used when proxy env vars are set, and plain Agent when not
  • Manually verified on a VM behind an HTTP proxy: web_fetch requests now appear in proxy logs

Human Verification (required)

  • Verified scenarios: web_fetch successfully routes through proxy on a VM with HTTPS_PROXY set via systemd override; all existing SSRF tests pass (36 tests across 3 test files)
  • Edge cases checked: lowercase env vars (http_proxy), missing env vars (falls back to plain Agent), mixed env var configurations
  • What you did not verify: NO_PROXY exclusion behavior (delegated to undici's EnvHttpProxyAgent implementation), web_search tool (uses bare fetch(), out of scope)

Compatibility / Migration

  • Backward compatible? Yes — no behavior change when proxy env vars are not set
  • Config/env changes? No — reads existing standard env vars
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Unset HTTP_PROXY/HTTPS_PROXY env vars to fall back to plain Agent (existing behavior)
  • Files/config to restore: src/infra/net/ssrf.ts
  • Known bad symptoms reviewers should watch for: web_fetch failing with connection errors when proxy env vars are set but proxy is unreachable

Risks and Mitigations

  • Risk: DNS pinning is bypassed when using a proxy, which widens the TOCTOU window for DNS rebinding.
    • Mitigation: SSRF hostname and resolved-IP pre-checks still execute before the dispatcher is created. The proxy itself performs DNS resolution, and the user has explicitly opted into proxy routing.

AI-assisted: This PR was developed with Claude Code (Opus). Fully tested on a real proxy setup. I understand what the code does.

Greptile Summary

Adds proxy environment variable support to web_fetch and all fetchWithSsrfGuard callers by detecting HTTP_PROXY/HTTPS_PROXY env vars in createPinnedDispatcher and returning an EnvHttpProxyAgent instead of a plain undici Agent when proxy configuration is detected.

  • Modified createPinnedDispatcher (src/infra/net/ssrf.ts:341-358) to check proxy env vars via new hasProxyEnv helper and conditionally return EnvHttpProxyAgent
  • SSRF hostname/IP pre-checks in resolvePinnedHostnameWithPolicy still execute before dispatcher creation, maintaining security boundary
  • DNS pinning is bypassed when proxying (proxy performs DNS resolution), which slightly widens TOCTOU window but is user-opted behavior
  • Added 4 test cases covering all proxy env var combinations (uppercase/lowercase variants)
  • Tests verify correct dispatcher type based on env var presence

Confidence Score: 4/5

  • Safe to merge with minor risk - SSRF pre-checks remain intact and proxy behavior only activates with explicit env var configuration
  • The implementation is straightforward and maintains existing SSRF protections. Security pre-checks in resolvePinnedHostnameWithPolicy execute before the proxy dispatcher is created. The main trade-off is DNS pinning bypass when proxying, but this is an acceptable security posture for user-configured proxy scenarios. Tests adequately cover the proxy detection logic.
  • No files require special attention

Last reviewed commit: 1db58db

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

When HTTP_PROXY/HTTPS_PROXY env vars are set, createPinnedDispatcher now
returns an undici EnvHttpProxyAgent instead of a plain Agent. This allows
web_fetch (and all fetchWithSsrFGuard callers) to route through the
configured proxy. DNS pinning is skipped when proxying since the proxy
handles resolution; SSRF hostname/IP pre-checks still run beforehand.

Related openclaw#2102
Related openclaw#8534
Supersedes openclaw#24625

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Use delete for undefined env vars instead of assigning undefined
(which would set them to the string "undefined").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle
Copy link

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Mar 3, 2026
@makew0rld
Copy link
Author

This is not outdated, I just need a review and merge.

@openclaw-barnacle openclaw-barnacle bot removed the stale Marked as stale due to inactivity label Mar 4, 2026
Pass proxyTunnel: false to EnvHttpProxyAgent so plain HTTP targets
use proper absolute-URI style proxying (GET http://host/path) instead
of CONNECT tunneling. Some proxies reject CONNECT for non-TLS traffic.
HTTPS targets still correctly use CONNECT regardless of this flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant