fix(browser): normalize batch act dispatch for selector and batch support#45457
Conversation
…ayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: #44431, #38844
…input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads.
6a19174 to
4e2932c
Compare
🔒 Aisle Security AnalysisWe found 3 potential security issue(s) in this PR:
1. 🟡 Unbounded
|
| Property | Value |
|---|---|
| Severity | Medium |
| CWE | CWE-400 |
| Location | src/browser/pw-tools-core.interactions.ts:198-212 |
Description
The press action accepts a user-controlled delayMs value that is not bounded and is forwarded into Playwright's keyboard.press() delay option.
- Input source (batch):
normalizeBatchActionfor kind"press"usestoNumber(raw.delayMs)without any max bound. - Input source (single /act):
/actkind"press"also readsdelayMs = toNumber(body.delayMs)without bounds. - Sink:
pressKeyViaPlaywrightpassesdelayMsdirectly topage.keyboard.press()asdelay.
This allows an authenticated caller to send an extremely large delayMs (e.g., hours), causing the request/worker to remain busy waiting on Playwright and potentially tying up browser/session resources, leading to a denial of service.
Vulnerable code:
await page.keyboard.press(key, {
delay: Math.max(0, Math.floor(opts.delayMs ?? 0)),
});Recommendation
Enforce an upper bound for press.delayMs (similar to click/wait bounds) before calling Playwright.
Option A (recommended): reuse the existing resolveBoundedDelayMs() helper and introduce a MAX_PRESS_DELAY_MS constant.
const MAX_PRESS_DELAY_MS = 5_000;
export async function pressKeyViaPlaywright(opts: {
cdpUrl: string;
targetId?: string;
key: string;
delayMs?: number;
}): Promise<void> {
const key = String(opts.key ?? "").trim();
if (!key) throw new Error("key is required");
const delay = resolveBoundedDelayMs(opts.delayMs, "press delayMs", MAX_PRESS_DELAY_MS);
const page = await getPageForTargetId(opts);
ensurePageState(page);
await page.keyboard.press(key, { delay });
}Also apply the same max bound at the HTTP layer for batch normalization (normalizeBatchAction case "press") and the /act press handler, so invalid requests fail fast with a 400 response.
2. 🟡 Unbounded recursion in batch request normalization enables denial-of-service via stack overflow / event-loop blocking
| Property | Value |
|---|---|
| Severity | Medium |
| CWE | CWE-674 |
| Location | src/browser/routes/agent.act.ts:140-146 |
Description
The /act route's new batch support performs recursive normalization and counting of nested { kind: "batch", actions: [...] } structures without any nesting-depth limit, before rejecting oversized batches.
Key issues:
normalizeBatchAction()recursively normalizes nested batches withraw.actions.map(normalizeBatchAction).- Only after fully normalizing the nested structure, it calls
countBatchActions(actions)which is also recursive. - The
/acthandler forkind: "batch"maps allbody.actionsthroughnormalizeBatchActionbefore any top-level length check.
Although the server has an Express JSON body limit of 1mb (express.json({ limit: "1mb" })), a 1mb JSON payload can still contain very deep nesting (thousands of levels). This can trigger RangeError: Maximum call stack size exceeded or otherwise consume significant CPU synchronously, blocking the Node.js event loop during request handling.
Vulnerable code (recursive normalization + recursive counting):
case "batch": {
const actions = Array.isArray(raw.actions) ? raw.actions.map(normalizeBatchAction) : [];
// ...
if (countBatchActions(actions) > MAX_BATCH_ACTIONS) {
throw new Error(`batch exceeds maximum of ${MAX_BATCH_ACTIONS} actions`);
}
return { kind, actions, ... };
}Top-level route normalization (no early array-length cap):
actions = Array.isArray(body.actions) ? body.actions.map(normalizeBatchAction) : [];Impact:
- An authenticated client (or any client if this service is exposed via a proxy/port-forward) can submit a deeply nested batch payload causing excessive synchronous recursion, potentially throwing a stack overflow error and/or consuming CPU, reducing availability (DoS).
Recommendation
Add explicit nesting-depth and action-count limits before recursion in the route-layer normalization.
Suggested approach:
- Pass a
depthparameter through normalization and reject requests beyond a smallMAX_BATCH_DEPTH(align with Playwright layer’sMAX_BATCH_DEPTH = 5). - Enforce a cheap top-level array limit before mapping/normalizing.
- Avoid recursion for counting/validation (use an explicit stack/queue) to prevent stack overflow.
Example (depth guard + early length cap):
const MAX_BATCH_DEPTH = 5;
const MAX_BATCH_ACTIONS = 100;
function normalizeBatchAction(value: unknown, depth = 0): BrowserActRequest {
if (depth > MAX_BATCH_DEPTH) {
throw new Error(`batch depth exceeds maximum of ${MAX_BATCH_DEPTH}`);
}
// ... same validation as today ...
if (kind === "batch") {
const rawActions = Array.isArray(raw.actions) ? raw.actions : [];
if (rawActions.length > MAX_BATCH_ACTIONS) {
throw new Error(`batch exceeds maximum of ${MAX_BATCH_ACTIONS} actions`);
}
const actions = rawActions.map((a) => normalizeBatchAction(a, depth + 1));
return { kind: "batch", actions };
}
// ...
}
// In route handler (before mapping):
const rawActions = Array.isArray(body.actions) ? body.actions : [];
if (rawActions.length > MAX_BATCH_ACTIONS) {
return jsonError(res, 400, `batch exceeds maximum of ${MAX_BATCH_ACTIONS} actions`);
}
const actions = rawActions.map((a) => normalizeBatchAction(a, 0));Also consider keeping the existing express.json({ limit: "1mb" }) (already present) and optionally lowering it for this endpoint if appropriate.
3. 🔵 Untrusted selector/ref reflected into API error messages (log/terminal injection risk)
| Property | Value |
|---|---|
| Severity | Low |
| CWE | CWE-117 |
| Location | src/browser/pw-tools-core.interactions.ts:882-888 |
Description
User-controlled selector (and other ref-like strings) are incorporated verbatim into generated error messages via toAIFriendlyError(...) and then returned to callers (including batched responses).
This creates an output-injection surface:
- Input:
selectorcomes from the/actrequest body (or nestedbatchaction objects) and is onlytrim()’d (requireRefOrSelector). - Propagation: the value is used as
label = resolved.ref ?? resolved.selector!and passed intotoAIFriendlyError(err, label). - Sink:
toAIFriendlyErrorembeds the label inside quoted strings (e.g.Selector "${selector}" ...), and in batch mode the resultingerr.messageis returned to the caller as JSON ({ ok:false, error: message }).
If an attacker can influence selectors (directly via API calls, or indirectly if an agent derives selectors from untrusted page content/attributes), they can inject control characters (e.g. \n, \r) or terminal escape sequences into returned error strings. While JSON encoding prevents breaking JSON structure, this can still:
- forge/mislead logs in downstream consumers that log these messages line-by-line,
- cause terminal escape injection in CLIs that print error strings,
- create prompt-injection-like contamination when these errors are fed back into an LLM context.
Vulnerable code (batch sink):
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
results.push({ ok: false, error: message });
if (opts.stopOnError !== false) {
break;
}
}Recommendation
Sanitize/normalize selector/ref strings before embedding them into error messages and before returning them in batch results.
Suggested pattern (single-line + length bound + strip ANSI escapes):
function sanitizeLabel(label: string, maxLen = 200): string {
return label
// strip ANSI escape sequences
.replace(/\u001b\[[0-9;]*m/g, "")
// collapse control whitespace that can forge logs
.replace(/[\r\n\t]/g, " ")
.slice(0, maxLen);
}Apply it at the boundary:
- In
toAIFriendlyError(error, selector)usesanitizeLabel(selector)before interpolating. - In
batchViaPlaywright, return a normalized message, or better return structured errors{ code, message }wheremessageis sanitized.
Example:
} catch (err) {
const raw = err instanceof Error ? err.message : String(err);
results.push({ ok: false, error: sanitizeLabel(raw, 500) });
}Analyzed PR: #45457 at commit 36d3e27
Last updated on: 2026-03-13T22:43:50Z
Greptile SummaryThis PR normalizes batch action payloads before dispatch in the browser Key issues found:
Confidence Score: 3/5
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cd8e8376a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36d3e27600
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await batchViaPlaywright({ | ||
| cdpUrl, | ||
| targetId: effectiveTargetId, | ||
| actions: action.actions, | ||
| stopOnError: action.stopOnError, |
There was a problem hiding this comment.
Propagate nested batch failures to the parent batch result
Handle nested batch outcomes explicitly here: this branch awaits batchViaPlaywright(...) but discards its returned results, while batchViaPlaywright internally catches action errors and reports them as { ok: false } instead of throwing. That means a nested batch with failing inner actions is recorded as { ok: true } by the outer loop, and stopOnError on the parent batch will not stop when nested actions fail, so callers can receive a successful top-level result even though part of the batch did not execute successfully.
Useful? React with 👍 / 👎.
| const result = await pw.batchViaPlaywright({ | ||
| cdpUrl, | ||
| targetId: tab.targetId, | ||
| actions, | ||
| stopOnError, |
There was a problem hiding this comment.
Validate batch size before dispatching to Playwright
Add a route-level size check before dispatch: /act currently forwards any non-empty actions array, but batchViaPlaywright throws when actions.length > 100. That exception is a plain Error, so withRouteTabContext maps it to a 500 response, turning client input validation into an internal server error. A request with 101 actions should be rejected as a 4xx validation error instead of surfacing as a server fault.
Useful? React with 👍 / 👎.
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]> (cherry picked from commit f59b2b1)
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
…port (openclaw#45457) * feat(browser): add batch actions, CSS selector support, and click delayMs Adds three improvements to the browser act tool: 1. CSS selector support: All element-targeting actions (click, type, hover, drag, scrollIntoView, select) now accept an optional 'selector' parameter alongside 'ref'. When selector is provided, Playwright's page.locator() is used directly, skipping the need for a snapshot to obtain refs. This reduces roundtrips for agents that already know the DOM structure. 2. Click delay (delayMs): The click action now accepts an optional 'delayMs' parameter. When set, the element is hovered first, then after the specified delay, clicked. This enables human-like hover-before-click in a single tool call instead of three (hover + wait + click). 3. Batch actions: New 'batch' action kind that accepts an array of actions to execute sequentially in a single tool call. Supports 'stopOnError' (default true) to control whether execution halts on first failure. Results are returned as an array. This eliminates the AI inference roundtrip between each action, dramatically reducing latency and token cost for multi-step flows. Addresses: openclaw#44431, openclaw#38844 * fix(browser): address security review — batch evaluateEnabled guard, input validation, recursion limit Fixes all 4 issues raised by Greptile review: 1. Security: batch actions now respect evaluateEnabled flag. executeSingleAction and batchViaPlaywright accept evaluateEnabled param. evaluate and wait-with-fn inside batches are rejected when evaluateEnabled=false, matching the direct route guards. 2. Security: batch input validation. Each action in body.actions is validated as a plain object with a known kind string before dispatch. Applies same normalization as direct action handlers. 3. Perf: SELECTOR_ALLOWED_KINDS moved to module scope as a ReadonlySet<string> constant (was re-created on every request). 4. Security: max batch nesting depth of 5. Nested batch actions track depth and throw if MAX_BATCH_DEPTH exceeded, preventing call stack exhaustion from crafted payloads. * fix(browser): normalize batch act dispatch * fix(browser): tighten existing-session act typing * fix(browser): preserve batch type text * fix(browser): complete batch action execution * test(browser): cover batch route normalization * test(browser): cover batch interaction dispatch * fix(browser): bound batch route action inputs * fix(browser): harden batch interaction limits * test(browser): cover batch security guardrails --------- Co-authored-by: Diwakar <[email protected]>
Summary
actbatch flow still forwarded raw JSON actions, dropped the route-levelevaluateEnabledsetting, and exposedresultsat runtime without typing it on the client response.evaluate/wait --fninside batches would not receive the configured gate consistently, and typedbrowserAct()callers could not access batch results.src/browser/routes/agent.act.tsbefore dispatchevaluateEnabledthrough topw.batchViaPlaywrightresultsfield toBrowserActResponseTASK.mdfile from the cherry-picked feature branch and add an Unreleased changelog entryevaluateEnabledroute behavior outside the batch path.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
actbatches now use the same normalization rules as directactrequests for supported fields.evaluate/wait --fnrequests now receive the route'sevaluateEnabledsetting.resultswithout casting.Security Impact (required)
No)No)No)No)No)Repro + Verification
Environment
Steps
pnpm test -- src/browser/client.test.tspnpm test -- src/browser/server.agent-contract-form-layout-act-commands.test.ts -t "normalizes batch actions and threads evaluateEnabled into the batch executor"pnpm test -- src/browser/server.agent-contract-form-layout-act-commands.test.ts -t "rejects malformed batch actions before dispatch"Expected
evaluateEnabledis passed into the batch executorresultsActual
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
resultsevaluateEnabledroute behavior outside the batch pathReview Conversations
Compatibility / Migration
Yes)No)No)Failure Recovery (if this breaks)
6a191745c8src/browser/routes/agent.act.ts,src/browser/client-actions-core.ts, browser test files,CHANGELOG.mdresultsRisks and Mitigations