Skip to content

fix(infra/net): pass pinned DNS lookup to EnvHttpProxyAgent in trusted_env_proxy mode (CWE-918)#45248

Closed
kvenux wants to merge 2 commits intoopenclaw:mainfrom
kvenux:fix/cwe-918-infra-net-trusted-proxy
Closed

fix(infra/net): pass pinned DNS lookup to EnvHttpProxyAgent in trusted_env_proxy mode (CWE-918)#45248
kvenux wants to merge 2 commits intoopenclaw:mainfrom
kvenux:fix/cwe-918-infra-net-trusted-proxy

Conversation

@kvenux
Copy link
Copy Markdown

@kvenux kvenux commented Mar 13, 2026

Summary

Fix DNS pinning bypass in trusted_env_proxy mode where EnvHttpProxyAgent was created without the pinned lookup, allowing DNS rebinding attacks (CWE-918).

Vulnerability

  • CWE: CWE-918 (Server-Side Request Forgery)
  • File: src/infra/net/fetch-guard.ts:198-199
  • Severity: High
  • Impact: In trusted_env_proxy mode, fetchWithSsrFGuard created a bare EnvHttpProxyAgent() discarding the validated IP addresses from resolvePinnedHostnameWithPolicy(). An attacker could exploit DNS rebinding: hostname resolves to a public IP at validation time, then rebinds to 127.0.0.1 or 169.254.169.254 when the proxy agent performs its own DNS resolution. This bypasses all SSRF protections.

Reproduction

  1. Set HTTP_PROXY env var to any proxy
  2. Call fetchWithSsrFGuard with mode: trusted_env_proxy and a DNS rebinding domain
  3. Validation passes (public IP at check time)
  4. EnvHttpProxyAgent resolves DNS again, gets private IP
  5. SSRF to internal services succeeds

Fix

Pass pinned.lookup to EnvHttpProxyAgent via connect options, ensuring the proxy agent uses the pre-validated IP addresses instead of performing its own DNS resolution:

dispatcher = new EnvHttpProxyAgent({
  connect: { lookup: pinned.lookup },
});

Test Results

After fix (PASS)

$ pnpm vitest run src/infra/net/fetch-guard.proxy.security.test.ts

 ✓ src/infra/net/fetch-guard.proxy.security.test.ts (3 tests)
   ✓ should pass pinned lookup to EnvHttpProxyAgent in trusted_env_proxy mode
   ✓ should still block private IPs even in trusted_env_proxy mode
   ✓ should block DNS rebinding via trusted_env_proxy

Tests: 3 passed, 3 total

Full regression: all infra/net tests pass

$ pnpm vitest run src/infra/net/

 ✓ 8 test files, 62 tests passed

Checklist

  • POC test cases: DNS rebinding blocked, private IPs blocked
  • Normal input regression tests PASS
  • Module-level regression tests pass (62/62)
  • No new security issues introduced

AI Disclosure

  • AI-assisted (Claude)
  • Test coverage: POC verification + full module regression tests pass

…d_env_proxy mode (CWE-918)

In trusted_env_proxy mode, fetchWithSsrFGuard created a bare
EnvHttpProxyAgent() discarding the pinned DNS lookup from
resolvePinnedHostnameWithPolicy(). This allowed DNS rebinding attacks
where a hostname resolves to a public IP at validation time but rebinds
to a private/internal IP when the proxy agent performs its own DNS
resolution. Fix by passing pinned.lookup to the EnvHttpProxyAgent
connect options.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR fixes a DNS rebinding / SSRF bypass (CWE-918) in trusted_env_proxy mode by passing the pre-validated pinned.lookup callback to EnvHttpProxyAgent, preventing it from performing its own DNS resolution. The core fix in fetch-guard.ts is correct and consistent with the pattern already used in createPinnedDispatcher inside ssrf.ts for the env-proxy policy mode.

  • Fix (fetch-guard.ts): Change is minimal and correct — new EnvHttpProxyAgent({ connect: { lookup: pinned.lookup } }) pins DNS resolution at connection time to the addresses already validated by resolvePinnedHostnameWithPolicy.
  • Tests (fetch-guard.proxy.security.test.ts): The three new tests do not actually validate the fix. Test 1 only checks instanceof EnvHttpProxyAgent, which was already true before the fix. Tests 2 and 3 configure the lookupFn to return private IPs on every call, meaning the SSRF guard throws before EnvHttpProxyAgent is ever created — so both tests pass whether or not the fix is applied. A genuine rebinding regression test would need a lookupFn that returns a public IP on the first call (allowing validation to pass) and a private IP on subsequent calls, combined with a way to verify that undici uses the pinned lookup rather than the system resolver.

Confidence Score: 4/5

  • The production fix is correct and safe to merge; the accompanying tests do not actually cover the fixed code path.
  • The change to fetch-guard.ts is a well-understood, one-line fix that mirrors an identical pattern already present in ssrf.ts::createPinnedDispatcher. It introduces no new risk. The score is not 5 because the test suite claimed to cover the rebinding scenario but the tests are structured in a way that all three would pass even if the fix were reverted, leaving the security regression undetected by CI.
  • src/infra/net/fetch-guard.proxy.security.test.ts — the tests need to be reworked to actually exercise the connect.lookup code path introduced by the fix.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.proxy.security.test.ts
Line: 52-69

Comment:
**Test does not actually validate DNS rebinding protection**

This test does not simulate a DNS rebinding attack — it simulates a hostname that *always* resolves to a private IP (`127.0.0.1`). Because `resolvePinnedHostnameWithPolicy` performs the SSRF check in Phase 2 (`assertAllowedResolvedAddressesOrThrow`) immediately after the DNS lookup, the call throws a `SsrFBlockedError` **before** the `EnvHttpProxyAgent` is ever created. This means the test would pass even if you reverted the entire fix to `new EnvHttpProxyAgent()`.

A genuine rebinding test needs the lookup to return different results at different call times — a public IP on the first call (letting validation pass) and a private IP on any subsequent call (simulating the rebind). The fix's protection is that `pinned.lookup` pins the pre-validated addresses, preventing `EnvHttpProxyAgent` from performing a fresh DNS resolution that would return the rebound IP. The current test never reaches that code path.

Example of a lookup that could exercise the actual fix:
```typescript
let callCount = 0;
const rebindLookup = vi.fn(async () => {
  callCount += 1;
  // First call: public IP (passes validation), second call: private IP (the rebind)
  return callCount === 1
    ? [{ address: "93.184.216.34", family: 4 }]
    : [{ address: "127.0.0.1", family: 4 }];
}) as unknown as LookupFn;
```
Note that verifying this scenario in a pure unit test is difficult because `fetchImpl` is mocked and won't trigger a second DNS resolution through undici; an integration-level test would be needed to fully exercise the `connect.lookup` path inside `EnvHttpProxyAgent`.

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.proxy.security.test.ts
Line: 15-36

Comment:
**Test assertion does not verify the pinned lookup is wired in**

The assertion `expect(capturedDispatcher).toBeInstanceOf(EnvHttpProxyAgent)` was already true **before the fix**, since `new EnvHttpProxyAgent()` (without options) is still an `EnvHttpProxyAgent`. This test therefore does not distinguish the before-fix from the after-fix behaviour, and would pass if the fix were reverted.

To verify the actual change, the test should inspect the dispatcher's internal `options` or, more robustly, spy on the `EnvHttpProxyAgent` constructor and assert it was called with `connect.lookup` set:
```typescript
const EnvHttpProxyAgentSpy = vi.spyOn(
  await import("undici"),
  "EnvHttpProxyAgent"
);
// ... run fetchWithSsrFGuard ...
expect(EnvHttpProxyAgentSpy).toHaveBeenCalledWith(
  expect.objectContaining({
    connect: expect.objectContaining({ lookup: expect.any(Function) }),
  })
);
```

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

Last reviewed commit: 9b2d6c4

Comment on lines +52 to +69
it("should block DNS rebinding via trusted_env_proxy", async () => {
vi.stubEnv("HTTP_PROXY", "http://127.0.0.1:7890");
// Simulate DNS rebinding: hostname resolves to private IP
const rebindLookup = vi.fn(async () => [
{ address: "127.0.0.1", family: 4 },
]) as unknown as LookupFn;
const fetchImpl = vi.fn();

await expect(
fetchWithSsrFGuard({
url: "https://rebind.attacker.com/steal",
fetchImpl,
lookupFn: rebindLookup,
mode: GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY,
}),
).rejects.toThrow(/private|internal|blocked/i);
expect(fetchImpl).not.toHaveBeenCalled();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test does not actually validate DNS rebinding protection

This test does not simulate a DNS rebinding attack — it simulates a hostname that always resolves to a private IP (127.0.0.1). Because resolvePinnedHostnameWithPolicy performs the SSRF check in Phase 2 (assertAllowedResolvedAddressesOrThrow) immediately after the DNS lookup, the call throws a SsrFBlockedError before the EnvHttpProxyAgent is ever created. This means the test would pass even if you reverted the entire fix to new EnvHttpProxyAgent().

A genuine rebinding test needs the lookup to return different results at different call times — a public IP on the first call (letting validation pass) and a private IP on any subsequent call (simulating the rebind). The fix's protection is that pinned.lookup pins the pre-validated addresses, preventing EnvHttpProxyAgent from performing a fresh DNS resolution that would return the rebound IP. The current test never reaches that code path.

Example of a lookup that could exercise the actual fix:

let callCount = 0;
const rebindLookup = vi.fn(async () => {
  callCount += 1;
  // First call: public IP (passes validation), second call: private IP (the rebind)
  return callCount === 1
    ? [{ address: "93.184.216.34", family: 4 }]
    : [{ address: "127.0.0.1", family: 4 }];
}) as unknown as LookupFn;

Note that verifying this scenario in a pure unit test is difficult because fetchImpl is mocked and won't trigger a second DNS resolution through undici; an integration-level test would be needed to fully exercise the connect.lookup path inside EnvHttpProxyAgent.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.proxy.security.test.ts
Line: 52-69

Comment:
**Test does not actually validate DNS rebinding protection**

This test does not simulate a DNS rebinding attack — it simulates a hostname that *always* resolves to a private IP (`127.0.0.1`). Because `resolvePinnedHostnameWithPolicy` performs the SSRF check in Phase 2 (`assertAllowedResolvedAddressesOrThrow`) immediately after the DNS lookup, the call throws a `SsrFBlockedError` **before** the `EnvHttpProxyAgent` is ever created. This means the test would pass even if you reverted the entire fix to `new EnvHttpProxyAgent()`.

A genuine rebinding test needs the lookup to return different results at different call times — a public IP on the first call (letting validation pass) and a private IP on any subsequent call (simulating the rebind). The fix's protection is that `pinned.lookup` pins the pre-validated addresses, preventing `EnvHttpProxyAgent` from performing a fresh DNS resolution that would return the rebound IP. The current test never reaches that code path.

Example of a lookup that could exercise the actual fix:
```typescript
let callCount = 0;
const rebindLookup = vi.fn(async () => {
  callCount += 1;
  // First call: public IP (passes validation), second call: private IP (the rebind)
  return callCount === 1
    ? [{ address: "93.184.216.34", family: 4 }]
    : [{ address: "127.0.0.1", family: 4 }];
}) as unknown as LookupFn;
```
Note that verifying this scenario in a pure unit test is difficult because `fetchImpl` is mocked and won't trigger a second DNS resolution through undici; an integration-level test would be needed to fully exercise the `connect.lookup` path inside `EnvHttpProxyAgent`.

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

Comment on lines +15 to +36
it("should pass pinned lookup to EnvHttpProxyAgent in trusted_env_proxy mode", async () => {
vi.stubEnv("HTTP_PROXY", "http://127.0.0.1:7890");
const lookupFn = createPublicLookup();
let capturedDispatcher: unknown = null;

const fetchImpl = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
const requestInit = init as RequestInit & { dispatcher?: unknown };
capturedDispatcher = requestInit.dispatcher;
return new Response("ok", { status: 200 });
});

const result = await fetchWithSsrFGuard({
url: "https://public.example/resource",
fetchImpl,
lookupFn,
mode: GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY,
});

expect(fetchImpl).toHaveBeenCalledTimes(1);
expect(capturedDispatcher).toBeInstanceOf(EnvHttpProxyAgent);
await result.release();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test assertion does not verify the pinned lookup is wired in

The assertion expect(capturedDispatcher).toBeInstanceOf(EnvHttpProxyAgent) was already true before the fix, since new EnvHttpProxyAgent() (without options) is still an EnvHttpProxyAgent. This test therefore does not distinguish the before-fix from the after-fix behaviour, and would pass if the fix were reverted.

To verify the actual change, the test should inspect the dispatcher's internal options or, more robustly, spy on the EnvHttpProxyAgent constructor and assert it was called with connect.lookup set:

const EnvHttpProxyAgentSpy = vi.spyOn(
  await import("undici"),
  "EnvHttpProxyAgent"
);
// ... run fetchWithSsrFGuard ...
expect(EnvHttpProxyAgentSpy).toHaveBeenCalledWith(
  expect.objectContaining({
    connect: expect.objectContaining({ lookup: expect.any(Function) }),
  })
);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.proxy.security.test.ts
Line: 15-36

Comment:
**Test assertion does not verify the pinned lookup is wired in**

The assertion `expect(capturedDispatcher).toBeInstanceOf(EnvHttpProxyAgent)` was already true **before the fix**, since `new EnvHttpProxyAgent()` (without options) is still an `EnvHttpProxyAgent`. This test therefore does not distinguish the before-fix from the after-fix behaviour, and would pass if the fix were reverted.

To verify the actual change, the test should inspect the dispatcher's internal `options` or, more robustly, spy on the `EnvHttpProxyAgent` constructor and assert it was called with `connect.lookup` set:
```typescript
const EnvHttpProxyAgentSpy = vi.spyOn(
  await import("undici"),
  "EnvHttpProxyAgent"
);
// ... run fetchWithSsrFGuard ...
expect(EnvHttpProxyAgentSpy).toHaveBeenCalledWith(
  expect.objectContaining({
    connect: expect.objectContaining({ lookup: expect.any(Function) }),
  })
);
```

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

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: 9b2d6c4b94

ℹ️ 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 +202 to +203
dispatcher = new EnvHttpProxyAgent({
connect: { lookup: pinned.lookup },
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 Don’t treat EnvHttpProxyAgent connect.lookup as proxy-path pinning

Using new EnvHttpProxyAgent({ connect: { lookup: pinned.lookup } }) here does not pin DNS for requests that actually go through the proxy, so the rebinding gap remains in trusted_env_proxy mode for proxied targets. The repository already documents and tests that EnvHttpProxyAgent applies connect to the no-proxy agent path, while proxied HTTPS transport uses proxy-specific settings instead (src/telegram/fetch.ts:260-262, src/telegram/fetch.env-proxy-runtime.test.ts:29-56), so this change only protects NO_PROXY/direct hops, not the proxied flow described in the commit message.

Useful? React with 👍 / 👎.

});

expect(fetchImpl).toHaveBeenCalledTimes(1);
expect(capturedDispatcher).toBeInstanceOf(EnvHttpProxyAgent);
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 Assert the dispatcher options, not just its class

This check only verifies that a dispatcher is an EnvHttpProxyAgent, so the test would still pass if the pinned lookup were accidentally removed and the constructor reverted to new EnvHttpProxyAgent(). Because this test is meant to guard a security fix, it needs to verify the lookup wiring itself (for example by inspecting the agent options/internal symbols) to catch regressions in future refactors.

Useful? React with 👍 / 👎.

Previous test only checked instanceof which was true before the fix.
Now spy on the constructor and assert connect.lookup is a function.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
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: 8410518955

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

// DNS rebinding attacks. A bare EnvHttpProxyAgent() would perform
// its own DNS resolution, discarding the validated IP addresses.
dispatcher = new EnvHttpProxyAgent({
connect: { lookup: pinned.lookup },
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 Do not rely on connect.lookup for proxied requests

In trusted_env_proxy mode this sets EnvHttpProxyAgent({ connect: { lookup } }), but for requests that actually go through HTTP(S)_PROXY (i.e., not in NO_PROXY) Undici does not use connect for the proxied HTTPS transport path, so the pinned resolver is bypassed and DNS can still be rebound after validation. This means the commit’s security fix only affects direct/no-proxy hops, not the proxied flow it claims to protect.

Useful? React with 👍 / 👎.

Comment on lines +9 to +11
vi.mock("undici", async (importOriginal) => {
const actual = (await importOriginal()) as Record<string, unknown>;
return { ...actual, EnvHttpProxyAgent: envHttpProxyAgentCtor };
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 Exercise the real proxy agent in the security regression test

This test replaces EnvHttpProxyAgent with a mock constructor and only asserts that a function is passed in options, so it cannot detect whether real proxied traffic actually honors that lookup. As written, the test still passes even if the runtime proxy path ignores connect.lookup, which makes the security regression check a false positive for the vulnerability this commit targets.

Useful? React with 👍 / 👎.

@kvenux kvenux closed this Mar 15, 2026
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 19, 2026
…s configured

When HTTP proxy environment variables (HTTPS_PROXY, etc.) are set, the
SSRF guard's pinned dispatcher connects directly to the DNS-resolved IP,
bypassing the proxy entirely. This fails in environments where direct
outbound connections are blocked:

- OpenShell/NemoClaw sandboxes (network namespace blocks direct TCP)
- Docker containers with network isolation
- Corporate networks requiring proxy for all HTTPS traffic

The SSRF hostname validation (`resolvePinnedHostnameWithPolicy`) still
runs before the fetch — this change only affects how the connection is
established, not what's allowed.

Fixes openclaw#47598, openclaw#49948, openclaw#32947, openclaw#46306
Related: openclaw#45248 (improved TRUSTED_ENV_PROXY mode, but STRICT mode still broken)
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 19, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 19, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 19, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 19, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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
kkav004 pushed a commit to kkav004/openclaw that referenced this pull request Mar 20, 2026
…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 added a commit that referenced this pull request Mar 31, 2026
* 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 #47598, #49948, #32947, #46306
Related: #45248

* test(infra): stabilize fetch guard proxy assertions

* fix: respect hostname-scoped proxy bypass (#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]>
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.

2 participants