fix(cdp): preserve explicitly selected default ports
This commit is contained in:
1 parent
6eb9d09268
commit
e0da397dee
2 files changed
+53
-13
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user