From 29a35f04a9e653b66fb9ca53b56be5a121d1ca73 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 30 Apr 2026 15:56:40 +0100 Subject: [PATCH] fix(browser): use source config for proxy decisions --- extensions/browser/src/control-service.ts | 4 +- .../src/node-host/invoke-browser.test.ts | 42 ++++++++++++++++++- .../browser/src/node-host/invoke-browser.ts | 8 ++-- extensions/browser/src/server.ts | 4 +- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/extensions/browser/src/control-service.ts b/extensions/browser/src/control-service.ts index f7e81721557..79d658b80e7 100644 --- a/extensions/browser/src/control-service.ts +++ b/extensions/browser/src/control-service.ts @@ -22,10 +22,10 @@ export async function startBrowserControlServiceFromConfig(): Promise ({ browser: {}, nodeHost: { browserProxy: { enabled: true, allowProfiles: [] as string[] } }, })), + sourceConfig: null as Record | null, })); const browserConfigMocks = vi.hoisted(() => ({ - resolveBrowserConfig: vi.fn(() => ({ + resolveBrowserConfig: vi.fn((browser?: { defaultProfile?: string }) => ({ enabled: true, - defaultProfile: "openclaw", + defaultProfile: browser?.defaultProfile ?? "openclaw", })), })); vi.mock("../sdk-config.js", () => ({ getRuntimeConfig: configMocks.loadConfig, + getRuntimeConfigSourceSnapshot: () => configMocks.sourceConfig, loadConfig: configMocks.loadConfig, })); @@ -150,6 +152,7 @@ describe("runBrowserProxyCommand", () => { })); controlServiceMocks.createBrowserControlContext.mockReset().mockReturnValue({ control: true }); controlServiceMocks.startBrowserControlServiceFromConfig.mockReset().mockResolvedValue(true); + configMocks.sourceConfig = null; configMocks.loadConfig.mockReset().mockReturnValue({ browser: {}, nodeHost: { browserProxy: { enabled: true, allowProfiles: [] as string[] } }, @@ -304,6 +307,41 @@ describe("runBrowserProxyCommand", () => { expect(dispatcherMocks.dispatch).not.toHaveBeenCalled(); }); + it("uses the browser source snapshot for proxy default-profile decisions", async () => { + configMocks.loadConfig.mockReturnValue({ + browser: { defaultProfile: "openclaw" }, + nodeHost: { browserProxy: { enabled: true, allowProfiles: ["work"] } }, + }); + configMocks.sourceConfig = { + browser: { defaultProfile: "work" }, + nodeHost: { browserProxy: { enabled: true, allowProfiles: ["work"] } }, + }; + browserConfigMocks.resolveBrowserConfig.mockImplementation( + (browser?: { defaultProfile?: string }) => ({ + enabled: true, + defaultProfile: browser?.defaultProfile ?? "openclaw", + }), + ); + dispatcherMocks.dispatch.mockResolvedValue({ + status: 200, + body: { ok: true }, + }); + + await runBrowserProxyCommand( + JSON.stringify({ + method: "GET", + path: "/snapshot", + timeoutMs: 50, + }), + ); + + expect(dispatcherMocks.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + path: "/snapshot", + }), + ); + }); + it("rejects unauthorized body.profile when allowProfiles is configured", async () => { configMocks.loadConfig.mockReturnValue({ browser: {}, diff --git a/extensions/browser/src/node-host/invoke-browser.ts b/extensions/browser/src/node-host/invoke-browser.ts index 5b655bf958c..5fe8add51e5 100644 --- a/extensions/browser/src/node-host/invoke-browser.ts +++ b/extensions/browser/src/node-host/invoke-browser.ts @@ -1,5 +1,6 @@ import fsPromises from "node:fs/promises"; import { redactCdpUrl } from "../browser/cdp.helpers.js"; +import { loadBrowserConfigForRuntimeRefresh } from "../browser/config-refresh-source.js"; import { resolveBrowserConfig } from "../browser/config.js"; import { isPersistentBrowserProfileMutation, @@ -11,7 +12,6 @@ import { createBrowserControlContext, startBrowserControlServiceFromConfig, } from "../control-service.js"; -import { getRuntimeConfig } from "../sdk-config.js"; import { withTimeout } from "../sdk-node-runtime.js"; import { detectMime } from "../sdk-setup-tools.js"; @@ -44,7 +44,7 @@ function normalizeProfileAllowlist(raw?: string[]): string[] { } function resolveBrowserProxyConfig() { - const cfg = getRuntimeConfig(); + const cfg = loadBrowserConfigForRuntimeRefresh(); const proxy = cfg.nodeHost?.browserProxy; const allowProfiles = normalizeProfileAllowlist(proxy?.allowProfiles); const enabled = proxy?.enabled !== false; @@ -64,7 +64,7 @@ async function ensureBrowserControlService(): Promise { return browserControlReady; } browserControlReady = (async () => { - const cfg = getRuntimeConfig(); + const cfg = loadBrowserConfigForRuntimeRefresh(); const resolved = resolveBrowserConfig(cfg.browser, cfg); if (!resolved.enabled) { throw new Error("browser control disabled"); @@ -231,7 +231,7 @@ export async function runBrowserProxyCommand(paramsJSON?: string | null): Promis } await ensureBrowserControlService(); - const cfg = getRuntimeConfig(); + const cfg = loadBrowserConfigForRuntimeRefresh(); const resolved = resolveBrowserConfig(cfg.browser, cfg); const method = typeof params.method === "string" ? params.method.toUpperCase() : "GET"; const path = normalizeBrowserRequestPath(pathValue); diff --git a/extensions/browser/src/server.ts b/extensions/browser/src/server.ts index 688c3b37eb2..6bc02d2abd6 100644 --- a/extensions/browser/src/server.ts +++ b/extensions/browser/src/server.ts @@ -35,10 +35,10 @@ export async function startBrowserControlServerFromConfig(): Promise