From 2cf765f73263df77d4593941ca5a768d8e53b084 Mon Sep 17 00:00:00 2001 From: Agustin Rivera <31522568+eleqtrizit@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:10:12 -0700 Subject: [PATCH] fix(browser): block node routes when sandbox host control is disabled (#97958) --- extensions/browser/src/browser-tool.test.ts | 17 ++++++++++++++++- extensions/browser/src/browser-tool.ts | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/extensions/browser/src/browser-tool.test.ts b/extensions/browser/src/browser-tool.test.ts index 978a4cbad677..7539111a2347 100644 --- a/extensions/browser/src/browser-tool.test.ts +++ b/extensions/browser/src/browser-tool.test.ts @@ -871,6 +871,21 @@ describe("browser tool snapshot maxChars", () => { expect(browserClientMocks.browserStatus).not.toHaveBeenCalled(); }); + it.each([ + ["target=node", { target: "node" }], + ["an explicit node pin", { node: "node-1" }], + ["automatic node routing", {}], + ])("blocks %s when host control is disabled", async (_label, route) => { + mockSingleBrowserProxyNode(); + const tool = createBrowserTool({ allowHostControl: false }); + + await expect(tool.execute?.("call-1", { action: "status", ...route })).rejects.toThrow( + /browser control is disabled by sandbox policy/i, + ); + expect(gatewayMocks.callGatewayTool).not.toHaveBeenCalled(); + expect(browserClientMocks.browserStatus).not.toHaveBeenCalled(); + }); + it("fails node proxy calls cleanly when payloadJSON is malformed", async () => { mockSingleBrowserProxyNode(); gatewayMocks.callGatewayTool.mockResolvedValueOnce({ @@ -1247,7 +1262,7 @@ describe("browser tool snapshot maxChars", () => { setResolvedBrowserProfiles({ user: { driver: "existing-session", attachOnly: true, color: "#00AA00" }, }); - const tool = createBrowserTool(); + const tool = createBrowserTool({ allowHostControl: true }); await tool.execute?.("call-1", { action: "status", profile: "user", target: "node" }); const { options, request } = lastNodeInvokeCall(); diff --git a/extensions/browser/src/browser-tool.ts b/extensions/browser/src/browser-tool.ts index b92f646c9014..463085cfd502 100644 --- a/extensions/browser/src/browser-tool.ts +++ b/extensions/browser/src/browser-tool.ts @@ -262,7 +262,15 @@ async function resolveBrowserNodeTarget(params: { requestedNode?: string; target?: "sandbox" | "host" | "node"; sandboxBridgeUrl?: string; + allowHostControl?: boolean; }): Promise { + if (params.allowHostControl === false) { + if (params.target === "node" || params.requestedNode) { + throw new Error("Node browser control is disabled by sandbox policy."); + } + return null; + } + const cfg = browserToolDeps.getRuntimeConfig(); const policy = cfg.gateway?.nodes?.browser; const mode = policy?.mode ?? "auto"; @@ -538,6 +546,7 @@ export function createBrowserTool(opts?: { requestedNode: requestedNode ?? undefined, target, sandboxBridgeUrl: opts?.sandboxBridgeUrl, + allowHostControl: opts?.allowHostControl, }); } catch (error) { // Keep the logged-in user browser usable on the host when auto-discovery