Skip to content

fix(net): harden trusted env proxy fetch guard and add explicit web_fetch opt-in#58034

Closed
cosmicnet wants to merge 1 commit intoopenclaw:mainfrom
cosmicnet:fix/ssrf-skip-dns-when-env-proxy
Closed

fix(net): harden trusted env proxy fetch guard and add explicit web_fetch opt-in#58034
cosmicnet wants to merge 1 commit intoopenclaw:mainfrom
cosmicnet:fix/ssrf-skip-dns-when-env-proxy

Conversation

@cosmicnet
Copy link
Copy Markdown
Contributor

@cosmicnet cosmicnet commented Mar 31, 2026

This PR now layers on top of merged #59007 rather than competing with it.

When using a trusted environment proxy, web_fetch can now skip local DNS pinning only when explicitly enabled with tools.web.fetch.useTrustedEnvProxy. The default behaviour stays unchanged. In the trusted-proxy path, hostname policy checks still run before dispatch, and requests fall back to the pinned-DNS path when no matching HTTP(S) proxy env var applies or when NO_PROXY excludes the target.

Local DNS fails in sandboxed environments where UDP is blocked and all traffic must route through the proxy, but skipping local DNS needs to stay narrow and explicit so SSRF protections do not get weakened by default.

  • Keep the core DNS-before-proxy fix from fix(net): skip DNS pinning before trusted env proxy dispatch #59007 as the baseline
  • Tighten trusted env proxy detection to match HTTP(S) proxy env semantics
  • Preserve pre-DNS hostname policy checks in the trusted-proxy path
  • Fall back to the pinned-DNS path when NO_PROXY excludes the target or no matching HTTP(S) proxy env var is configured
  • Add an explicit tools.web.fetch.useTrustedEnvProxy opt-in for web_fetch
  • Document the new config setting and update the generated config-doc baseline

Summary

  • Problem: after fix(net): skip DNS pinning before trusted env proxy dispatch #59007 fixed the basic DNS-before-proxy ordering, the trusted env proxy path still had important gaps. Proxy applicability did not fully match EnvHttpProxyAgent HTTP(S) semantics, hostname policy checks were skipped in the trusted-proxy branch, NO_PROXY exclusions did not cleanly fall back to the pinned-DNS path, and web_fetch still relied on an implicit opt-in model.
  • Why it matters: proxy-only sandbox environments need the proxy to resolve DNS, but that path still needs explicit activation and the same hostname-level SSRF guardrails. Without those follow-up fixes, some proxy configurations still fail unexpectedly and the safety boundary is harder to reason about.
  • What changed: the fetch guard now uses protocol-aware HTTP(S) proxy detection, applies assertHostnameAllowedByPolicy before trusted env proxy dispatch, falls back to the normal pinned-DNS path when NO_PROXY excludes the target or no matching HTTP(S) proxy env var exists, and exposes tools.web.fetch.useTrustedEnvProxy as an explicit web_fetch config opt-in. This PR also adds the corresponding documentation and updates the config-doc baseline hash.
  • What did NOT change (scope boundary): no changes to general proxy dispatcher bootstrap, no changes to strict-mode redirect handling or response processing, and no default behaviour change for web_fetch unless the new config opt-in is enabled.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • 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

Note: #59007 merged the core DNS-before-proxy ordering fix. This PR keeps the remaining hardening and behaviour-shaping changes on top of that: protocol-aware proxy detection, NO_PROXY fallback, pre-DNS hostname policy checks in the trusted-proxy path, the explicit web_fetch config opt-in, and the user-facing documentation for that new setting.

Root Cause / Regression History (if applicable)

  • Root cause: trusted env proxy support was originally added as a narrow dispatch path, but its applicability and safety checks were incomplete. In particular, proxy detection did not fully match HTTP(S) env-proxy semantics, hostname policy enforcement did not run in the trusted-proxy branch, and NO_PROXY exclusions did not cleanly revert to the pinned-DNS path.
  • Missing detection / guardrail: there was no direct coverage for protocol-aware proxy detection, NO_PROXY fallback behaviour, or the explicit config opt-in for web_fetch.
  • Prior context: the SSRF guard was originally designed for direct-fetch environments. Trusted env proxy support was added later, and fix(net): skip DNS pinning before trusted env proxy dispatch #59007 subsequently fixed the core DNS-before-proxy ordering issue. This PR is the follow-up hardening pass.
  • Why this surfaced now: the basic ordering bug was fixed upstream first, which made the remaining edge cases clearer and allowed this branch to narrow down to the residual gaps.
  • If unknown, what was ruled out: N/A. The remaining issues were isolated to the trusted env proxy path and config surface.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target tests or files: fetch-guard SSRF tests, proxy-env tests, web-fetch SSRF tests, web tools fetch tests, and config schema tests
  • Scenario the tests should lock in:
    1. Trusted env proxy dispatch only activates when a matching HTTP(S) proxy env var applies.
    2. NO_PROXY exclusions fall back to the pinned-DNS path.
    3. Hostname policy checks still run in the trusted env proxy path.
    4. web_fetch only opts into this path when tools.web.fetch.useTrustedEnvProxy is explicitly enabled.
    5. The runtime config schema accepts the new setting.
  • Why this is the smallest reliable guardrail: these unit-level checks exercise the fetch guard and config surfaces directly, without needing a live proxy or sandbox deployment.
  • Existing test that already covers this (if any): none comprehensively covered these trusted env proxy edge cases before.
  • If no new test is added, why not: new coverage was added.

