fix(browser): CDP auth fails with percent-encoded credentials#97972
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 10:09 PM ET / 02:09 UTC. Summary PR surface: Source +10, Tests +29. Total +39 across 3 files. Reproducibility: yes. Current main constructs Basic auth from encoded URL userinfo, and a focused Node URL check confirms the reported password is read as p%40ss%20word before decoding. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the shared helper fix after ordinary maintainer review and current-head CI completion. Do we have a high-confidence way to reproduce the issue? Yes. Current main constructs Basic auth from encoded URL userinfo, and a focused Node URL check confirms the reported password is read as p%40ss%20word before decoding. Is this the best way to solve the issue? Yes. Decoding inside getHeadersWithAuth is the narrow shared fix for guarded HTTP CDP fetches, raw CDP WebSockets, and Playwright attach headers without changing config, endpoint parsing, or SSRF policy. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 54b09580f61b. Label changesLabel justifications:
Evidence reviewedPR surface: Source +10, Tests +29. Total +39 across 3 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 I have updated the PR body with the requested real behavior proof. The new Evidence section now includes an after-fix local run using a real Chrome CDP This proof is scoped to the real Chrome CDP HTTP discovery path through the shared |
|
🦞🧹 I asked ClawSweeper to review this item again. |
This PR fixes browser CDP Basic auth failures when the configured endpoint URL contains percent-encoded username or password characters.
Related: #93680 also extends
getHeadersWithAuthfor extension-bridge Bearer auth. This PR is complementary: it only decodes percent-encoded URL username/password before constructing the existing Basic Authorization header, and preserves the current precedence where explicit URL credentials win over fallback auth.What Problem This Solves
Browser CDP endpoints can be configured with URL-embedded Basic auth credentials, for example:
In URL userinfo, reserved characters must be percent-encoded. The password above represents the real credential value
p@ss word, where%40is@and%20is a space.The browser CDP helper read
URL.usernameandURL.passworddirectly when constructing theAuthorization: Basic ...header. In Node's WHATWG URL implementation, those properties remain percent-encoded when read, so OpenClaw built the Basic auth payload from the literal string:instead of the credential the user configured:
That causes remote CDP services requiring Basic auth to reject otherwise valid credentials whenever the username or password contains characters such as
@, spaces,:, or other URL-reserved characters that are commonly percent-encoded in URLs.Why This Change Was Made
Decode the URL username/password components before building the Basic auth header, while preserving the existing fallback behavior if decoding fails.
This keeps the browser CDP authentication surface narrow: guarded HTTP CDP fetches, WebSocket CDP connections, and Playwright CDP attach paths continue to share the same
getHeadersWithAuthhelper. ExplicitAuthorizationheaders still take precedence over URL credentials, and this PR does not change URL credential stripping from outbound request URLs.User Impact
Users can configure browser CDP endpoints whose Basic auth credentials contain reserved characters such as
@, spaces, or other percent-encoded characters, and OpenClaw will send the decoded credentials expected by the remote CDP service.Change
URL.usernameandURL.passwordbefore constructing the Basic auth payload.getHeadersWithAuth()behavior and the guarded CDP fetch path.Evidence
Before this fix, the credential-bearing URL:
produced a Basic auth payload for:
After this fix, the same URL produces a Basic auth payload for:
The guarded CDP fetch regression also verifies that the request URL still has credentials stripped before fetch:
while the decoded Basic auth header is sent through request headers.
Real behavior proof after this fix:
--remote-debugging-port./json/versionendpoint behind a loopback Basic-auth proxy.alice:p@ss word.http://alice:p%40ss%[email protected]:<proxy-port>/json/version.getHeadersWithAuth()output for the request headers.Observed terminal output:
This proof covers the real Chrome CDP HTTP discovery path through the shared auth helper. The credentials in the proof are synthetic, and the only endpoint is local loopback.
Possible call chain / impact
The affected helper is shared by the browser CDP connection paths:
This PR only changes how URL-embedded Basic auth credentials are converted into an auth header. It does not change CDP endpoint parsing, SSRF policy, proxy handling, explicit
Authorizationheader precedence, WebSocket connection behavior, or browser tool behavior after authentication succeeds.Testing
pnpm install --frozen-lockfilenode scripts/run-vitest.mjs extensions/browser/src/browser/cdp.helpers.test.ts extensions/browser/src/browser/browser-utils.test.tsnode scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions/browser/src/browser/browser-utils.test.ts extensions/browser/src/browser/cdp.helpers.test.ts extensions/browser/src/browser/cdp.helpers.tspnpm tsgo:extensionspnpm tsgo:extensions:testgit diff --check/json/versionbehind a loopback Basic-auth proxy, using encoded URL credentials and the current branch'sgetHeadersWithAuth()output.Limitations:
pnpm test:extension browserstarted the browser extension test lane but the Windows process exited with3221226505(0xc0000409) before reporting test assertions.codex review --base origin/maincould not run on this machine because Windows returnedAccess is deniedforcodex.exe.