Skip to content

fix(infra/net): use EnvHttpProxyAgent when proxy env vars are configured#50650

Merged
obviyus merged 3 commits intoopenclaw:mainfrom
kkav004:fix/fetch-guard-proxy-fallback
Mar 31, 2026
Merged

fix(infra/net): use EnvHttpProxyAgent when proxy env vars are configured#50650
obviyus merged 3 commits intoopenclaw:mainfrom
kkav004:fix/fetch-guard-proxy-fallback

Conversation

@kkav004
Copy link
Copy Markdown
Contributor

@kkav004 kkav004 commented Mar 19, 2026

Problem

When HTTPS_PROXY / HTTP_PROXY environment variables are set, web_fetch (and other tools using fetchWithSsrFGuard without an explicit dispatcherPolicy.mode) fail with fetch failed or getaddrinfo EAI_AGAIN. The pinned dispatcher connects directly to the DNS-resolved IP, bypassing the proxy entirely.

This breaks OpenClaw in:

  • OpenShell/NemoClaw sandboxes — DNS is blocked in the network namespace, and direct outbound TCP is only routable through the proxy
  • Docker containers with proxy-only network access
  • Corporate networks requiring proxy for all HTTPS traffic

The TRUSTED_ENV_PROXY mode (#45248) works correctly but requires explicit opt-in. The default STRICT mode always uses createPinnedDispatcher in direct mode, which bypasses the proxy.

Fix

Two files changed.

src/infra/net/fetch-guard.ts — When hasEnvHttpProxyConfigured() returns true and the caller hasn't already specified a dispatcherPolicy.mode or allowed private networks, use createPinnedDispatcher with mode: "env-proxy" instead of the default direct mode:

+import { hasEnvHttpProxyConfigured, hasProxyEnvConfigured } from "./proxy-env.js";
-import { hasProxyEnvConfigured } from "./proxy-env.js";

       } else if (params.pinDns !== false) {
-        dispatcher = createPinnedDispatcher(pinned, params.dispatcherPolicy, params.policy);
+        const protocol = parsedUrl.protocol === "http:" ? "http" : "https";
+        const useEnvProxy =
+          hasEnvHttpProxyConfigured(protocol) &&
+          !params.dispatcherPolicy?.mode &&
+          !params.policy?.allowPrivateNetwork &&
+          !params.policy?.dangerouslyAllowPrivateNetwork;
+        const dispatcherPolicy: PinnedDispatcherPolicy | undefined = useEnvProxy
+          ? Object.assign({}, params.dispatcherPolicy, { mode: "env-proxy" as const })
+          : params.dispatcherPolicy;
+        dispatcher = createPinnedDispatcher(pinned, dispatcherPolicy, params.policy);
       }

src/infra/net/fetch-guard.ssrf.test.ts — Updated test expectations to reflect that STRICT mode now uses EnvHttpProxyAgent (via pinned env-proxy dispatcher) when proxy env vars are configured:

