From e90c89cf8b1459f2aa1f3a665be67392b6c03fdf Mon Sep 17 00:00:00 2001 From: HansY Date: Fri, 17 Apr 2026 16:45:02 +0000 Subject: [PATCH] fix(browser): auto-allowlist configured CDP hostnames in SSRF policy --- extensions/browser/src/browser/config.test.ts | 46 +++++++++++++++++++ extensions/browser/src/browser/config.ts | 46 +++++++++++++++++-- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/extensions/browser/src/browser/config.test.ts b/extensions/browser/src/browser/config.test.ts index d74f5bc8b24..29c04f0bfd6 100644 --- a/extensions/browser/src/browser/config.test.ts +++ b/extensions/browser/src/browser/config.test.ts @@ -343,6 +343,52 @@ describe("browser config", () => { }); }); + it("auto-allowlists hostnames from user-configured profile cdpUrls", () => { + const resolved = resolveBrowserConfig({ + profiles: { + remote: { + color: "#123456", + cdpUrl: "http://172.29.128.1:9223", + }, + }, + }); + expect(resolved.ssrfPolicy).toEqual({ + allowedHostnames: ["172.29.128.1"], + }); + }); + + it("merges configured profile cdpUrl hostnames with existing ssrfPolicy allowedHostnames", () => { + const resolved = resolveBrowserConfig({ + ssrfPolicy: { + allowedHostnames: ["metadata.internal"], + }, + profiles: { + remote: { + color: "#123456", + cdpUrl: "http://172.29.128.1:9223", + }, + }, + }); + expect(resolved.ssrfPolicy?.allowedHostnames?.toSorted()).toEqual( + ["172.29.128.1", "metadata.internal"].toSorted(), + ); + }); + + it("does not duplicate hostnames already in allowedHostnames", () => { + const resolved = resolveBrowserConfig({ + ssrfPolicy: { + allowedHostnames: ["172.29.128.1"], + }, + profiles: { + remote: { + color: "#123456", + cdpUrl: "http://172.29.128.1:9223", + }, + }, + }); + expect(resolved.ssrfPolicy?.allowedHostnames).toEqual(["172.29.128.1"]); + }); + it("resolves existing-session profiles without cdpPort or cdpUrl", () => { const resolved = resolveBrowserConfig({ profiles: { diff --git a/extensions/browser/src/browser/config.ts b/extensions/browser/src/browser/config.ts index 503146dcf4a..6b0a3de242f 100644 --- a/extensions/browser/src/browser/config.ts +++ b/extensions/browser/src/browser/config.ts @@ -126,11 +126,27 @@ function resolveCdpPortRangeStart( const normalizeStringList = normalizeOptionalTrimmedStringList; -function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy | undefined { +function mergeAllowedHostnames( + base: string[] | undefined, + extra: readonly string[], +): string[] | undefined { + if (extra.length === 0) { + return base; + } + return Array.from(new Set([...(base ?? []), ...extra])); +} + +function resolveBrowserSsrFPolicy( + cfg: BrowserConfig | undefined, + extraAllowedHostnames: readonly string[] = [], +): SsrFPolicy | undefined { const rawPolicy = cfg?.ssrfPolicy as BrowserSsrFPolicyCompat | undefined; const allowPrivateNetwork = rawPolicy?.allowPrivateNetwork; const dangerouslyAllowPrivateNetwork = rawPolicy?.dangerouslyAllowPrivateNetwork; - const allowedHostnames = normalizeStringList(rawPolicy?.allowedHostnames); + const allowedHostnames = mergeAllowedHostnames( + normalizeStringList(rawPolicy?.allowedHostnames), + extraAllowedHostnames, + ); const hostnameAllowlist = normalizeStringList(rawPolicy?.hostnameAllowlist); const hasExplicitPrivateSetting = allowPrivateNetwork !== undefined || dangerouslyAllowPrivateNetwork !== undefined; @@ -159,6 +175,30 @@ function resolveBrowserSsrFPolicy(cfg: BrowserConfig | undefined): SsrFPolicy | }; } +function collectConfiguredCdpHostnames(cfg: BrowserConfig | undefined): string[] { + const hostnames = new Set(); + const addHostnameFromUrl = (rawUrl: string | undefined): void => { + const trimmed = rawUrl?.trim() ?? ""; + if (!trimmed) { + return; + } + try { + const hostname = new URL(trimmed).hostname; + if (hostname) { + hostnames.add(hostname); + } + } catch { + // Ignore unparseable URLs; they will be rejected elsewhere with a proper error. + } + }; + + addHostnameFromUrl(cfg?.cdpUrl); + for (const profile of Object.values(cfg?.profiles ?? {})) { + addHostnameFromUrl(profile?.cdpUrl); + } + return Array.from(hostnames); +} + function ensureDefaultProfile( profiles: Record | undefined, defaultColor: string, @@ -293,7 +333,7 @@ export function resolveBrowserConfig( attachOnly, defaultProfile, profiles, - ssrfPolicy: resolveBrowserSsrFPolicy(cfg), + ssrfPolicy: resolveBrowserSsrFPolicy(cfg, collectConfiguredCdpHostnames(cfg)), extraArgs, }; }