fix(browser): reject non-page json new targets#104129
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 11, 2026, 3:41 PM ET / 19:41 UTC. Summary PR surface: Source +3, Tests +27. Total +30 across 2 files. Reproducibility: yes. at source level. Current main adopts any raw Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the shared-predicate check with its regression, preserving the current navigation and endpoint validations; maintainers should explicitly accept the contract-aligned rejection of nonconforming raw CDP target descriptors. Do we have a high-confidence way to reproduce the issue? Yes at source level. Current main adopts any raw Is this the best way to solve the issue? Yes. Reusing the existing shared selectable-target predicate at the tab-ops owner boundary is narrower and less drift-prone than adding another filter, and it preserves the existing SSRF/navigation and CDP endpoint checks. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 589f1c00de32. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +3, Tests +27. Total +30 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review Added real behavior proof for the Focused validation:
Real/controlled proof:
{
"head": "0652ddcfac0586ff444b8d12818c620f3e7189f1",
"chromeBin": "/usr/bin/google-chrome",
"rawChromeJsonNew": {
"ok": true,
"type": "page",
"url": "about:blank",
"webSocketDebuggerUrlPresent": true
},
"createTargetProbe": {
"ok": true
},
"fallbackPage": {
"targetId": "PAGE",
"type": "page",
"url": "about:blank",
"lastTargetId": "PAGE"
},
"rejectNonPage": {
"ok": true,
"message": "Failed to open tab (non-selectable target)",
"lastBefore": "before-target",
"lastAfter": "before-target"
}
} |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
…p-json-new-page-target-20260711 # Conflicts: # extensions/browser/src/browser/server-context.tab-ops.ts
|
Maintainer verification complete at head I kept the contributor's shared target-predicate fix and tightened the adoption boundary: a raw-created target now becomes Validation:
Proof boundary: compliant Chrome returns a page descriptor from |
|
Merged via squash.
|
* fix(browser): reject non-page json new targets * fix(browser): adopt only validated raw CDP targets --------- Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
Remote raw CDP tab listing filters
/json/listresults to selectable page targets, but the raw/json/newfallback accepted the target returned by Chrome without applying that same target contract. If Chrome returned a worker, browser-internal, or otherwise non-selectable target, OpenClaw could store it as the latest tab and continue as though a user page was opened.Why This Change Was Made
The fallback path should uphold the same page-target hygiene as the list path. This change applies the existing
isSelectableCdpBrowserTargethelper to the/json/newresponse before adopting the target id.User Impact
Valid page targets continue to open normally. Non-page or browser-internal targets returned by raw CDP tab creation now fail closed instead of becoming the selected browser tab.
Evidence
Target.createTargetis unavailable and/json/newreturns aworkertarget.openTabrejects the target and does not store it as the profile'slastTargetId.0652ddcfac0586ff444b8d12818c620f3e7189f1:/usr/bin/google-chromewith remote debugging returned a normal/json/newtarget withtype: "page",url: "about:blank", and awebSocketDebuggerUrl.createProfileTabOps.openTabaccepted a page-shaped/json/newresponse and rejected a worker-shaped/json/newresponse without changinglastTargetId.Proof
Base:
upstream/mainatffc8051cacb50d1f4b39cc2c6f822083caba1f2a.node scripts/run-vitest.mjs extensions/browser/src/browser/server-context.remote-profile-tab-ops.fallback.test.ts -- --reporter=verbose- 1 file / 13 tests passed.git diff --check upstream/main...HEAD -- extensions/browser/src/browser/server-context.tab-ops.ts extensions/browser/src/browser/server-context.remote-profile-tab-ops.fallback.test.ts{ "head": "0652ddcfac0586ff444b8d12818c620f3e7189f1", "chromeBin": "/usr/bin/google-chrome", "rawChromeJsonNew": { "ok": true, "type": "page", "url": "about:blank", "webSocketDebuggerUrlPresent": true }, "createTargetProbe": { "ok": true }, "fallbackPage": { "targetId": "PAGE", "type": "page", "url": "about:blank", "lastTargetId": "PAGE" }, "rejectNonPage": { "ok": true, "message": "Failed to open tab (non-selectable target)", "lastBefore": "before-target", "lastAfter": "before-target" } }