diff --git a/extensions/browser/src/browser/server-context.tab-ops.ts b/extensions/browser/src/browser/server-context.tab-ops.ts index 9344f4af6cd..f91bcadb4cd 100644 --- a/extensions/browser/src/browser/server-context.tab-ops.ts +++ b/extensions/browser/src/browser/server-context.tab-ops.ts @@ -230,14 +230,14 @@ export function createProfileTabOps({ { method: "PUT", }, - ssrfPolicyOpts.ssrfPolicy, + getCdpControlPolicy(), ).catch(async (err) => { if (String(err).includes("HTTP 405")) { return await fetchJson( endpoint, CDP_JSON_NEW_TIMEOUT_MS, undefined, - ssrfPolicyOpts.ssrfPolicy, + getCdpControlPolicy(), ); } throw err; diff --git a/extensions/browser/src/browser/server-context.tab-selection-state.test.ts b/extensions/browser/src/browser/server-context.tab-selection-state.test.ts index f6bd2c5d704..03354909abf 100644 --- a/extensions/browser/src/browser/server-context.tab-selection-state.test.ts +++ b/extensions/browser/src/browser/server-context.tab-selection-state.test.ts @@ -6,6 +6,7 @@ vi.hoisted(() => { }); import "./server-context.chrome-test-harness.js"; +import * as cdpHelpersModule from "./cdp.helpers.js"; import * as cdpModule from "./cdp.js"; import { InvalidBrowserNavigationUrlError } from "./navigation-guard.js"; import { createBrowserRouteContext } from "./server-context.js"; @@ -296,4 +297,38 @@ describe("browser server-context tab selection state", () => { ); expect(fetchMock).not.toHaveBeenCalled(); }); + + it("uses the loopback CDP control policy for /json/new fallback requests", async () => { + vi.spyOn(cdpModule, "createTargetViaCdp").mockRejectedValue(new Error("cdp unavailable")); + const fetchJson = vi.spyOn(cdpHelpersModule, "fetchJson"); + fetchJson.mockRejectedValueOnce(new Error("HTTP 405")).mockResolvedValueOnce({ + id: "NEW", + title: "New Tab", + url: "https://example.com", + webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/NEW", + type: "page", + }); + + const state = makeState("openclaw"); + state.resolved.ssrfPolicy = {}; + const ctx = createBrowserRouteContext({ getState: () => state }); + const openclaw = ctx.forProfile("openclaw"); + + const opened = await openclaw.openTab("https://example.com"); + expect(opened.targetId).toBe("NEW"); + expect(fetchJson).toHaveBeenNthCalledWith( + 1, + expect.stringContaining("/json/new"), + expect.any(Number), + { method: "PUT" }, + undefined, + ); + expect(fetchJson).toHaveBeenNthCalledWith( + 2, + expect.stringContaining("/json/new"), + expect.any(Number), + undefined, + undefined, + ); + }); });