User-visible / Behaviour Changes

  • Default behaviour is unchanged.
  • When tools.web.fetch.useTrustedEnvProxy is true and a matching HTTP(S) proxy env var applies, web_fetch routes through the trusted env proxy without local DNS pinning.
  • When the opt-in is unset or false, or when NO_PROXY excludes the target, or when no matching HTTP(S) proxy env var exists, the guard falls back to the existing pinned-DNS path.
  • The new opt-in is documented in the Web Fetch guide and the gateway configuration reference.

Diagram (if applicable)

Before:
[web_fetch] -> fetchWithSsrfGuard -> trusted env proxy branch
                                   -> local safeguards incomplete for proxy applicability / hostname checks / NO_PROXY fallback

After:
[web_fetch] -> tools.web.fetch.useTrustedEnvProxy?
               NO  -> pinned-DNS path
               YES -> assertHostnameAllowedByPolicy
                      -> matching HTTP(S) proxy env and not NO_PROXY?
                         YES -> EnvHttpProxyAgent [proxy resolves DNS]
                         NO  -> resolvePinnedHostnameWithPolicy -> createPinnedDispatcher

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? Yes
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: when tools.web.fetch.useTrustedEnvProxy is enabled and the trusted path is taken, local SSRF IP pinning is delegated to the trusted proxy instead of being performed locally. The risk is contained because (1) the path is explicit and off by default, (2) it only activates when a matching HTTP(S) proxy env var applies to the request protocol, (3) NO_PROXY exclusions fall back to the pinned-DNS path, and (4) hostname policy checks still run before proxy dispatch.

Repro + Verification

Environment

  • OS: Linux (WSL2 / Ubuntu)
  • Runtime/container: OpenShell sandbox with CONNECT proxy
  • Model/provider: any, this is a network-path issue
  • Integration/channel (if any): N/A
  • Relevant config (redacted): tools.web.fetch.useTrustedEnvProxy: true with HTTP_PROXY or HTTPS_PROXY set in the sandbox environment

Steps

  1. Run OpenClaw inside an OpenShell sandbox where UDP DNS is blocked
  2. Enable tools.web.fetch.useTrustedEnvProxy for web_fetch
  3. Use the web_fetch agent tool to fetch a public URL
  4. Repeat with NO_PROXY excluding the target host

Expected

  • With the opt-in enabled and a matching HTTP(S) proxy env var active, the request routes through the trusted proxy and the proxy resolves DNS.
  • If NO_PROXY excludes the target, or no matching HTTP(S) proxy env var exists, the request falls back to the pinned-DNS path.

Actual

  • Before these follow-up fixes, trusted env proxy mode still had edge cases around proxy detection, hostname checks, and NO_PROXY fallback.
  • After this PR, the trusted path is explicit, protocol-aware, and falls back cleanly when the proxy should not be used.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets

Verification performed for the rebased branch:

  • pnpm config:docs:check passes after the schema/docs update
  • pnpm test was run on updated main and on this branch after rebasing; after refreshing the stale generated base-schema file, the visible failures on this branch matched the current main baseline, with no extra visible branch-only failure identified
  • live manual verification against a completely fresh bleeding-edge NemoClaw / OpenShell / OpenClaw build and deploy showed that enabling tools.web.fetch.useTrustedEnvProxy let web_fetch reach upstream services through the trusted env proxy path; remaining example.com-style failures traced to Bug(proxy): is the sandbox proxy’s upstream TLS CA bundle too limited for some ordinary public HTTPS sites? Example repros included NVIDIA/OpenShell#813 rather than this OpenClaw change
  • the refreshed rebased branch has been force-pushed to the fork after the final local fixups, so the remote PR branch now matches this verified state

