Skip to content

Commit 854cb92

Browse files
committed
fix(browser): validate response body numeric options
1 parent fce7470 commit 854cb92

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

extensions/browser/src/browser/routes/agent.act.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
12
import { formatErrorMessage } from "../../infra/errors.js";
23
import {
34
clickChromeMcpElement,
@@ -41,7 +42,7 @@ import {
4142
import { resolveTargetIdAfterNavigate } from "./agent.snapshot-target.js";
4243
import { EXISTING_SESSION_LIMITS } from "./existing-session-limits.js";
4344
import type { BrowserRouteRegistrar } from "./types.js";
44-
import { asyncBrowserRoute, jsonError, toNumber, toStringOrEmpty } from "./utils.js";
45+
import { asyncBrowserRoute, jsonError, toStringOrEmpty } from "./utils.js";
4546

4647
function sleep(ms: number): Promise<void> {
4748
return new Promise((resolve) => setTimeout(resolve, ms));
@@ -347,6 +348,14 @@ function getExistingSessionUnsupportedMessage(action: BrowserActRequest): string
347348
throw new Error("Unsupported browser act kind");
348349
}
349350

351+
function readRoutePositiveInteger(value: unknown, fieldName: string): number | undefined {
352+
const parsed = parseStrictPositiveInteger(value);
353+
if (parsed === undefined && value != null) {
354+
throw new Error(`${fieldName} must be a positive integer.`);
355+
}
356+
return parsed;
357+
}
358+
350359
export function registerBrowserAgentActRoutes(
351360
app: BrowserRouteRegistrar,
352361
ctx: BrowserRouteContext,
@@ -697,8 +706,14 @@ export function registerBrowserAgentActRoutes(
697706
const body = readBody(req);
698707
const targetId = resolveTargetIdFromBody(body);
699708
const url = toStringOrEmpty(body.url);
700-
const timeoutMs = toNumber(body.timeoutMs);
701-
const maxChars = toNumber(body.maxChars);
709+
let timeoutMs: number | undefined;
710+
let maxChars: number | undefined;
711+
try {
712+
timeoutMs = readRoutePositiveInteger(body.timeoutMs, "timeoutMs");
713+
maxChars = readRoutePositiveInteger(body.maxChars, "maxChars");
714+
} catch (err) {
715+
return jsonError(res, 400, formatErrorMessage(err));
716+
}
702717
if (!url) {
703718
return jsonError(res, 400, "url is required");
704719
}

extensions/browser/src/browser/server.agent-contract-form-layout-act-commands.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,25 @@ describe("browser control server", () => {
453453
slowTimeoutMs,
454454
);
455455

456+
it("rejects loose response body numeric options before dispatch", async () => {
457+
const base = await startServerAndBase();
458+
const beforeCalls = pwMocks.responseBodyViaPlaywright.mock.calls.length;
459+
460+
const timeoutRes = await postJson<{ error?: string }>(`${base}/response/body`, {
461+
url: "**/api/data",
462+
timeoutMs: "1e3",
463+
});
464+
expect(timeoutRes.error).toContain("timeoutMs must be a positive integer.");
465+
466+
const maxCharsRes = await postJson<{ error?: string }>(`${base}/response/body`, {
467+
url: "**/api/data",
468+
maxChars: "0x10",
469+
});
470+
expect(maxCharsRes.error).toContain("maxChars must be a positive integer.");
471+
472+
expect(pwMocks.responseBodyViaPlaywright).toHaveBeenCalledTimes(beforeCalls);
473+
});
474+
456475
it("agent contract: hooks + response + downloads + screenshot", async () => {
457476
const base = await startServerAndBase();
458477

0 commit comments

Comments
 (0)