fix(cdp): preserve explicitly selected default ports

This commit is contained in:
morluto committed 2026-10-24 02:41:17 +08:00
1 parent 6eb9d09268
commit e0da397dee
2 files changed
+53 -13

No files matched your search

+26
View File
@@ -11,6 +11,32 @@ import {
} from "./browserObservation.js";
describe("browser observation contracts", () => {
it.each(["127.0.0.1", "[::1]"])(
"preserves explicitly supplied default endpoint ports on %s",
(host) => {
const http = `http://${host}:80`;
expect(browserEndpointSchema.parse(http)).toBe(http);
expect(
browserEndpointSchema.parse(browserEndpointSchema.parse(http)),
).toBe(http);
expect(recordedBrowserObservationEndpointSchema.parse(http)).toBe(http);
const socket = `ws://${host}:80/devtools/browser/id`;
expect(browserObservationEndpointSchema.parse(socket)).toEqual({
transport: "browser_websocket",
url: socket,
recorded: `ws://${host}:80/devtools/browser/[REDACTED]`,
});
for (const value of [
`http://${host}`,
`ws://${host}/devtools/browser/id`,
"http://192.0.2.1:80",
])
expect(
browserObservationEndpointSchema.safeParse(value).success,
value,
).toBe(false);
},
);
it("normalizes exact HTTP origins without accepting broader URL scopes", () => {
expect(browserOriginSchema.parse("https://Example.COM:443")).toBe(
"https://example.com",
+27 -13
View File
@@ -65,31 +65,45 @@ const HTTP_ENDPOINT_MESSAGE =
const BROWSER_SOCKET_PATH = "/devtools/browser/";
const REDACTED_BROWSER_SOCKET_PATH = `${BROWSER_SOCKET_PATH}[REDACTED]`;
/** Normalized origin of an accepted loopback CDP HTTP discovery endpoint. */
const httpDiscoveryOrigin = (url: URL): string | undefined =>
/** Retain an explicit default port, which URL.port and URL.origin erase. */
const explicitEndpointAuthority = (
value: string,
url: URL,
): string | undefined => {
const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#\\]*)/iu.exec(value)?.[1];
const port =
authority === undefined ? undefined : /:([0-9]+)$/u.exec(authority)?.[1];
return port === undefined
? undefined
: `${url.protocol}//${url.hostname}:${String(Number(port))}`;
};
/** Normalized explicit-port origin of a loopback CDP HTTP discovery endpoint. */
const httpDiscoveryOrigin = (value: string, url: URL): string | undefined =>
url.protocol === "http:" &&
isLiteralLoopbackHostname(url.hostname) &&
url.port !== "" &&
url.username === "" &&
url.password === "" &&
url.pathname === "/" &&
!urlDeclaresQueryOrFragment(url)
? url.origin
? explicitEndpointAuthority(value, url)
: undefined;
/** Scheme and authority of an accepted loopback CDP browser WebSocket. */
const browserSocketAuthority = (url: URL): string | undefined => {
const browserSocketAuthority = (
value: string,
url: URL,
): string | undefined => {
const id = url.pathname.slice(BROWSER_SOCKET_PATH.length);
return url.protocol === "ws:" &&
isLiteralLoopbackHostname(url.hostname) &&
url.port !== "" &&
url.username === "" &&
url.password === "" &&
!urlDeclaresQueryOrFragment(url) &&
url.pathname.startsWith(BROWSER_SOCKET_PATH) &&
id.length > 0 &&
!id.includes("/")
? `${url.protocol}//${url.host}`
? explicitEndpointAuthority(value, url)
: undefined;
};
@@ -99,7 +113,7 @@ export const browserEndpointSchema = z
.min(1)
.transform((value, context) => {
const url = URL.parse(value);
const origin = url === null ? undefined : httpDiscoveryOrigin(url);
const origin = url === null ? undefined : httpDiscoveryOrigin(value, url);
if (origin !== undefined) return origin;
context.addIssue({
code: "custom",
@@ -132,14 +146,14 @@ export const browserObservationEndpointSchema = z
context.addIssue({ code: "custom", message: "Invalid CDP endpoint URL" });
return z.NEVER;
}
const origin = httpDiscoveryOrigin(url);
const origin = httpDiscoveryOrigin(value, url);
if (origin !== undefined)
return { transport: "http_discovery", url: origin, recorded: origin };
const authority = browserSocketAuthority(url);
const authority = browserSocketAuthority(value, url);
if (authority !== undefined)
return {
transport: "browser_websocket",
url: url.href,
url: `${authority}${url.pathname}`,
recorded: `${authority}${REDACTED_BROWSER_SOCKET_PATH}`,
};
context.addIssue({
@@ -161,8 +175,8 @@ export const recordedBrowserObservationEndpointSchema = z
const url = URL.parse(value);
return (
url !== null &&
(httpDiscoveryOrigin(url) !== undefined ||
(browserSocketAuthority(url) !== undefined &&
(httpDiscoveryOrigin(value, url) !== undefined ||
(browserSocketAuthority(value, url) !== undefined &&
url.pathname === REDACTED_BROWSER_SOCKET_PATH))
);
},