mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-02 09:34:56 +00:00
fix: validate browser snapshot numbers
This commit is contained in:
@@ -159,6 +159,26 @@ describe("browser cli snapshot defaults", () => {
|
||||
expect(params?.query?.urls).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects non-integer snapshot numeric options before dispatch", async () => {
|
||||
await expect(runSnapshot(["--limit", "1e3"])).rejects.toThrow("__exit__:1");
|
||||
expect(runtime.error.mock.calls.at(-1)?.[0]).toContain(
|
||||
"Invalid --limit: must be an integer >= 1",
|
||||
);
|
||||
|
||||
resetRuntimeCapture();
|
||||
await expect(runSnapshot(["--depth", "-1"])).rejects.toThrow("__exit__:1");
|
||||
expect(runtime.error.mock.calls.at(-1)?.[0]).toContain(
|
||||
"Invalid --depth: must be an integer >= 0",
|
||||
);
|
||||
|
||||
expect(sharedMocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("passes zero snapshot depth because root depth is valid", async () => {
|
||||
const params = await runSnapshot(["--depth", "0"]);
|
||||
expect(params?.query?.depth).toBe(0);
|
||||
});
|
||||
|
||||
it("sends screenshot request with trimmed target id and jpeg type", async () => {
|
||||
const params = await runBrowserInspect(["screenshot", " tab-1 ", "--type", "jpeg"], true);
|
||||
expect(params?.path).toBe("/screenshot");
|
||||
|
||||
@@ -10,6 +10,24 @@ import {
|
||||
type SnapshotResult,
|
||||
} from "./core-api.js";
|
||||
|
||||
function parseOptionalIntegerOption(
|
||||
value: string | undefined,
|
||||
label: string,
|
||||
opts: { min: number },
|
||||
): number | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const raw = value.trim();
|
||||
const parsed = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
|
||||
if (!Number.isSafeInteger(parsed) || parsed < opts.min) {
|
||||
defaultRuntime.error(danger(`Invalid ${label}: must be an integer >= ${opts.min}`));
|
||||
defaultRuntime.exit(1);
|
||||
return undefined;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function registerBrowserInspectCommands(
|
||||
browser: Command,
|
||||
parentOpts: (cmd: Command) => BrowserParentOpts,
|
||||
@@ -60,12 +78,12 @@ export function registerBrowserInspectCommands(
|
||||
.description("Capture a snapshot (default: ai; aria is the accessibility tree)")
|
||||
.option("--format <aria|ai>", "Snapshot format (default: ai)", "ai")
|
||||
.option("--target-id <id>", "CDP target id (or unique prefix)")
|
||||
.option("--limit <n>", "Max nodes (default: 500/800)", (v: string) => Number(v))
|
||||
.option("--limit <n>", "Max nodes (default: 500/800)")
|
||||
.option("--mode <efficient>", "Snapshot preset (efficient)")
|
||||
.option("--efficient", "Use the efficient snapshot preset", false)
|
||||
.option("--interactive", "Role snapshot: interactive elements only", false)
|
||||
.option("--compact", "Role snapshot: compact output", false)
|
||||
.option("--depth <n>", "Role snapshot: max depth", (v: string) => Number(v))
|
||||
.option("--depth <n>", "Role snapshot: max depth")
|
||||
.option("--selector <sel>", "Role snapshot: scope to CSS selector")
|
||||
.option("--frame <sel>", "Role snapshot: scope to an iframe selector")
|
||||
.option("--labels", "Include viewport label overlay screenshot", false)
|
||||
@@ -85,14 +103,22 @@ export function registerBrowserInspectCommands(
|
||||
? "efficient"
|
||||
: undefined;
|
||||
const mode = opts.efficient === true || opts.mode === "efficient" ? "efficient" : configMode;
|
||||
const limit = parseOptionalIntegerOption(opts.limit, "--limit", { min: 1 });
|
||||
const depth = parseOptionalIntegerOption(opts.depth, "--depth", { min: 0 });
|
||||
if (
|
||||
(opts.limit !== undefined && limit === undefined) ||
|
||||
(opts.depth !== undefined && depth === undefined)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const query: Record<string, string | number | boolean | undefined> = {
|
||||
format,
|
||||
targetId: normalizeOptionalString(opts.targetId),
|
||||
limit: Number.isFinite(opts.limit) ? opts.limit : undefined,
|
||||
limit,
|
||||
interactive: opts.interactive ? true : undefined,
|
||||
compact: opts.compact ? true : undefined,
|
||||
depth: Number.isFinite(opts.depth) ? opts.depth : undefined,
|
||||
depth,
|
||||
selector: normalizeOptionalString(opts.selector),
|
||||
frame: normalizeOptionalString(opts.frame),
|
||||
labels: opts.labels ? true : undefined,
|
||||
|
||||
Reference in New Issue
Block a user