Skip to content

Commit 9233351

Browse files
committed
Config: plumb opt-in SSRF policy
1 parent f6de4cd commit 9233351

37 files changed

Lines changed: 1373 additions & 253 deletions

docs/.generated/config-baseline.json

Lines changed: 747 additions & 138 deletions
Large diffs are not rendered by default.

docs/.generated/config-baseline.jsonl

Lines changed: 87 additions & 59 deletions
Large diffs are not rendered by default.

docs/.generated/plugin-sdk-api-baseline.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@
10151015
"exportName": "BlockStreamingCoalesceSchema",
10161016
"kind": "const",
10171017
"source": {
1018-
"line": 339,
1018+
"line": 350,
10191019
"path": "src/config/zod-schema.core.ts"
10201020
}
10211021
},
@@ -1033,7 +1033,7 @@
10331033
"exportName": "DmConfigSchema",
10341034
"kind": "const",
10351035
"source": {
1036-
"line": 293,
1036+
"line": 304,
10371037
"path": "src/config/zod-schema.core.ts"
10381038
}
10391039
},
@@ -1042,7 +1042,7 @@
10421042
"exportName": "DmPolicySchema",
10431043
"kind": "const",
10441044
"source": {
1045-
"line": 337,
1045+
"line": 348,
10461046
"path": "src/config/zod-schema.core.ts"
10471047
}
10481048
},
@@ -1060,7 +1060,7 @@
10601060
"exportName": "GroupPolicySchema",
10611061
"kind": "const",
10621062
"source": {
1063-
"line": 335,
1063+
"line": 346,
10641064
"path": "src/config/zod-schema.core.ts"
10651065
}
10661066
},
@@ -1078,7 +1078,7 @@
10781078
"exportName": "MarkdownConfigSchema",
10791079
"kind": "const",
10801080
"source": {
1081-
"line": 371,
1081+
"line": 382,
10821082
"path": "src/config/zod-schema.core.ts"
10831083
}
10841084
},
@@ -1096,7 +1096,7 @@
10961096
"exportName": "ReplyRuntimeConfigSchemaShape",
10971097
"kind": "const",
10981098
"source": {
1099-
"line": 347,
1099+
"line": 358,
11001100
"path": "src/config/zod-schema.core.ts"
11011101
}
11021102
},
@@ -1105,7 +1105,7 @@
11051105
"exportName": "requireOpenAllowFrom",
11061106
"kind": "const",
11071107
"source": {
1108-
"line": 486,
1108+
"line": 497,
11091109
"path": "src/config/zod-schema.core.ts"
11101110
}
11111111
},
@@ -1141,7 +1141,7 @@
11411141
"exportName": "ToolPolicySchema",
11421142
"kind": "const",
11431143
"source": {
1144-
"line": 253,
1144+
"line": 254,
11451145
"path": "src/config/zod-schema.agent-runtime.ts"
11461146
}
11471147
},

docs/.generated/plugin-sdk-api-baseline.jsonl

Lines changed: 8 additions & 8 deletions
Large diffs are not rendered by default.

