test: enforce browser cdp policy before playwright

This commit is contained in:
Peter Steinberger
2026-04-10 17:21:01 +01:00
parent 0dd8ce72a2
commit fa2ee2af85
4 changed files with 16 additions and 12 deletions

View File

@@ -6,6 +6,7 @@ import { rawDataToString } from "../infra/ws.js";
import { isWebSocketUrl } from "./cdp.helpers.js";
import { createTargetViaCdp, evaluateJavaScript, normalizeCdpWsUrl, snapshotAria } from "./cdp.js";
import { parseHttpUrl } from "./config.js";
import { BrowserCdpEndpointBlockedError } from "./errors.js";
import { InvalidBrowserNavigationUrlError } from "./navigation-guard.js";
describe("cdp", () => {
@@ -241,7 +242,7 @@ describe("cdp", () => {
allowedHostnames: ["127.0.0.1"],
},
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
});
it("blocks the initial /json/version fetch when the cdpUrl host is outside strict SSRF policy", async () => {
@@ -254,7 +255,7 @@ describe("cdp", () => {
allowedHostnames: ["127.0.0.1"],
},
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
});
it("blocks direct websocket cdp urls outside strict SSRF policy", async () => {
@@ -267,7 +268,7 @@ describe("cdp", () => {
allowedHostnames: ["127.0.0.1"],
},
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
});
it("evaluates javascript via CDP", async () => {

View File

@@ -6,7 +6,6 @@ import os from "node:os";
import path from "node:path";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { WebSocketServer } from "ws";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
import {
decorateOpenClawProfile,
ensureProfileCleanExit,
@@ -22,6 +21,7 @@ import {
DEFAULT_OPENCLAW_BROWSER_COLOR,
DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
} from "./constants.js";
import { BrowserCdpEndpointBlockedError } from "./errors.js";
type StopChromeTarget = Parameters<typeof stopOpenClawChrome>[0];
@@ -357,7 +357,7 @@ describe("browser chrome helpers", () => {
dangerouslyAllowPrivateNetwork: false,
allowedHostnames: ["127.0.0.1"],
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
} finally {
await new Promise<void>((resolve) => server.close(() => resolve()));
}

View File

@@ -1,5 +1,4 @@
import { describe, expect, it, vi } from "vitest";
import { BrowserCdpEndpointBlockedError } from "./errors.js";
import {
installRemoteProfileTestLifecycle,
loadRemoteProfileTestDeps,
@@ -173,9 +172,15 @@ describe("browser remote profile tab ops via Playwright", () => {
const ctx = deps.createBrowserRouteContext({ getState: () => state });
const remote = ctx.forProfile("remote");
await expect(remote.listTabs()).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
await expect(remote.focusTab("T1")).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
await expect(remote.closeTab("T1")).rejects.toBeInstanceOf(BrowserCdpEndpointBlockedError);
await expect(remote.listTabs()).rejects.toMatchObject({
name: "BrowserCdpEndpointBlockedError",
});
await expect(remote.focusTab("T1")).rejects.toMatchObject({
name: "BrowserCdpEndpointBlockedError",
});
await expect(remote.closeTab("T1")).rejects.toMatchObject({
name: "BrowserCdpEndpointBlockedError",
});
expect(listPagesViaPlaywright).not.toHaveBeenCalled();
expect(focusPageByTargetIdViaPlaywright).not.toHaveBeenCalled();
expect(closePageByTargetIdViaPlaywright).not.toHaveBeenCalled();

View File

@@ -82,9 +82,7 @@ export function createProfileTabOps({
const mod = await getPwAiModule({ mode: "strict" });
const listPagesViaPlaywright = (mod as Partial<PwAiModule> | null)?.listPagesViaPlaywright;
if (typeof listPagesViaPlaywright === "function") {
// SSRF check runs inside connectBrowser on cache miss; skip the
// redundant pre-call DNS lookup so cached sessions are not broken
// by transient resolver failures.
await assertProfileCdpEndpointAllowed();
const pages = await listPagesViaPlaywright({
cdpUrl: profile.cdpUrl,
ssrfPolicy: state().resolved.ssrfPolicy,