Human Verification (required)

  • Verified scenarios: code and tests were reviewed for protocol-aware proxy applicability, NO_PROXY fallback, hostname policy enforcement in the trusted-proxy path, the explicit web_fetch opt-in, and the accompanying config/docs surfaces. The trusted env proxy path was also exercised in a completely fresh bleeding-edge NemoClaw / OpenShell / OpenClaw build and deploy.
  • Edge cases checked: proxy mode enabled but no matching HTTP(S) proxy env var configured; target excluded by NO_PROXY; default web_fetch behaviour when the opt-in is not enabled.
  • What I did not verify: I did not repeat that full fresh-build deployment after the final local fixups, because the live verification had already been completed against a freshly built bleeding-edge NemoClaw / OpenShell / OpenClaw environment.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgement.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? Yes, optional new tools.web.fetch.useTrustedEnvProxy setting
  • Migration needed? No

Risks and Mitigations

  • Risk: when the explicit trusted env proxy opt-in is enabled and the trusted path is taken, local SSRF IP pinning is no longer performed.
    • Mitigation: hostname policy checks still run before dispatch; the path is off by default; it only activates when a matching HTTP(S) proxy env var applies; and NO_PROXY exclusions fall back to the existing pinned-DNS path.

This PR was co-authored with an AI assistant (GitHub Copilot). All changes were reviewed and verified by the human author.

Copilot AI review requested due to automatic review settings March 31, 2026 01:09
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 31, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 31, 2026

Greptile Summary

This PR fixes a latent bug where fetchWithSsrFGuard unconditionally called resolvePinnedHostnameWithPolicy() before checking whether a trusted env proxy was configured. In sandboxed environments where UDP DNS is blocked, this caused all web_fetch tool calls to fail before the proxy dispatch was ever reached.

The fix correctly restructures the dispatcher-selection logic in fetch-guard.ts — moving assertExplicitProxySupportsPinnedDns, resolvePinnedHostnameWithPolicy, and createPinnedDispatcher into the else branch that runs only when no trusted env proxy is active. It also adds useEnvProxy: true to the web_fetch tool's call site so it opts into the new proxy-aware path. The safety gate (canUseTrustedEnvProxy requires both the explicit mode flag AND a configured proxy env var) is sound, and the fallback to full DNS pinning when no proxy is configured is verified by a new unit test.

Key findings:

  • The core logic restructuring is correct. Dispatcher lifecycle (create → close on redirect → release on success/error) is unchanged.
  • The first new test ("skips DNS resolution entirely in trusted env proxy mode") is complete and correct.
  • The second new test ("falls back to DNS pinning when … no proxy env var exists") stubs only four of the six proxy env keys checked by hasProxyEnvConfigured()ALL_PROXY and all_proxy are not cleared. On CI hosts or developer machines where either of those vars is set, the test will silently exercise the proxy path instead of the DNS-pinning path it was designed to guard, potentially masking regressions in the fallback behaviour.

Confidence Score: 4/5

  • Safe to merge after fixing the missing ALL_PROXY/all_proxy stubs in the fallback test; the production code change is sound.
  • The production logic change in fetch-guard.ts is correct and well-reasoned. The only concrete issue is in the new test: two of the six proxy env keys checked by hasProxyEnvConfigured() are not stubbed, which can cause the fallback test to silently validate the wrong code path in environments where ALL_PROXY or all_proxy is set. This is a P1 test-isolation gap that undermines the guardrail the PR explicitly adds to prevent future regressions.
  • src/infra/net/fetch-guard.ssrf.test.ts — the fallback test needs ALL_PROXY and all_proxy stubs added to reliably isolate the DNS-pinning path.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.ssrf.test.ts
Line: 371-376

Comment:
**Missing `ALL_PROXY`/`all_proxy` stubs weaken test isolation**

