fix(web-fetch): expose ssrfPolicy.allowRfc2544BenchmarkRange config option#25258
fix(web-fetch): expose ssrfPolicy.allowRfc2544BenchmarkRange config option#25258byungsker wants to merge 1 commit intoopenclaw:mainfrom
Conversation
…ption web_fetch unconditionally applied the SSRF block list, which includes the RFC 2544 benchmark range (198.18.0.0/15). Clash/mihomo and similar proxy tools use this range for their fake-ip mode: DNS queries return virtual IPs in 198.18.x.x that are transparently forwarded to the real destination by the kernel. The block made web_fetch completely unusable for these users after the RFC 2544 check was added in 2026.2.22-2. The underlying SsrFPolicy.allowRfc2544BenchmarkRange option already existed (used by the Telegram delivery path), and was partially wired into the web_fetch tool. However, ToolsWebFetchSchema used .strict() without including ssrfPolicy, so the config field was rejected at validation time with 'Unrecognized key: ssrfPolicy'. Fix: - Add ssrfPolicy, readability, and firecrawl to ToolsWebFetchSchema (all three were present in types.tools.ts and schema.help.ts but missing from the Zod validation schema, causing config rejections) - Wire ssrfPolicy.allowRfc2544BenchmarkRange through to fetchWithSsrFGuard - Add schema help and labels for tools.web.fetch.ssrfPolicy - Add config regression tests for ssrfPolicy, readability, and firecrawl When tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange is true, the 198.18.0.0/15 range is allowed; all other SSRF protections (private IPs, loopback, cloud-metadata hostnames, etc.) remain active. Fixes openclaw#25215
|
This would fix #25215 for us — we run Clash/mihomo in fake-ip mode and had to pin an older version to work around the SSRF block. Clean approach exposing it as a config option. 👍 |
|
This fix is critical for many users in China who rely on Clash/mihomo with fake-ip mode. The web_fetch tool is completely unusable without this. Hope this can be merged soon! Thanks for the clean implementation. |
|
This pull request has been automatically marked as stale due to inactivity. |
|
This fix is critical for many users in China who rely on Clash/mihomo with fake-ip mode. The web_fetch tool is completely unusable without this. Hope this can be merged soon! Thanks for the clean implementation. |
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 SSRF and Firecrawl API key exfiltration via configurable firecrawl.baseUrl
Description
Impact:
Vulnerable flow:
Vulnerable code: const endpoint = resolveFirecrawlEndpoint(params.baseUrl);
const res = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});RecommendationTreat Preferred (most secure): disallow arbitrary baseUrl
If custom base URLs must be supported (self-hosted Firecrawl):
Example hardening: import { fetchWithSsrFGuard } from "../../infra/net/fetch-guard.js";
// Validate baseUrl (https + allowlisted hostname) before use.
const endpoint = resolveFirecrawlEndpoint(validatedBaseUrl);
const { response } = await fetchWithSsrFGuard({
url: endpoint,
init: {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
},
// ensure private/internal ranges remain blocked for this config-driven request
policy: { dangerouslyAllowPrivateNetwork: false },
auditContext: "firecrawl",
});Additionally:
Analyzed PR: #25258 at commit Last updated on: 2026-03-09T13:51:05Z |
|
Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit. |
Greptile SummaryThis PR fixes two distinct bugs: (1)
Confidence Score: 5/5
Last reviewed commit: 41ab60b |
Greptile SummaryThis PR fixes a real-world breakage where
Confidence Score: 5/5
Last reviewed commit: 41ab60b |
| allowRfc2544BenchmarkRange: | ||
| fetch && | ||
| "ssrfPolicy" in fetch && | ||
| typeof fetch.ssrfPolicy === "object" && | ||
| fetch.ssrfPolicy !== null && | ||
| "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy | ||
| ? fetch.ssrfPolicy.allowRfc2544BenchmarkRange === true | ||
| : false, |
There was a problem hiding this comment.
Overly verbose runtime check — simpler equivalent available
The inline guard chain (fetch && "ssrfPolicy" in fetch && typeof … === "object" && … !== null && "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy) is considerably more verbose than necessary. Since fetch is typed as WebFetchConfig which now includes ssrfPolicy?: { allowRfc2544BenchmarkRange?: boolean }, TypeScript already guarantees the shape. Other boolean flags in this file (e.g. readabilityEnabled via resolveFetchReadabilityEnabled) use a lightweight resolver or a simple optional-chain expression. The entire expression collapses to:
| allowRfc2544BenchmarkRange: | |
| fetch && | |
| "ssrfPolicy" in fetch && | |
| typeof fetch.ssrfPolicy === "object" && | |
| fetch.ssrfPolicy !== null && | |
| "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy | |
| ? fetch.ssrfPolicy.allowRfc2544BenchmarkRange === true | |
| : false, | |
| allowRfc2544BenchmarkRange: fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true, |
This is semantically identical (returns false for undefined, null, missing key, or an explicit false) and aligns with how other optional boolean flags are resolved in the codebase.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/web-fetch.ts
Line: 773-780
Comment:
**Overly verbose runtime check — simpler equivalent available**
The inline guard chain (`fetch && "ssrfPolicy" in fetch && typeof … === "object" && … !== null && "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy`) is considerably more verbose than necessary. Since `fetch` is typed as `WebFetchConfig` which now includes `ssrfPolicy?: { allowRfc2544BenchmarkRange?: boolean }`, TypeScript already guarantees the shape. Other boolean flags in this file (e.g. `readabilityEnabled` via `resolveFetchReadabilityEnabled`) use a lightweight resolver or a simple optional-chain expression. The entire expression collapses to:
```suggestion
allowRfc2544BenchmarkRange: fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true,
```
This is semantically identical (returns `false` for `undefined`, `null`, missing key, or an explicit `false`) and aligns with how other optional boolean flags are resolved in the codebase.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Greptile SummaryThis PR fixes a real regression where Key changes:
Confidence Score: 4/5
Last reviewed commit: 41ab60b |
| allowRfc2544BenchmarkRange: | ||
| fetch && | ||
| "ssrfPolicy" in fetch && | ||
| typeof fetch.ssrfPolicy === "object" && | ||
| fetch.ssrfPolicy !== null && | ||
| "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy | ||
| ? fetch.ssrfPolicy.allowRfc2544BenchmarkRange === true | ||
| : false, |
There was a problem hiding this comment.
Inconsistency: config value computed inside execute callback
All other config-derived values (readabilityEnabled, firecrawlEnabled, firecrawlApiKey, userAgent, etc.) are pre-computed outside the execute callback and simply referenced inside it. allowRfc2544BenchmarkRange is the only one that is computed inline on every execute call.
Additionally, since ssrfPolicy is now a properly-typed field on WebFetchConfig (added to types.tools.ts in this PR), the verbose runtime inspection is unnecessary — TypeScript already knows the field exists. This could be simplified to a single line, extracted before the callback like all other values:
| allowRfc2544BenchmarkRange: | |
| fetch && | |
| "ssrfPolicy" in fetch && | |
| typeof fetch.ssrfPolicy === "object" && | |
| fetch.ssrfPolicy !== null && | |
| "allowRfc2544BenchmarkRange" in fetch.ssrfPolicy | |
| ? fetch.ssrfPolicy.allowRfc2544BenchmarkRange === true | |
| : false, | |
| allowRfc2544BenchmarkRange: fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true, |
For full consistency with the rest of the function, the extraction should be placed alongside the other const declarations above the return { ... } block:
const allowRfc2544BenchmarkRange = fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true;and then just reference allowRfc2544BenchmarkRange in the runWebFetch call.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/web-fetch.ts
Line: 773-780
Comment:
**Inconsistency: config value computed inside execute callback**
All other config-derived values (`readabilityEnabled`, `firecrawlEnabled`, `firecrawlApiKey`, `userAgent`, etc.) are pre-computed outside the `execute` callback and simply referenced inside it. `allowRfc2544BenchmarkRange` is the only one that is computed inline on every execute call.
Additionally, since `ssrfPolicy` is now a properly-typed field on `WebFetchConfig` (added to `types.tools.ts` in this PR), the verbose runtime inspection is unnecessary — TypeScript already knows the field exists. This could be simplified to a single line, extracted before the callback like all other values:
```suggestion
allowRfc2544BenchmarkRange: fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true,
```
For full consistency with the rest of the function, the extraction should be placed alongside the other `const` declarations above the `return { ... }` block:
```ts
const allowRfc2544BenchmarkRange = fetch?.ssrfPolicy?.allowRfc2544BenchmarkRange === true;
```
and then just reference `allowRfc2544BenchmarkRange` in the `runWebFetch` call.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Problem
web_fetchunconditionally applied the SSRF block list including the RFC 2544 benchmark range (198.18.0.0/15). Clash/mihomo and similar proxy tools use this range for fake-ip mode: DNS queries return virtual IPs in198.18.x.xthat are transparently forwarded to the real destination by the kernel.This made
web_fetchcompletely unusable for users running a local proxy after the RFC 2544 check was added in 2026.2.22-2.Furthermore, even though
SsrFPolicy.allowRfc2544BenchmarkRangealready existed and partial wiring was inweb-fetch.ts, theToolsWebFetchSchemaZod schema used.strict()without includingssrfPolicy— so the config field was silently rejected at validation time with:Additionally,
readabilityandfirecrawlsub-config were in the same situation: present intypes.tools.tsandschema.help.tsbut missing fromToolsWebFetchSchema, causing config validation failures.Fixes #25215
Changes
src/config/zod-schema.agent-runtime.ts— AddssrfPolicy,readability, andfirecrawltoToolsWebFetchSchema(all three caused unrecognized-key errors due to.strict())src/agents/tools/web-fetch.ts— WiressrfPolicy.allowRfc2544BenchmarkRangethrough tofetchWithSsrFGuardsrc/config/types.tools.ts— TypeScript type for the new config fieldsrc/config/schema.help.ts— Help text fortools.web.fetch.ssrfPolicyandtools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRangesrc/config/schema.labels.ts— UI labels for the new ssrfPolicy fieldssrc/agents/tools/web-fetch.ssrf.test.ts— Tests for RFC 2544 range allow/block behaviorsrc/config/config.schema-regressions.test.ts— Config regression tests forssrfPolicy,readability, andfirecrawlConfig
Users running Clash/mihomo in fake-ip mode can now add to their config:
{ "tools": { "web": { "fetch": { "ssrfPolicy": { "allowRfc2544BenchmarkRange": true } } } } }When enabled, the
198.18.0.0/15range is permitted forweb_fetch; all other SSRF protections (private IPs, loopback, cloud-metadata hostnames, etc.) remain active.Greptile Summary
Added
ssrfPolicy.allowRfc2544BenchmarkRangeconfig option to fixweb_fetchcompatibility with Clash/mihomo proxy fake-ip mode, and fixed schema validation bugs that were silently rejectingssrfPolicy,readability, andfirecrawlconfig fields.Key changes:
ssrfPolicy.allowRfc2544BenchmarkRangethrough from config tofetchWithSsrFGuardinweb-fetch.ts:539ToolsWebFetchSchemamissing fields (readability,ssrfPolicy,firecrawl) that caused silent rejection with.strict()validationConfidence Score: 5/5
Last reviewed commit: 41ab60b