extensions/browser/src/browser/config.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,14 @@ describe("browser config", () => {
233233
const resolved = resolveBrowserConfig({
234234
ssrfPolicy: {
235235
allowPrivateNetwork: true,
236+
allowRfc2544BenchmarkRange: true,
236237
allowedHostnames: [" localhost ", ""],
237238
hostnameAllowlist: [" *.trusted.example ", " "],
238239
},
239240
});
240241
expect(resolved.ssrfPolicy).toEqual({
241242
dangerouslyAllowPrivateNetwork: true,
243+
allowRfc2544BenchmarkRange: true,
242244
allowedHostnames: ["localhost"],
243245
hostnameAllowlist: ["*.trusted.example"],
244246
});
@@ -260,6 +262,18 @@ describe("browser config", () => {
260262
expect(resolved.ssrfPolicy).toEqual({});
261263
});
262264

265+
it("preserves allowRfc2544BenchmarkRange even in strict browser mode", () => {
266+
const resolved = resolveBrowserConfig({
267+
ssrfPolicy: {
268+
dangerouslyAllowPrivateNetwork: false,
269+
allowRfc2544BenchmarkRange: true,
270+
},
271+
});
272+
expect(resolved.ssrfPolicy).toEqual({
273+
allowRfc2544BenchmarkRange: true,
274+
});
275+
});
276+
263277
it("resolves existing-session profiles without cdpPort or cdpUrl", () => {
264278
const resolved = resolveBrowserConfig({
265279
profiles: {

extensions/browser/src/browser/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ function normalizeStringList(raw: string[] | undefined): string[] | undefined {
102102
function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy | undefined {
103103
const allowPrivateNetwork = cfg?.ssrfPolicy?.allowPrivateNetwork;
104104
const dangerouslyAllowPrivateNetwork = cfg?.ssrfPolicy?.dangerouslyAllowPrivateNetwork;
105+
const allowRfc2544BenchmarkRange = cfg?.ssrfPolicy?.allowRfc2544BenchmarkRange;
105106
const allowedHostnames = normalizeStringList(cfg?.ssrfPolicy?.allowedHostnames);
106107
const hostnameAllowlist = normalizeStringList(cfg?.ssrfPolicy?.hostnameAllowlist);
107108
const hasExplicitPrivateSetting =
@@ -115,6 +116,7 @@ function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy |
115116
if (
116117
!resolvedAllowPrivateNetwork &&
117118
!hasExplicitPrivateSetting &&
119+
allowRfc2544BenchmarkRange !== true &&
118120
!allowedHostnames &&
119121
!hostnameAllowlist
120122
) {
@@ -123,6 +125,7 @@ function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy |
123125

124126
return {
125127
...(resolvedAllowPrivateNetwork ? { dangerouslyAllowPrivateNetwork: true } : {}),
128+
...(allowRfc2544BenchmarkRange === true ? { allowRfc2544BenchmarkRange: true } : {}),
126129
...(allowedHostnames ? { allowedHostnames } : {}),
127130
...(hostnameAllowlist ? { hostnameAllowlist } : {}),
128131
};

extensions/google/src/gemini-web-search-provider.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Type } from "@sinclair/typebox";
2+
import type { SsrFPolicy } from "openclaw/plugin-sdk/infra-runtime";
23
import { DEFAULT_GOOGLE_API_BASE_URL } from "openclaw/plugin-sdk/provider-google";
34
import {
45
buildSearchCacheKey,
@@ -82,6 +83,7 @@ async function runGeminiSearch(params: {
8283
apiKey: string;
8384
model: string;
8485
timeoutSeconds: number;
86+
citationRedirectSsrFPolicy?: SsrFPolicy;
8587
}): Promise<{ content: string; citations: Array<{ url: string; title?: string }> }> {
8688
const endpoint = `${GEMINI_API_BASE}/models/${params.model}:generateContent`;
8789

@@ -144,7 +146,9 @@ async function runGeminiSearch(params: {
144146
const resolved = await Promise.all(
145147
batch.map(async (citation) => ({
146148
...citation,
147-
url: await resolveCitationRedirectUrl(citation.url),
149+
url: await resolveCitationRedirectUrl(citation.url, {
150+
ssrfPolicy: params.citationRedirectSsrFPolicy,
151+
}),
148152
})),
149153
);
150154
citations.push(...resolved);
@@ -221,6 +225,7 @@ function createGeminiToolDefinition(
221225
apiKey,
222226
model,
223227
timeoutSeconds: resolveSearchTimeoutSeconds(searchConfig),
228+
citationRedirectSsrFPolicy: searchConfig?.citationRedirect?.ssrfPolicy,
224229
});
225230
const payload = {
226231
query,

extensions/mattermost/src/mattermost/send.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ export async function sendMessageMattermost(
401401
try {
402402
const media = await loadOutboundMediaFromUrl(mediaUrl, {
403403
mediaLocalRoots: opts.mediaLocalRoots,
404+
ssrfPolicy: cfg.messages?.remoteMedia?.ssrfPolicy,
404405
});
405406
const fileInfo = await uploadMattermostFile(client, {
406407
channelId,

extensions/msteams/src/send.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export async function sendMessageMSTeams(
127127
const media = await loadOutboundMediaFromUrl(mediaUrl, {
128128
maxBytes: mediaMaxBytes,
129129
mediaLocalRoots,
130+
ssrfPolicy: cfg.messages?.remoteMedia?.ssrfPolicy,
130131
});
131132
const isLargeFile = media.buffer.length >= FILE_CONSENT_THRESHOLD_BYTES;
132133
const isImage = media.contentType?.startsWith("image/") ?? false;

extensions/signal/src/send.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export async function sendMessageSignal(
129129
if (opts.mediaUrl?.trim()) {
130130
const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, {
131131
localRoots: opts.mediaLocalRoots,
132+
ssrfPolicy: cfg.messages?.remoteMedia?.ssrfPolicy,
132133
});
133134
attachments = [resolved.path];
134135
const kind = kindFromMime(resolved.contentType ?? undefined);

0 commit comments

Comments
 (0)