-  it("ignores env proxy by default to preserve DNS-pinned destination binding", async () => {
+  it("routes through env proxy in strict mode via pinned env-proxy dispatcher", async () => {
     await runProxyModeDispatcherTest({
       mode: GUARDED_FETCH_MODE.STRICT,
-      expectEnvProxy: false,
+      expectEnvProxy: true,
     });
   });

-  it("uses env proxy only when dangerous proxy bypass is explicitly enabled", async () => {
+  it("routes through env proxy when trusted proxy mode is explicitly enabled", async () => {

What's preserved:

  • DNS pinningcreatePinnedDispatcher threads the pinned lookup into EnvHttpProxyAgent via connect: withPinnedLookup() in its existing env-proxy mode
  • SSRF validationresolvePinnedHostnameWithPolicy() still runs before the dispatcher is created
  • Explicit dispatcher policies — callers with mode: "explicit-proxy" (Telegram media), mode: "direct" (fallback recovery), or any other mode are not overridden
  • Private/local network access — callers with allowPrivateNetwork or dangerouslyAllowPrivateNetwork stay on the direct pinned path (BlueBubbles, remote memory over LAN)
  • TRUSTED_ENV_PROXY — unchanged, remains the explicit opt-in for unpinned proxy routing
  • Protocol-aware detection — uses hasEnvHttpProxyConfigured(protocol) so http:// URLs only trigger env-proxy when HTTP_PROXY is set
  • No proxy configured — falls through to existing direct mode behavior

How I found this

Deploying NemoClaw (NVIDIA's OpenClaw wrapper) inside an OpenShell sandbox. The sandbox has NODE_USE_ENV_PROXY=1, HTTPS_PROXY, and NODE_EXTRA_CA_CERTS configured — raw fetch() works natively. But web_fetch fails because the SSRF guard's pinned dispatcher in direct mode bypasses the proxy.

The env-proxy mode already existed in createPinnedDispatcher (in ssrf.ts) and correctly threads DNS pinning through EnvHttpProxyAgent. This PR just activates it when proxy env vars are detected and the caller hasn't opted out.

Testing

  • All 14 tests in fetch-guard.ssrf.test.ts pass locally
  • pnpm check passes locally (format, types, lint, all boundary checks)
  • Verified web_fetch works for google.com, httpbin.org, bbc.com, api.github.com in an OpenShell sandbox
  • SSRF validation still blocks private/reserved IPs
  • Slack API, Jira API, and Codex inference unaffected
  • No proxy configured → existing direct mode (unchanged)
  • Callers with explicit dispatcherPolicy.mode → not overridden
  • Callers with allowPrivateNetwork / dangerouslyAllowPrivateNetwork → direct pinned path preserved

Fixes

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR routes fetchWithSsrFGuard through the system HTTP proxy (via EnvHttpProxyAgent) in STRICT mode whenever proxy env vars are present, fixing real-world failures in proxy-only network environments. However, the implementation has two security regressions that should be addressed before merging.

Key issues:

  • ALL_PROXY-only deployments silently lose DNS-pinning protection: hasProxyEnvConfigured() returns true for ALL_PROXY/all_proxy, but EnvHttpProxyAgent explicitly ignores those variables (documented in proxy-env.ts). When only ALL_PROXY is set, the new branch fires, EnvHttpProxyAgent is used, but no proxy is actually applied — the connection becomes a direct connection without the pinned DNS lookup, stripping the anti-DNS-rebinding guarantee. Using hasEnvHttpProxyConfigured() (already available in proxy-env.ts) would restrict the branch to cases where EnvHttpProxyAgent will actually route through a proxy.
  • STRICT mode silently adopts TRUSTED_ENV_PROXY semantics for any proxy-env deployment: The distinction between modes exists precisely so that operators can opt into the weaker DNS-rebinding posture that proxy routing entails. Unconditionally using EnvHttpProxyAgent in STRICT mode when proxy vars are set erases that boundary. A safer alternative is to use createPinnedDispatcher with { mode: "env-proxy" } which threads EnvHttpProxyAgent through the existing pinned-dispatcher path.
  • canUseTrustedEnvProxy is dead code: It is a strict subset of hasProxyEnvConfigured() and no longer affects control flow.

Confidence Score: 2/5

  • Not safe to merge as-is — the ALL_PROXY-only scenario silently removes DNS-pinning SSRF protection, and STRICT mode loses its anti-DNS-rebinding guarantee for any proxy-env deployment.
  • The functional goal (making web_fetch work through HTTP proxies) is correct, and the SSRF hostname validation call is preserved. However, the implementation condition uses hasProxyEnvConfigured() which includes ALL_PROXY, a value ignored by EnvHttpProxyAgent, creating a case where the pinned dispatcher is bypassed but no proxy is used. Additionally, unconditionally using EnvHttpProxyAgent (without the pinned lookup) in STRICT mode removes the DNS-rebinding protection that is the core security property of that mode.
  • src/infra/net/fetch-guard.ts — the changed condition on line 198 and its interaction with ALL_PROXY semantics and STRICT mode DNS-pinning.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.ts
Line: 198

Comment:
**`ALL_PROXY` triggers `EnvHttpProxyAgent` but is silently ignored by it**

`hasProxyEnvConfigured()` (in `proxy-env.ts`) returns `true` for `ALL_PROXY`/`all_proxy`, but your own codebase documents that `EnvHttpProxyAgent` ignores `ALL_PROXY` (see the comment in `resolveEnvHttpProxyUrl`):

```
// - ALL_PROXY is ignored by EnvHttpProxyAgent
```

So when *only* `ALL_PROXY` is set (no `HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY` variants), this new branch fires:
1. `hasProxyEnvConfigured()` returns `true`
2. `new EnvHttpProxyAgent()` is selected — bypassing `createPinnedDispatcher`
3. `EnvHttpProxyAgent` has no configured proxy (it ignored `ALL_PROXY`), so it falls back to a **direct connection without the pinned lookup**
4. DNS is re-resolved freely at connection time — the anti-DNS-rebinding protection provided by `createPinnedDispatcher` is silently lost

The fix should use `hasEnvHttpProxyConfigured()` (already exported from `proxy-env.ts`) which only returns `true` when `EnvHttpProxyAgent` would actually route through a proxy:

```suggestion
      if (canUseTrustedEnvProxy || hasEnvHttpProxyConfigured()) {
```

This ensures the branch is only taken when a proxy will actually be used, preserving DNS-pinning protection for `ALL_PROXY`-only configurations.

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

---

This is a comment left during a code review.
Path: src/infra/net/fetch-guard.ts
Line: 196-198

Comment:
**`STRICT` mode silently loses DNS-pinning protection when proxy env vars are present**

The separation between `STRICT` and `TRUSTED_ENV_PROXY` modes was architecturally intentional. The whole point of the pinned dispatcher in `STRICT` mode is to prevent DNS rebinding attacks: DNS is resolved once, validated against the SSRF policy, and then the connection is pinned to that exact IP so a racing DNS change cannot redirect the connection to an internal address.

When `EnvHttpProxyAgent` is used instead, the CONNECT tunnel carries the **original hostname** to the proxy, which then performs its own DNS resolution at connection time. The validated IP from `resolvePinnedHostnameWithPolicy` is never actually used for the connection. This means:

1. `resolvePinnedHostnameWithPolicy("evil-rebinding.example")` resolves to `1.2.3.4` → passes SSRF check ✓
2. DNS TTL expires; attacker's nameserver now points `evil-rebinding.example``192.168.1.10`
3. `EnvHttpProxyAgent` sends `CONNECT evil-rebinding.example:443` → proxy resolves → `192.168.1.10`
4. SSRF protection bypassed

`TRUSTED_ENV_PROXY` mode was an **explicit operator opt-in** that accepted this trade-off. This PR promotes the same weakened posture to `STRICT` mode for any deployment with proxy env vars — without any explicit acknowledgement in the mode name or call sites.

A safer approach that preserves the mode boundary would be to create the dispatcher via `createPinnedDispatcher` with `{ mode: "env-proxy" }`:

```typescript
} else if (params.pinDns !== false) {
  const proxyPolicy = hasEnvHttpProxyConfigured()
    ? { ...params.dispatcherPolicy, mode: "env-proxy" as const }
    : params.dispatcherPolicy;
  dispatcher = createPinnedDispatcher(pinned, proxyPolicy, params.policy);
}
```

This routes through the proxy while still threading the pinned `lookup` through the `connect` option of `EnvHttpProxyAgent`, preserving the DNS-pinning contract at the undici layer for the proxy connection itself.

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

---

This is a comment left during a code review.
Path: src/infra/net/fetch-guard.ts
Line: 196-198

Comment:
**`canUseTrustedEnvProxy` is now dead code**

Since the updated condition is `canUseTrustedEnvProxy || hasProxyEnvConfigured()`, and `canUseTrustedEnvProxy` is defined as `mode === GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY && hasProxyEnvConfigured()`, it is always a strict subset of `hasProxyEnvConfigured()`. The variable can never be `true` when `hasProxyEnvConfigured()` is `false`, so it no longer changes the branching outcome. The variable (and the double call to `hasProxyEnvConfigured()`) can be removed:

```suggestion
      const hasProxy = hasProxyEnvConfigured();
      if (hasProxy) {
```

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

Last reviewed commit: "fix(infra/net): use ..."

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: 39561aead5

ℹ️ 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".

@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 19, 2026

Thanks for the thorough review. All three points were valid — pushed an updated commit addressing all of them:

1. ALL_PROXY edge case — Switched to hasEnvHttpProxyConfigured() which only returns true when HTTP_PROXY/HTTPS_PROXY is actually set (matching EnvHttpProxyAgent's behavior).

2. DNS-rebinding protection — Replaced the bare EnvHttpProxyAgent() with createPinnedDispatcher(pinned, { mode: "env-proxy" }). This uses the existing env-proxy mode in createPinnedDispatcher which threads the pinned DNS lookup into the connect option of EnvHttpProxyAgent. STRICT mode's DNS-pinning contract is now preserved even when routing through the proxy.

3. Dead codecanUseTrustedEnvProxy is no longer redundant since we reverted to only using bare EnvHttpProxyAgent for the explicit TRUSTED_ENV_PROXY mode.

The updated approach: STRICT mode + proxy env → pinned dispatcher with mode: "env-proxy" (DNS pinning preserved, routed through proxy). TRUSTED_ENV_PROXY → bare EnvHttpProxyAgent (explicit opt-in, no pinning). No proxy → direct pinned dispatcher (existing behavior).

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: 7a2f5e5f43

ℹ️ 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".

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch from 7a2f5e5 to ac8ba11 Compare March 19, 2026 22:06
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: ac8ba11384

ℹ️ 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".

@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 19, 2026

Rebased on latest main (c3b05fc) — the CI failures were from being behind main (type errors in channelContentConfig.ts, groupTemplates.ts, etc.), unrelated to this change.

Regarding the Codex P1 about DNS pinning being lost in env-proxy mode: the env-proxy path in createPinnedDispatcher (in ssrf.ts) already handles this — it passes connect: withPinnedLookup(lookup, policy.connect) to EnvHttpProxyAgent. The connect option controls the target connection (after the CONNECT tunnel to the proxy), so the pinned DNS lookup IS threaded through. This mode was intentionally designed for exactly this use case.

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch from 85e8024 to 1e3f3f5 Compare March 19, 2026 22:44
@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 19, 2026

Pushed fix for the protocol-sensitive proxy detection — now passes parsedUrl.protocol to hasEnvHttpProxyConfigured() so http:// URLs only trigger env-proxy when HTTP_PROXY is actually set.

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: 1e3f3f5304

ℹ️ 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".

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch from 1e3f3f5 to 7606a26 Compare March 19, 2026 22:54
@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 19, 2026

Pushed fix for the explicit-proxy and direct-fallback concerns:

  • Only override to env-proxy when params.dispatcherPolicy?.mode is not already set
  • Callers with mode: "explicit-proxy" (Telegram media downloads) or mode: "direct" (fallback recovery) are left untouched
  • The env-proxy override only fires for the default no-mode case (web_fetch, web_search, etc.)

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch 3 times, most recently from 76a3025 to 3b3ae0a Compare March 20, 2026 00:37
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: 3b3ae0a9c4

ℹ️ 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".

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch from 3b3ae0a to 633eabd Compare March 20, 2026 02:13
@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 20, 2026

Added !params.policy?.allowPrivateNetwork guard — callers that explicitly allow private/local hosts (BlueBubbles attachments, remote memory over LAN) stay on the direct pinned path. Env-proxy only activates for the default public-internet case. Full pnpm check passes locally.

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: 633eabdf3b

ℹ️ 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".

@kkav004 kkav004 force-pushed the fix/fetch-guard-proxy-fallback branch 2 times, most recently from 9fab5b2 to 9423591 Compare March 20, 2026 02:28
@kkav004
Copy link
Copy Markdown
Contributor Author

kkav004 commented Mar 20, 2026

Added !params.policy?.dangerouslyAllowPrivateNetwork — both the legacy and current alias are now honored. Callers using either field stay on the direct pinned path.

Note: pnpm check has a pre-existing type error from f1ce679 (Discord commandDeploymentMode — merged to main just now). Our file passes all checks independently.

@mjamiv
Copy link
Copy Markdown

mjamiv commented Mar 30, 2026

Bumping this — the PR has 7 bot reviews but zero human reviews so far.

This fix is critical for anyone running OpenClaw behind an HTTP CONNECT proxy (e.g., OpenShell sandboxes, corporate networks). Without it, fetch-guard blocks all outbound requests because DNS doesn't resolve inside the proxy namespace — we currently work around it with a post-install patch script (openclaw-fetch-guard-patch.py), which is fragile and breaks on every update.

The latest commit from @kkav004 addresses the dangerouslyAllowPrivateNetwork alias. The bot feedback about STRICT mode DNS-pinning is worth discussing, but in proxy environments the proxy itself handles DNS resolution and enforces network policy, so the pinning tradeoff is acceptable — that's exactly what TRUSTED_ENV_PROXY mode was designed for.

Would really appreciate human eyes on this. Happy to test any requested changes.

cc @steipete @obviyus

@mjamiv
Copy link
Copy Markdown

mjamiv commented Mar 30, 2026

Also cc @scoootscooob @gumadeiras — would appreciate a human review on this one. It's been open since March 19 with only bot reviews so far.

Kiryl Kavalenka and others added 2 commits March 31, 2026 08:20
…ng DNS pinning

When HTTP_PROXY/HTTPS_PROXY env vars are configured, the SSRF guard's
pinned dispatcher connects directly to the DNS-resolved IP, bypassing the
proxy. This fails in environments where direct outbound connections are
blocked (OpenShell sandboxes, Docker containers, corporate networks).

Use `createPinnedDispatcher` with `mode: "env-proxy"` when
`hasEnvHttpProxyConfigured()` returns true. This preserves DNS-pinning
(the resolved IP is threaded into the connect option via
`EnvHttpProxyAgent`) while routing through the proxy.

- Uses `hasEnvHttpProxyConfigured()` (not `hasProxyEnvConfigured()`) to
  avoid the ALL_PROXY edge case where EnvHttpProxyAgent ignores ALL_PROXY
- Preserves STRICT mode's anti-DNS-rebinding guarantee
- TRUSTED_ENV_PROXY remains the explicit opt-in for unpinned proxy routing
- No change when proxy env vars are not set

Fixes openclaw#47598, openclaw#49948, openclaw#32947, openclaw#46306
Related: openclaw#45248
@obviyus obviyus force-pushed the fix/fetch-guard-proxy-fallback branch from 6cdd822 to 28528cb Compare March 31, 2026 02:56
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: 28528cb2fe

ℹ️ 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".

@obviyus obviyus self-assigned this Mar 31, 2026
@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented Mar 31, 2026

Addressed the latest review thread in 3a37bf4: the direct-path bypass is now keyed to the current request hostname instead of any non-empty allowedHostnames, and src/infra/net/fetch-guard.ssrf.test.ts now covers both the matching-host direct path and unrelated-allowlist proxy path.

Copy link
Copy Markdown
Contributor

@obviyus obviyus left a comment

Choose a reason for hiding this comment

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

Reviewed latest changes; landing now.

@obviyus obviyus merged commit 082778d into openclaw:main Mar 31, 2026
9 checks passed
@obviyus
Copy link
Copy Markdown
Contributor

obviyus commented Mar 31, 2026

Landed on main.

Thanks @kkav004.

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: 3a37bf4c3e

ℹ️ 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 on lines +204 to +207
const dispatcherPolicy: PinnedDispatcherPolicy | undefined = useEnvProxy
? Object.assign({}, params.dispatcherPolicy, { mode: "env-proxy" as const })
: params.dispatcherPolicy;
dispatcher = createPinnedDispatcher(pinned, dispatcherPolicy, params.policy);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep strict SSRF fetches bound to the pinned destination

Switching STRICT requests to mode: "env-proxy" here drops the DNS-to-connect binding that strict mode relied on. resolvePinnedHostnameWithPolicy() validates one DNS answer, but for proxied traffic undici routes through EnvHttpProxyAgent/ProxyAgent, where the proxy path does not honor the pinned connect.lookup for selecting the origin endpoint, so the actual CONNECT target is chosen later from hostname/proxy resolution. In proxy-enabled deployments, a DNS change between validation and connect can bypass strict destination pinning.

Useful? React with 👍 / 👎.

Comment on lines +200 to +203
const useEnvProxy =
hasEnvHttpProxyConfigured(protocol) &&
!params.dispatcherPolicy?.mode &&
!keepsTrustedHostOnDirectPath(parsedUrl.hostname, params.policy);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fallback to direct pinning when env proxy config is invalid

This branch now forces STRICT mode onto env-proxy whenever a proxy env var is merely non-empty, but it never validates that the env proxy settings can actually construct an EnvHttpProxyAgent. With malformed inherited HTTP_PROXY/HTTPS_PROXY, guarded fetches now fail during dispatcher creation instead of using the prior direct pinned path, which can break unrelated web_fetch/media requests in otherwise direct-connect environments.

Useful? React with 👍 / 👎.

pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…kav004)

* fix(infra/net): route through env proxy in STRICT mode while preserving DNS pinning

When HTTP_PROXY/HTTPS_PROXY env vars are configured, the SSRF guard's
pinned dispatcher connects directly to the DNS-resolved IP, bypassing the
proxy. This fails in environments where direct outbound connections are
blocked (OpenShell sandboxes, Docker containers, corporate networks).

Use `createPinnedDispatcher` with `mode: "env-proxy"` when
`hasEnvHttpProxyConfigured()` returns true. This preserves DNS-pinning
(the resolved IP is threaded into the connect option via
`EnvHttpProxyAgent`) while routing through the proxy.

- Uses `hasEnvHttpProxyConfigured()` (not `hasProxyEnvConfigured()`) to
  avoid the ALL_PROXY edge case where EnvHttpProxyAgent ignores ALL_PROXY
- Preserves STRICT mode's anti-DNS-rebinding guarantee
- TRUSTED_ENV_PROXY remains the explicit opt-in for unpinned proxy routing
- No change when proxy env vars are not set

Fixes openclaw#47598, openclaw#49948, openclaw#32947, openclaw#46306
Related: openclaw#45248

* test(infra): stabilize fetch guard proxy assertions

* fix: respect hostname-scoped proxy bypass (openclaw#50650) (thanks @kkav004)

---------

Co-authored-by: Kiryl Kavalenka <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…kav004)

* fix(infra/net): route through env proxy in STRICT mode while preserving DNS pinning

When HTTP_PROXY/HTTPS_PROXY env vars are configured, the SSRF guard's
pinned dispatcher connects directly to the DNS-resolved IP, bypassing the
proxy. This fails in environments where direct outbound connections are
blocked (OpenShell sandboxes, Docker containers, corporate networks).

Use `createPinnedDispatcher` with `mode: "env-proxy"` when
`hasEnvHttpProxyConfigured()` returns true. This preserves DNS-pinning
(the resolved IP is threaded into the connect option via
`EnvHttpProxyAgent`) while routing through the proxy.

- Uses `hasEnvHttpProxyConfigured()` (not `hasProxyEnvConfigured()`) to
  avoid the ALL_PROXY edge case where EnvHttpProxyAgent ignores ALL_PROXY
- Preserves STRICT mode's anti-DNS-rebinding guarantee
- TRUSTED_ENV_PROXY remains the explicit opt-in for unpinned proxy routing
- No change when proxy env vars are not set

Fixes openclaw#47598, openclaw#49948, openclaw#32947, openclaw#46306
Related: openclaw#45248

* test(infra): stabilize fetch guard proxy assertions

* fix: respect hostname-scoped proxy bypass (openclaw#50650) (thanks @kkav004)

---------

Co-authored-by: Kiryl Kavalenka <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
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.

3 participants