fix(web-tools): land #31176 allow RFC2544 trusted fetch range (@sunkinux)

Landed from contributor PR #31176 by @sunkinux.

Co-authored-by: sunkinux <sunkinux@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-03-02 03:43:18 +00:00
parent 2a252a14cc
commit e1bf9591c3
4 changed files with 55 additions and 2 deletions

View File

@@ -113,6 +113,7 @@ Docs: https://docs.openclaw.ai
- OpenAI Responses/Compaction: rewrite and unify the OpenAI Responses store patches to treat empty `baseUrl` as non-direct, honor `compat.supportsStore=false`, and auto-inject server-side compaction `context_management` for compatible direct OpenAI models (with per-model opt-out/threshold overrides). Landed from contributor PRs #16930 (@OiPunk), #22441 (@EdwardWu7), and #25088 (@MoerAI). Thanks @OiPunk, @EdwardWu7, and @MoerAI.
- Signal/Sync message null-handling: treat `syncMessage` presence (including `null`) as sync envelope traffic so replayed sentTranscript payloads cannot bypass loop guards after daemon restart. Landed from contributor PR #31138 by @Sid-Qin. Thanks @Sid-Qin.
- Inbound metadata/Multi-account routing: include `account_id` in trusted inbound metadata so multi-account channel sessions can reliably disambiguate the receiving account in prompt context. Landed from contributor PR #30984 by @Stxle2. Thanks @Stxle2.
- Web tools/RFC2544 fake-IP compatibility: allow RFC2544 benchmark range (`198.18.0.0/15`) for trusted web-tool fetch endpoints so proxy fake-IP networking modes do not trigger false SSRF blocks. Landed from contributor PR #31176 by @sunkinux. Thanks @sunkinux.
- Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky.
- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff.
## Unreleased

View File

@@ -0,0 +1,51 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { fetchWithSsrFGuard } from "../../infra/net/fetch-guard.js";
import { withStrictWebToolsEndpoint, withTrustedWebToolsEndpoint } from "./web-guarded-fetch.js";
vi.mock("../../infra/net/fetch-guard.js", () => ({
fetchWithSsrFGuard: vi.fn(),
}));
describe("web-guarded-fetch", () => {
afterEach(() => {
vi.clearAllMocks();
});
it("uses trusted SSRF policy for trusted web tools endpoints", async () => {
vi.mocked(fetchWithSsrFGuard).mockResolvedValue({
response: new Response("ok", { status: 200 }),
finalUrl: "https://example.com",
release: async () => {},
});
await withTrustedWebToolsEndpoint({ url: "https://example.com" }, async () => undefined);
expect(fetchWithSsrFGuard).toHaveBeenCalledWith(
expect.objectContaining({
url: "https://example.com",
policy: expect.objectContaining({
dangerouslyAllowPrivateNetwork: true,
allowRfc2544BenchmarkRange: true,
}),
}),
);
});
it("keeps strict endpoint policy unchanged", async () => {
vi.mocked(fetchWithSsrFGuard).mockResolvedValue({
response: new Response("ok", { status: 200 }),
finalUrl: "https://example.com",
release: async () => {},
});
await withStrictWebToolsEndpoint({ url: "https://example.com" }, async () => undefined);
expect(fetchWithSsrFGuard).toHaveBeenCalledWith(
expect.objectContaining({
url: "https://example.com",
}),
);
const call = vi.mocked(fetchWithSsrFGuard).mock.calls[0]?.[0];
expect(call?.policy).toBeUndefined();
});
});

View File

@@ -7,6 +7,7 @@ import type { SsrFPolicy } from "../../infra/net/ssrf.js";
const WEB_TOOLS_TRUSTED_NETWORK_SSRF_POLICY: SsrFPolicy = {
dangerouslyAllowPrivateNetwork: true,
allowRfc2544BenchmarkRange: true,
};
type WebToolGuardedFetchOptions = Omit<GuardedFetchOptions, "proxy"> & {

View File

@@ -955,8 +955,8 @@ describe("resolveOutboundSessionRoute", () => {
target: "12345",
threadId: "12345:99",
expected: {
sessionKey: "agent:main:telegram:direct:12345",
from: "telegram:12345",
sessionKey: "agent:main:telegram:direct:12345:thread:99",
from: "telegram:12345:topic:99",
to: "telegram:12345",
threadId: 99,
chatType: "direct",