fix: secure chrome extension relay cdp

This commit is contained in:
Peter Steinberger
2026-02-01 02:25:14 -08:00
parent e4f7155369
commit a1e89afcc1
6 changed files with 129 additions and 11 deletions

View File

@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it } from "vitest";
import WebSocket from "ws";
import {
ensureChromeExtensionRelayServer,
getChromeExtensionRelayAuthHeaders,
stopChromeExtensionRelayServer,
} from "./extension-relay.js";
@@ -30,6 +31,17 @@ function waitForOpen(ws: WebSocket) {
});
}
function waitForError(ws: WebSocket) {
return new Promise<Error>((resolve, reject) => {
ws.once("error", (err) => resolve(err instanceof Error ? err : new Error(String(err))));
ws.once("open", () => reject(new Error("expected websocket error")));
});
}
function relayAuthHeaders(url: string) {
return getChromeExtensionRelayAuthHeaders(url);
}
function createMessageQueue(ws: WebSocket) {
const queue: string[] = [];
let waiter: ((value: string) => void) | null = null;
@@ -137,7 +149,9 @@ describe("chrome extension relay server", () => {
cdpUrl = `http://127.0.0.1:${port}`;
await ensureChromeExtensionRelayServer({ cdpUrl });
const v1 = (await fetch(`${cdpUrl}/json/version`).then((r) => r.json())) as {
const v1 = (await fetch(`${cdpUrl}/json/version`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as {
webSocketDebuggerUrl?: string;
};
expect(v1.webSocketDebuggerUrl).toBeUndefined();
@@ -145,7 +159,9 @@ describe("chrome extension relay server", () => {
const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`);
await waitForOpen(ext);
const v2 = (await fetch(`${cdpUrl}/json/version`).then((r) => r.json())) as {
const v2 = (await fetch(`${cdpUrl}/json/version`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as {
webSocketDebuggerUrl?: string;
};
expect(String(v2.webSocketDebuggerUrl ?? "")).toContain(`/cdp`);
@@ -153,6 +169,19 @@ describe("chrome extension relay server", () => {
ext.close();
});
it("rejects CDP access without relay auth token", async () => {
const port = await getFreePort();
cdpUrl = `http://127.0.0.1:${port}`;
await ensureChromeExtensionRelayServer({ cdpUrl });
const res = await fetch(`${cdpUrl}/json/version`);
expect(res.status).toBe(401);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`);
const err = await waitForError(cdp);
expect(err.message).toContain("401");
});
it("tracks attached page targets and exposes them via CDP + /json/list", async () => {
const port = await getFreePort();
cdpUrl = `http://127.0.0.1:${port}`;
@@ -181,7 +210,9 @@ describe("chrome extension relay server", () => {
}),
);
const list = (await fetch(`${cdpUrl}/json/list`).then((r) => r.json())) as Array<{
const list = (await fetch(`${cdpUrl}/json/list`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as Array<{
id?: string;
url?: string;
title?: string;
@@ -208,7 +239,9 @@ describe("chrome extension relay server", () => {
const list2 = await waitForListMatch(
async () =>
(await fetch(`${cdpUrl}/json/list`).then((r) => r.json())) as Array<{
(await fetch(`${cdpUrl}/json/list`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as Array<{
id?: string;
url?: string;
title?: string;
@@ -226,7 +259,9 @@ describe("chrome extension relay server", () => {
),
).toBe(true);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`, {
headers: relayAuthHeaders(`ws://127.0.0.1:${port}/cdp`),
});
await waitForOpen(cdp);
const q = createMessageQueue(cdp);
@@ -271,7 +306,9 @@ describe("chrome extension relay server", () => {
const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`);
await waitForOpen(ext);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`, {
headers: relayAuthHeaders(`ws://127.0.0.1:${port}/cdp`),
});
await waitForOpen(cdp);
const q = createMessageQueue(cdp);