From fa2ee2af8569b677ee3943f326fff0bd74e4f0b3 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 10 Apr 2026 17:21:01 +0100 Subject: [PATCH] test: enforce browser cdp policy before playwright --- extensions/browser/src/browser/cdp.test.ts | 7 ++++--- extensions/browser/src/browser/chrome.test.ts | 4 ++-- ...ontext.remote-profile-tab-ops.playwright.test.ts | 13 +++++++++---- .../browser/src/browser/server-context.tab-ops.ts | 4 +--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/extensions/browser/src/browser/cdp.test.ts b/extensions/browser/src/browser/cdp.test.ts index d70e5586b35..5f13bed78ab 100644 --- a/extensions/browser/src/browser/cdp.test.ts +++ b/extensions/browser/src/browser/cdp.test.ts @@ -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 () => { diff --git a/extensions/browser/src/browser/chrome.test.ts b/extensions/browser/src/browser/chrome.test.ts index e78a7ab306b..cc0b3f49d93 100644 --- a/extensions/browser/src/browser/chrome.test.ts +++ b/extensions/browser/src/browser/chrome.test.ts @@ -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[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((resolve) => server.close(() => resolve())); } diff --git a/extensions/browser/src/browser/server-context.remote-profile-tab-ops.playwright.test.ts b/extensions/browser/src/browser/server-context.remote-profile-tab-ops.playwright.test.ts index 7dc7b1c7224..b7e40d61917 100644 --- a/extensions/browser/src/browser/server-context.remote-profile-tab-ops.playwright.test.ts +++ b/extensions/browser/src/browser/server-context.remote-profile-tab-ops.playwright.test.ts @@ -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(); diff --git a/extensions/browser/src/browser/server-context.tab-ops.ts b/extensions/browser/src/browser/server-context.tab-ops.ts index 0377f542949..c16a0803e49 100644 --- a/extensions/browser/src/browser/server-context.tab-ops.ts +++ b/extensions/browser/src/browser/server-context.tab-ops.ts @@ -82,9 +82,7 @@ export function createProfileTabOps({ const mod = await getPwAiModule({ mode: "strict" }); const listPagesViaPlaywright = (mod as Partial | 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,