From aefc6fc161f6d9b2a254f5bc3c026f4885d2787b Mon Sep 17 00:00:00 2001 From: Agustin Rivera <31522568+eleqtrizit@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:33:50 +0000 Subject: [PATCH] fix(browser): validate profile cdp urls --- .../src/browser/profiles-service.test.ts | 24 +++++++++++++++++++ .../browser/src/browser/profiles-service.ts | 2 ++ 2 files changed, 26 insertions(+) diff --git a/extensions/browser/src/browser/profiles-service.test.ts b/extensions/browser/src/browser/profiles-service.test.ts index 8668d1f7a32..0cd88916988 100644 --- a/extensions/browser/src/browser/profiles-service.test.ts +++ b/extensions/browser/src/browser/profiles-service.test.ts @@ -141,6 +141,30 @@ describe("BrowserProfilesService", () => { ); }); + it("rejects private-network cdpUrl when strict SSRF mode is enabled", async () => { + const resolved = resolveBrowserConfig({ + ssrfPolicy: { dangerouslyAllowPrivateNetwork: false }, + }); + const { ctx } = createCtx(resolved); + + vi.mocked(loadConfig).mockReturnValue({ + browser: { + ssrfPolicy: { dangerouslyAllowPrivateNetwork: false }, + profiles: {}, + }, + }); + + const service = createBrowserProfilesService(ctx); + + await expect( + service.createProfile({ + name: "remote", + cdpUrl: "http://10.0.0.42:9222", + }), + ).rejects.toThrow(/private\/internal\/special-use ip address/i); + expect(writeConfigFile).not.toHaveBeenCalled(); + }); + it("creates existing-session profiles as attach-only local entries", async () => { const resolved = resolveBrowserConfig({}); const { ctx, state } = createCtx(resolved); diff --git a/extensions/browser/src/browser/profiles-service.ts b/extensions/browser/src/browser/profiles-service.ts index ea1f3b674c6..74919d22087 100644 --- a/extensions/browser/src/browser/profiles-service.ts +++ b/extensions/browser/src/browser/profiles-service.ts @@ -4,6 +4,7 @@ import type { BrowserProfileConfig, OpenClawConfig } from "../config/config.js"; import { loadConfig, writeConfigFile } from "../config/config.js"; import { deriveDefaultBrowserCdpPortRange } from "../config/port-defaults.js"; import { resolveUserPath } from "../utils.js"; +import { assertCdpEndpointAllowed } from "./cdp.helpers.js"; import { resolveOpenClawUserDataDir } from "./chrome.js"; import { parseHttpUrl, resolveProfile } from "./config.js"; import { @@ -124,6 +125,7 @@ export function createBrowserProfilesService(ctx: BrowserRouteContext) { let parsed: ReturnType; try { parsed = parseHttpUrl(rawCdpUrl, "browser.profiles.cdpUrl"); + await assertCdpEndpointAllowed(parsed.normalized, state.resolved.ssrfPolicy); } catch (err) { throw new BrowserValidationError(String(err)); }