You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(browser): encapsulate explicit-port detection in parseBrowserHttpUrl
The previous implementation checked parsed.parsed.port in resolveProfile,
which missed WHATWG-normalized default ports (:80/:443) and was vulnerable
to false positives on IPv6 hosts and userinfo colons.
Move explicit-port detection into parseBrowserHttpUrl as hasExplicitPort
using a robust raw-string parser that handles:
- IPv6 bracket notation ([::1] vs [::1]:9222)
- userinfo (user:pass@host — colon is not a port)
- WHATWG default-port normalization (:80/:443 → empty .port)
Return normalizedWithPort alongside normalized so callers never need
raw-string URL hacks. resolveProfile now reads:
explicit URL port → use it
no URL port + cdpPort → inject cdpPort
no URL port + no cdpPort → protocol default
Consolidate scattered port-precedence tests into a focused describe block
covering all 9 cases including IPv6, default ports, stale WS, and error.
Copy file name to clipboardExpand all lines: extensions/browser/src/browser/cdp.helpers.ts
+65-1Lines changed: 65 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -15,6 +15,38 @@ import { withAllowedHostname } from "./ssrf-policy-helpers.js";
15
15
16
16
export{isLoopbackHost};
17
17
18
+
/**
19
+
* Detects whether a raw URL string contains an explicitly written port.
20
+
*
21
+
* WHATWG `URL` normalizes default ports (e.g. `:80` for http, `:443` for
22
+
* https) to an empty `.port` string, making it impossible to distinguish
23
+
* "user wrote :80" from "user omitted the port". This helper inspects the
24
+
* raw string to preserve that intent.
25
+
*
26
+
* Handles IPv6 bracket notation and userinfo (user:pass@host) correctly.
27
+
*/
28
+
functionhasRawExplicitPort(raw: string): boolean{
29
+
// Strip scheme (e.g. "http://") and take only the authority portion
30
+
// (everything before the first /, ?, or #).
31
+
constauthority=
32
+
raw
33
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"")
34
+
.split(/[/?#]/,1)[0]??"";
35
+
36
+
// Strip userinfo (user:pass@) — the colon there is NOT a port separator.
it("prefers explicit cdpPort when cdpUrl has no port",()=>{
560
-
// cdpUrl "http://127.0.0.1" (no port) should not override
// When the URL has an explicit port, always use it. When the URL omits the
506
-
// port (e.g. "http://127.0.0.1"), prefer an already-configured cdpPort over
507
-
// the protocol default (80/443) to avoid binding Chrome to a privileged
508
-
// port.
509
-
if(parsed.parsed.port){
505
+
// Port precedence: explicit URL port > configured cdpPort > protocol default.
506
+
if(parsed.hasExplicitPort){
510
507
cdpPort=parsed.port;
511
-
cdpUrl=parsed.normalized;
508
+
cdpUrl=parsed.normalizedWithPort;
512
509
}elseif(cdpPort){
513
510
// URL omitted the port but we have an explicit cdpPort — inject it while
514
511
// preserving the rest of the URL (path, query, credentials, etc.).
0 commit comments