`hasProxyEnvConfigured()` iterates over all six keys in `PROXY_ENV_KEYS`, which includes `ALL_PROXY` and `all_proxy`. This test only stubs four of them. If a CI host (or a developer's machine) has `ALL_PROXY` or `all_proxy` set, `hasProxyEnvConfigured()` returns `true`, `canUseTrustedEnvProxy` becomes `true`, and the code takes the proxy path instead of the DNS-pinning path the test expects — causing the test to either fail or silently validate wrong behavior.

Since the goal of this test is to lock in the fallback behaviour with high confidence ("no proxy env var exists"), all six keys need to be cleared:

```suggestion
    // Ensure no proxy vars leak from the test host
    vi.stubEnv("HTTP_PROXY", "");
    vi.stubEnv("HTTPS_PROXY", "");
    vi.stubEnv("ALL_PROXY", "");
    vi.stubEnv("http_proxy", "");
    vi.stubEnv("https_proxy", "");
    vi.stubEnv("all_proxy", "");
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(net): skip DNS pinning when routing ..." | Re-trigger Greptile

Comment thread src/infra/net/fetch-guard.ssrf.test.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e0e2788ef

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/fetch-guard.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the SSRF/DNS pinning flow in the network fetch guard so that when routing through a trusted environment proxy, local DNS resolution (and associated pinning) is skipped—unblocking proxy-only sandbox environments where UDP DNS is unavailable.

Changes:

  • Restructures fetchWithSsrFGuard to branch early between trusted env-proxy dispatch and direct/pinned-DNS dispatch.
  • Adds unit tests to ensure DNS resolution is skipped in trusted env-proxy mode and that the code falls back to DNS pinning when no proxy env is configured.
  • Enables useEnvProxy: true for the web_fetch tool so it opts into env-proxy routing when available.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/infra/net/fetch-guard.ts Moves DNS pinning/lookup into the non-proxy path; uses EnvHttpProxyAgent when trusted env proxy is active.
src/infra/net/fetch-guard.ssrf.test.ts Adds tests covering DNS-skip behavior in trusted env proxy mode and fallback behavior when no proxy env is set.
src/agents/tools/web-fetch.ts Opts web_fetch into env-proxy routing via useEnvProxy: true.

Comment thread src/infra/net/fetch-guard.ts
Comment thread src/infra/net/fetch-guard.ssrf.test.ts Outdated
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 1e0e278 to ba4a2a8 Compare April 1, 2026 01:17
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba4a2a8b1f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/fetch-guard.ts Outdated
Comment thread src/agents/tools/web-fetch.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1cf12cffc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/fetch-guard.ts Outdated
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from b1cf12c to f291d7a Compare April 1, 2026 19:56
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from f291d7a to d998d26 Compare April 1, 2026 20:11
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d998d26180

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/fetch-guard.ts Outdated
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from d998d26 to b296e4e Compare April 1, 2026 22:07
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b296e4e671

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/agents/tools/web-fetch.ts Outdated
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from b296e4e to 3eb849d Compare April 1, 2026 23:18
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3eb849dd5e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/agents/tools/web-fetch.ts Outdated
@cosmicnet
Copy link
Copy Markdown
Contributor Author

Note: #59007 was opened addressing the same root cause. That PR moves DNS resolution into the non-proxy branch (same structural fix as here) but does not include:

  • Protocol-aware proxy detection (hasEnvHttpProxyConfigured vs hasProxyEnvConfigured)
  • Pre-DNS hostname policy enforcement in the proxy path (assertHostnameAllowedByPolicy)
  • NO_PROXY/no_proxy fallback to DNS pinning (isHostExcludedByNoProxy)
  • The useEnvProxy: true opt-in for web_fetch

Codex flagged the hasProxyEnvConfigured gap as P1 on #59007 as well.

Size on this pull changed from S to M largely due to increased test coverage.

@mjamiv
Copy link
Copy Markdown
Contributor

mjamiv commented Apr 8, 2026

Heads up — #59007 (skip DNS pinning before trusted env proxy dispatch) merged on 2026-04-08, which covers the core DNS-before-proxy ordering fix that this PR also addresses.

This PR has additional protocol-aware detection logic that #59007 doesn't include. Is there interest in continuing with that extra coverage, or should this be closed as superseded? Happy either way — just want to make sure it doesn't go stale without a decision.

@cosmicnet
Copy link
Copy Markdown
Contributor Author

@mjamiv Yeah, I saw that. I replied on your comment here with the changes I'm currently working on for this branch:
#43821 (comment)

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 3eb849d to c3c471e Compare April 10, 2026 00:55
@cosmicnet
Copy link
Copy Markdown
Contributor Author

I’ve rebased this branch onto the latest main, which is moving pretty quickly.

The change to make web_fetch respect a trusted proxy for DNS resolution is now behind an explicit config value, so the default behaviour remains unchanged and SSRF protections stay in place unless it is deliberately enabled.

@mjamiv does this provide what you need?

@mjamiv
Copy link
Copy Markdown
Contributor

mjamiv commented Apr 11, 2026

This PR also closes out #63565 ("web_fetch bypasses env proxy; SSRF guard DNS pinning fails in proxy-only envs"). That issue describes the same proxy-only sandbox scenario this PR is hardening for — adding it to the Linked Issue/PR list may help with review routing. Happy to test the branch against a proxy-only sandbox deployment if that would help get this unstuck.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 2c25c56 to f1b8f45 Compare April 11, 2026 16:52
@cosmicnet
Copy link
Copy Markdown
Contributor Author

cosmicnet commented Apr 11, 2026

@mjamiv

Thanks, that makes sense. #63565 is the same proxy-only sandbox failure mode this branch is narrowing down, so I’ve added it to the Linked Issue/PR list for review routing.

I have now force-pushed the rebased branch again on top of current main and re-ran pnpm test on both main and this branch. After refreshing the stale generated base-schema file on the branch, the visible failing tests on this branch matched the current main baseline, with no extra visible branch-only failure left over from this PR.

I’d definitely welcome a proxy-only sandbox test on your side if you have time. That is the exact deployment shape this branch is intended to cover. The live verification I already ran against a completely fresh bleeding-edge NemoClaw / OpenShell / OpenClaw build and deploy showed that, once tools.web.fetch.useTrustedEnvProxy is enabled, web_fetch does route through the trusted env proxy and can reach upstream services. The remaining example.com-style failures I hit during that work traced to NVIDIA/OpenShell#813 rather than to this OpenClaw change.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from f1b8f45 to 5df6a0f Compare April 11, 2026 17:31
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5df6a0f6fc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/proxy-env.ts Outdated
@mjamiv
Copy link
Copy Markdown
Contributor

mjamiv commented Apr 11, 2026

Tested this branch end-to-end in a proxy-only sandbox deployment (HTTP CONNECT egress, UDP DNS blocked, HTTPS_PROXY/HTTP_PROXY routed through EnvHttpProxyAgent). Short version: it works.

  • useTrustedEnvProxy: true flows cleanly through the zod schema, resolveFetchUseTrustedEnvProxy, and into runWebFetch / fetchWithSsrFGuard.
  • Trusted-env path returns real HTTP responses for allowlisted hosts where the STRICT baseline fails at DNS as expected.
  • assertHostnameAllowedByPolicy still fires pre-dispatch in the trusted-env branch — confirmed for allowlist misses and for the cloud-metadata IP (169.254.169.254). That pre-DNS block is the load-bearing reassurance for anyone worried about this path widening the SSRF envelope.
  • NO_PROXY exclusion correctly flips canUseTrustedEnvProxy to false and falls through to the pinned-DNS branch.

This covers the shape of environment described in #63565. Rolled the test environment back to stock afterward, no residual changes. Happy to re-run if you rebase or want additional scenarios.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 036d6c8648

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/net/proxy-env.ts Outdated
@smallmj
Copy link
Copy Markdown

smallmj commented Apr 13, 2026

Hi @cosmicnet, just checking in on this PR. This fix seems important for users running OpenClaw behind Clash or other transparent proxies where UDP is blocked and all DNS must go through the proxy.

Could you let us know if there's anything blocking this from being merged, or if it needs any additional information from the maintainers?

@cosmicnet
Copy link
Copy Markdown
Contributor Author

@smallmj thanks for checking in.

From my side, I do not think there is any substantive author-side blocker left on this branch. The change is implemented, I have manually verified it in a live OpenShell/NemoClaw deployment, and @mjamiv has independently verified it end to end in a proxy-only sandbox.

I do think part of the confusion here is that #59007 was treated as superseding this PR, but it did not actually pull in all of the remaining work from this branch. It landed the narrower DNS-before-proxy ordering fix, but it did not include the explicit tools.web.fetch.useTrustedEnvProxy opt-in for web_fetch, the protocol-aware HTTP(S) proxy detection, the NO_PROXY fallback to the pinned-DNS path, or the pre-DNS hostname policy checks in the trusted-proxy path. In other words, it did not fully supersede this PR, and I suspect that may be part of why this has stalled.

So from my side, I believe this is ready to pull in. If there is still a specific maintainer concern blocking merge, I would appreciate a pointer to it.

@smallmj
Copy link
Copy Markdown

smallmj commented Apr 13, 2026

Comprehensive Proxy Test Results (2026-04-13)

Test Environment

  • OpenClaw Gateway running on Ubuntu, behind Clash (socks5 proxy at 127.0.0.1:7897)
  • Gateway started via systemctl (SetLoginEnvironment=no), so shell env vars do NOT propagate to the process

Test 1: Without Tun mode (proxy env vars not set)

curl with explicit -x flag (proxy works):

curl -x http://127.0.0.1:7897 https://api.ip.sb/ip
→ 216.236.40.184 (Hong Kong exit)

curl without proxy (direct):

curl https://api.ip.sb/ip
→ 2408:8207:... (domestic IPv6, no proxy)

web_fetch tool:

web_fetch https://api.ip.sb/ip
→ 221.219.70.115 (Beijing, direct — no proxy) ❌

Conclusion: web_fetch does not use proxy even when proxy is working for curl.


Test 2: With Tun mode + DNS override enabled (no HTTPS_PROXY env var set)

All DNS now routes through Clash TUN, which returns 198.18.x.x (RFC 2544 benchmark IP range) for most domains.

curl direct (Tun catches all):

curl https://api.ip.sb/ip
→ 216.236.40.180 (proxy exit) ✅

web_fetch:

web_fetch https://icanhazip.com
→ Blocked: resolves to private/internal/special-use IP address ❌

web_fetch https://checkip.amazonaws.com
→ Blocked: resolves to private/internal/special-use IP address ❌

web_fetch https://cloudflare.com/cdn-cgi/trace
→ Blocked: resolves to private/internal/special-use IP address ❌

Root cause: Clash TUN DNS returns 198.18.x.x (benchmark IPs). OpenClaw SSRF guard correctly blocks these as special-use addresses.

Conclusion: Tun mode breaks web_fetch via DNS + SSRF interaction.


Test 3: Tun mode OFF, DNS override OFF, HTTPS_PROXY env var set in systemctl

Properly injected proxy env into Gateway:

systemctl --user set-environment HTTPS_PROXY=http://127.0.0.1:7897
systemctl --user set-environment ALL_PROXY=http://127.0.0.1:7897
systemctl --user restart openclaw-gateway

Verification:

systemctl --user show-environment | grep proxy
→ ALL_PROXY=http://127.0.0.1:7897 ✅
→ HTTPS_PROXY=http://127.0.0.1:7897 ✅

curl (no -x flag, Tun off):

curl https://api.ip.sb/ip
→ 216.236.40.177 (proxy exit) ✅

web_fetch:

web_fetch https://api.ip.sb/ip
→ 221.219.70.115 (direct, no proxy) ❌

Conclusion: Even with HTTPS_PROXY correctly set in the Gateway process environment, web_fetch still does not use the proxy. This confirms web_fetch itself does not read HTTPS_PROXY/ALL_PROXY env vars — it uses Node.js built-in fetch which goes direct.


Summary

Scenario curl web_fetch
No Tun, no env var, curl -x ✅ Proxy (HK exit) ❌ Direct
Tun + DNS override ✅ Proxy (HK exit) ❌ SSRF blocked (198.18.x.x)
No Tun, HTTPS_PROXY env var set ✅ Proxy (HK exit) ❌ Direct

This is why PR #58034 is needed. The core issue is not the environment or DNS — it's that web_fetch does not respect HTTPS_PROXY. The useEnvProxy: true flag in this PR would make web_fetch explicitly use the proxy configured in openclaw.json, solving all of the above.

Additional note from cosmicnet: PR #59007 merged the DNS-before-proxy ordering fix for the core network layer, but did NOT include the web_fetch opt-in. This PR (#58034) adds that missing piece.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 755cbf6 to 9307bc6 Compare April 15, 2026 00:02
@cosmicnet
Copy link
Copy Markdown
Contributor Author

Rebased onto current main and force-pushed the branch.

I reran the affected coverage for the edited paths only: the NO_PROXY matcher, fetch guard SSRF path, web_fetch trusted-env-proxy path, and the config-doc baseline check. Those all pass on the rebased branch. I have not rerun the full suite.

The branch still carries the web_fetch-specific trusted env proxy opt-in and docs on top of the upstream proxy/fetch changes already on main.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 9307bc6 to d063e40 Compare April 21, 2026 11:32
@cosmicnet
Copy link
Copy Markdown
Contributor Author

For context, I think this branch addresses a different case from #60785 (which was just closed).

#60785 is about fake-IP setups via tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange. That makes sense for deployments where local DNS still succeeds and returns RFC2544 198.18.0.0/15 addresses that are then handled by the network layer.

This branch is aimed at proxy-only environments where local DNS is blocked or unavailable and web_fetch needs the trusted HTTP(S) env proxy itself to resolve DNS. Current main still keeps web_fetch on the local pinned-DNS path by default even when HTTP_PROXY or HTTPS_PROXY is set. This branch adds an explicit tools.web.fetch.useTrustedEnvProxy opt-in for that path, while still keeping hostname policy checks before proxy dispatch and falling back to pinned DNS when NO_PROXY excludes the target or no matching proxy env var applies.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs maintainer review before merge.

What this changes:

The PR adds a default-off tools.web.fetch.useTrustedEnvProxy config/docs/schema surface, wires it into web_fetch guarded fetch calls, and adds trusted-proxy, NO_PROXY, hostname-policy, and config regression coverage.

Maintainer follow-up before merge:

This is useful active contributor work, but it is dirty against current main and changes SSRF/trusted-proxy network semantics, so maintainers should review the trust boundary and rebase before landing rather than sending it to autonomous repair.

Security review:

Security review cleared: The diff changes network routing, but the reviewed design keeps the new path default-off, protocol/NO_PROXY gated, and hostname-policy guarded; no concrete secrets, CI, dependency, or supply-chain regression was found.

Review details

Best possible solution:

Land a rebased version if maintainers accept the trust boundary: keep web_fetch strict by default, add the explicit trusted-env-proxy opt-in, preserve hostname-policy checks before proxy dispatch, keep protocol-aware and NO_PROXY fallback behavior, and update docs/schema/tests without regressing newer current-main SSRF policy keys.

Do we have a high-confidence way to reproduce the issue?

Yes. The high-confidence reproduction is a proxy-only sandbox with HTTP_PROXY/HTTPS_PROXY configured and local UDP DNS blocked; current main keeps web_fetch on the strict pinned-DNS path, and the PR discussion includes independent live verification of the failure and branch behavior.

Is this the best way to solve the issue?

Likely yes, subject to maintainer trust-boundary approval and a clean rebase. A default-off config knob with protocol-aware proxy gating, NO_PROXY fallback, and pre-dispatch hostname checks is the narrow maintainable direction for this web_fetch case.

Acceptance criteria:

  • pnpm test src/infra/net/proxy-env.test.ts src/infra/net/fetch-guard.ssrf.test.ts src/agents/tools/web-tools.fetch.test.ts src/agents/tools/web-fetch.ssrf.test.ts src/config/schema.test.ts
  • pnpm config:docs:check
  • pnpm check:changed in Testbox before merge

What I checked:

  • Current main still keeps web_fetch strict: runWebFetch calls fetchWithWebToolsNetworkGuard without passing useEnvProxy, so ordinary web_fetch cannot enter trusted env-proxy mode on current main. (src/agents/tools/web-fetch.ts:425, 51affb81b9a5)
  • Current test locks default strict behavior: The current test asserts that web_fetch keeps DNS pinning and does not use EnvHttpProxyAgent even when HTTP_PROXY is configured. (src/agents/tools/web-tools.fetch.test.ts:334, 51affb81b9a5)
  • Current trusted env branch exists but lacks this PR's pre-dispatch hardening: Current main gates trusted env proxy through shouldUseEnvHttpProxyForUrl, then dispatches with createHttp1EnvHttpProxyAgent; the PR adds hostname-policy checks before that dispatch. (src/infra/net/fetch-guard.ts:368, 51affb81b9a5)
  • PR diff adds the missing web_fetch opt-in: The live PR diff adds resolveFetchUseTrustedEnvProxy, carries it into WebFetchRuntimeParams, passes it as useEnvProxy, and documents/configures the new setting. (src/agents/tools/web-fetch.ts:424, b93c8f64dc99)
  • Dependency contract checked for NO_PROXY semantics: package.json pins undici 8.1.0, and the upstream EnvHttpProxyAgent source only treats the whole NO_PROXY value * as the global bypass case, supporting the PR's mixed-list wildcard adjustment. (package.json:1651, 51affb81b9a5)
  • Live PR metadata shows merge blocker: The GitHub API reports head b93c8f64dc996ad565733f4df7ee7fe8c16e4d27, mergeable: false, rebaseable: false, and mergeable_state: dirty. (b93c8f64dc99)

Likely related people:

  • cluster2600: Authored merged fix(net): skip DNS pinning before trusted env proxy dispatch #59007, which established the current trusted env-proxy DNS-ordering baseline that this PR builds on. (role: introduced behavior; confidence: high; commits: d7c3210cd6f5, 2a915fb22d15; files: src/infra/net/fetch-guard.ts, src/infra/net/fetch-guard.ssrf.test.ts)
  • steipete: Current checkout blame and prior ClawSweeper history point to recent maintenance on the guarded fetch and proxy-env helpers involved in this PR. (role: recent maintainer; confidence: medium; commits: f0a2b09df69a, dc859584a352, c973b053a5e2; files: src/infra/net/fetch-guard.ts, src/infra/net/proxy-env.ts, src/agents/tools/web-fetch.ts)
  • vincentkoc: Prior main-history work cited in the existing ClawSweeper review connects this person to web_fetch internals and adjacent trusted env-proxy/provider HTTP paths. (role: adjacent owner; confidence: medium; commits: 86099ec62a41, 6ee8e194c027; files: src/agents/tools/web-fetch.ts, src/infra/net/proxy-env.ts)

Remaining risk / open question:

  • The PR head is dirty against current main, so a rebase must preserve newer current-main web_fetch SSRF policy fields such as allowIpv6UniqueLocalRange.
  • The opt-in intentionally delegates DNS and post-resolution IP checks to an operator-controlled proxy, so maintainers should explicitly accept that trust boundary before merge.
  • The author reported focused local checks, but the current head has no visible commit status checks from the GitHub status API.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 51affb81b9a5.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from d063e40 to bd87eae Compare April 28, 2026 17:40
@cosmicnet
Copy link
Copy Markdown
Contributor Author

Thanks. Latest push is bd87eae254.

I believe the concrete implementation pieces called out here are now present on the branch:

  • tools.web.fetch.useTrustedEnvProxy is an explicit, default-off config field.
  • createWebFetchTool wires that config through to fetchWithWebToolsNetworkGuard.
  • Default web_fetch behavior still keeps strict DNS pinning even when HTTP_PROXY is configured.
  • Opt-in trusted env proxy mode uses env proxy dispatch only when a matching HTTP(S) proxy applies.
  • NO_PROXY exclusions and no matching proxy env both fall back to the pinned-DNS path.
  • Hostname-policy checks still run before trusted-proxy dispatch.
  • Docs, schema, generated config metadata, and regression tests are updated.

Focused local verification after the final push contents:

  • pnpm exec oxlint src/agents/tools/web-fetch.ts src/agents/tools/web-fetch.ssrf.test.ts src/agents/tools/web-tools.fetch.test.ts src/infra/net/fetch-guard.ts src/infra/net/fetch-guard.ssrf.test.ts src/infra/net/proxy-env.ts src/infra/net/proxy-env.test.ts
  • pnpm vitest run src/infra/net/proxy-env.test.ts src/infra/net/fetch-guard.ssrf.test.ts src/agents/tools/web-tools.fetch.test.ts src/agents/tools/web-fetch.ssrf.test.ts src/config/schema.test.ts
  • pnpm config:docs:check

All passed locally.

On the Firecrawl/provider fallback question: I left that as an intentional scope boundary for this PR. This branch makes the built-in web_fetch page-fetch path work in proxy-only deployments while preserving default strict mode. Firecrawl is a provider/API fallback path with a separate trust boundary, so I think maintainers should decide whether tools.web.fetch.useTrustedEnvProxy should also apply to provider API calls, or whether that should be a follow-up with separate docs/tests.

@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from bd87eae to 6bb5882 Compare April 28, 2026 17:51
@cosmicnet cosmicnet force-pushed the fix/ssrf-skip-dns-when-env-proxy branch from 6bb5882 to b93c8f6 Compare April 28, 2026 18:10
@openclaw-barnacle openclaw-barnacle Bot removed the gateway Gateway runtime label Apr 28, 2026
@steipete
Copy link
Copy Markdown
Contributor

steipete commented May 3, 2026

Post-#76887 status: still relevant, but this remains a separate web_fetch trusted-env-proxy change. #76887 only landed scoped fake-IP allowances for trusted provider/web-search hosts and did not make web_fetch honor ambient proxy env routing.

Current branch state is still DIRTY / conflicting with failing stale CI, so the next useful step is a rebase against current main plus fresh focused SSRF/web_fetch tests and Testbox pnpm check:changed. The trust-boundary decision remains separate from #76887.

@steipete
Copy link
Copy Markdown
Contributor

steipete commented May 3, 2026

Landed the maintainer-side version on main in 66336bf.

Kept the setting default-off as tools.web.fetch.useTrustedEnvProxy, preserved strict DNS pinning by default, enforced hostname/IP policy before trusted env-proxy dispatch, corrected NO_PROXY wildcard handling to match Undici, and added docs/schema/changelog plus regression coverage.

Verification before landing:

  • targeted local tests for proxy-env, fetch-guard SSRF, web-tools fetch, web-fetch SSRF, and schema
  • pnpm config:docs:check
  • git diff --check
  • Testbox pnpm check:changed on the rebased commit: tbx_01kqqw5q42q6wkwmmxn9deezk6

Thanks @cosmicnet and @mjamiv.

@steipete steipete closed this May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants