fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode#66458
fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode#66458vincentkoc merged 2 commits intomainfrom
Conversation
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟠 SSRF protections bypassed when auto-upgrading to TRUSTED_ENV_PROXY based on HTTP_PROXY/HTTPS_PROXY env vars
Description
Impact:
Vulnerable code paths:
Vulnerable code: // shared.ts
const resolvedMode = options?.mode ?? (
shouldAutoUpgradeToTrustedEnvProxy({ url, dispatcherPolicy: options?.dispatcherPolicy })
? GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY
: undefined
);
...
...(resolvedMode ? { mode: resolvedMode } : {}),// fetch-guard.ts
const canUseTrustedEnvProxy =
mode === GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY && hasProxyEnvConfigured();
if (canUseTrustedEnvProxy) {
dispatcher = createHttp1EnvHttpProxyAgent();
} else if (params.pinDns === false) {
await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, ...);
...
} else {
const pinned = await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, ...);
...
}RecommendationDo not allow Options (pick one, in order of safety):
Example sketch: // In fetchWithSsrFGuard()
if (mode === GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY) {
// Still block obvious SSRF targets
await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, {
lookupFn: params.lookupFn,
policy: params.policy,
});
// Then dispatch via env proxy without pinning
dispatcher = createHttp1EnvHttpProxyAgent();
}
Also document clearly that Analyzed PR: #66458 at commit Last updated on: 2026-04-14T09:31:05Z |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eeb8a1bd89
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| (shouldAutoUpgradeToTrustedEnvProxy({ | ||
| url, | ||
| dispatcherPolicy: options?.dispatcherPolicy, | ||
| }) | ||
| ? GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY |
There was a problem hiding this comment.
Re-check NO_PROXY on redirect hops before trusted mode
resolvedMode is computed once from the initial URL, but fetchWithSsrFGuard reuses that mode across all redirect hops. In environments with HTTP_PROXY/HTTPS_PROXY plus NO_PROXY, an initial non-bypassed URL can redirect to a NO_PROXY target (for example localhost), and EnvHttpProxyAgent will then connect directly while trusted mode skips pinned-DNS/private-network SSRF checks. This reintroduces the exact bypass the NO_PROXY gate was meant to prevent, so the mode decision needs to be re-evaluated per hop (or forced back to strict on redirected NO_PROXY matches).
Useful? React with 👍 / 👎.
Greptile SummaryThis PR adds Confidence Score: 5/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/infra/net/proxy-env.ts
Line: 82
Comment:
**Inconsistent empty-lower-case precedence vs `resolveEnvHttpProxyUrl`**
`normalizeProxyEnvValue` returns `null` for a set-but-empty string, and `null ?? rhs` still evaluates `rhs`. So `no_proxy=""` with `NO_PROXY="api.openai.com"` will resolve to `"api.openai.com"` here — but the analogous path in `resolveEnvHttpProxyUrl` uses `!== undefined` so an empty `http_proxy=""` correctly suppresses `HTTP_PROXY`. In a proxy-only env, this mismatch could prevent the auto-upgrade from firing for targets listed in `NO_PROXY` even when the operator intended `no_proxy=""` to clear the bypass list.
```suggestion
const lowerNoProxy = normalizeProxyEnvValue(env.no_proxy);
const raw = lowerNoProxy !== undefined ? lowerNoProxy : normalizeProxyEnvValue(env.NO_PROXY);
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "Update CHANGELOG.md" | Re-trigger Greptile |
| * SSRF bypass. | ||
| */ | ||
| export function matchesNoProxy(targetUrl: string, env: NodeJS.ProcessEnv = process.env): boolean { | ||
| const raw = normalizeProxyEnvValue(env.no_proxy) ?? normalizeProxyEnvValue(env.NO_PROXY); |
There was a problem hiding this comment.
Inconsistent empty-lower-case precedence vs
resolveEnvHttpProxyUrl
normalizeProxyEnvValue returns null for a set-but-empty string, and null ?? rhs still evaluates rhs. So no_proxy="" with NO_PROXY="api.openai.com" will resolve to "api.openai.com" here — but the analogous path in resolveEnvHttpProxyUrl uses !== undefined so an empty http_proxy="" correctly suppresses HTTP_PROXY. In a proxy-only env, this mismatch could prevent the auto-upgrade from firing for targets listed in NO_PROXY even when the operator intended no_proxy="" to clear the bypass list.
| const raw = normalizeProxyEnvValue(env.no_proxy) ?? normalizeProxyEnvValue(env.NO_PROXY); | |
| const lowerNoProxy = normalizeProxyEnvValue(env.no_proxy); | |
| const raw = lowerNoProxy !== undefined ? lowerNoProxy : normalizeProxyEnvValue(env.NO_PROXY); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/net/proxy-env.ts
Line: 82
Comment:
**Inconsistent empty-lower-case precedence vs `resolveEnvHttpProxyUrl`**
`normalizeProxyEnvValue` returns `null` for a set-but-empty string, and `null ?? rhs` still evaluates `rhs`. So `no_proxy=""` with `NO_PROXY="api.openai.com"` will resolve to `"api.openai.com"` here — but the analogous path in `resolveEnvHttpProxyUrl` uses `!== undefined` so an empty `http_proxy=""` correctly suppresses `HTTP_PROXY`. In a proxy-only env, this mismatch could prevent the auto-upgrade from firing for targets listed in `NO_PROXY` even when the operator intended `no_proxy=""` to clear the bypass list.
```suggestion
const lowerNoProxy = normalizeProxyEnvValue(env.no_proxy);
const raw = lowerNoProxy !== undefined ? lowerNoProxy : normalizeProxyEnvValue(env.NO_PROXY);
```
How can I resolve this? If you propose a fix, please make it concise.…d env proxy mode (#66458) * fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode * Update CHANGELOG.md
…d env proxy mode (openclaw#66458) * fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode * Update CHANGELOG.md
…d env proxy mode (openclaw#66458) * fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode * Update CHANGELOG.md
…d env proxy mode (openclaw#66458) * fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode * Update CHANGELOG.md
…d env proxy mode (openclaw#66458) * fix(media-understanding): auto-upgrade provider HTTP helper to trusted env proxy mode * Update CHANGELOG.md
Summary
ENOTFOUND/EAI_AGAINbefore the configured proxy ever saw the requestsrc/media-understanding/shared.tscalledfetchWithSsrFGuard()without selectingtrusted_env_proxymode for env-proxy cases, and there was no sharedNO_PROXYmatcher to keep the auto-upgrade from bypassing strict mode on direct-bypass targetsNO_PROXYbypasses the target or a caller supplies explicit dispatcher policy, and add focused coverage for the proxy-env matcher plus the guarded helper behaviorFixes #52162.
Related #66245.
Supersedes #64974.
Verification
pnpm test:serial src/media-understanding/shared.test.ts src/infra/net/proxy-env.test.ts src/infra/net/fetch-guard.ssrf.test.tspnpm buildChangelog
Thanks @mjamiv and @vincentkoc