From 73728127b65919dfb85299cbd239f5c2a0d76992 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 18 Apr 2026 23:25:55 +0100 Subject: [PATCH] refactor(browser): share SSRF hostname allowlist helper --- .../src/browser/cdp-reachability-policy.ts | 8 ++------ extensions/browser/src/browser/cdp.helpers.ts | 15 +++------------ .../browser/src/browser/ssrf-policy-helpers.ts | 11 +++++++++++ 3 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 extensions/browser/src/browser/ssrf-policy-helpers.ts diff --git a/extensions/browser/src/browser/cdp-reachability-policy.ts b/extensions/browser/src/browser/cdp-reachability-policy.ts index c53bc75ef31..40c9361fa21 100644 --- a/extensions/browser/src/browser/cdp-reachability-policy.ts +++ b/extensions/browser/src/browser/cdp-reachability-policy.ts @@ -1,6 +1,7 @@ import { isPrivateNetworkAllowedByPolicy, type SsrFPolicy } from "../infra/net/ssrf.js"; import type { ResolvedBrowserProfile } from "./config.js"; import { getBrowserProfileCapabilities } from "./profile-capabilities.js"; +import { withAllowedHostname } from "./ssrf-policy-helpers.js"; function withCdpHostnameAllowed( profile: ResolvedBrowserProfile, @@ -12,12 +13,7 @@ function withCdpHostnameAllowed( if (isPrivateNetworkAllowedByPolicy(ssrfPolicy)) { return ssrfPolicy; } - return { - ...ssrfPolicy, - allowedHostnames: Array.from( - new Set([...(ssrfPolicy.allowedHostnames ?? []), profile.cdpHost]), - ), - }; + return withAllowedHostname(ssrfPolicy, profile.cdpHost); } export function resolveCdpReachabilityPolicy( diff --git a/extensions/browser/src/browser/cdp.helpers.ts b/extensions/browser/src/browser/cdp.helpers.ts index 0b777a54029..fa9b1afcaaf 100644 --- a/extensions/browser/src/browser/cdp.helpers.ts +++ b/extensions/browser/src/browser/cdp.helpers.ts @@ -13,6 +13,7 @@ import { getDirectAgentForCdp, withNoProxyForCdpUrl } from "./cdp-proxy-bypass.j import { CDP_HTTP_REQUEST_TIMEOUT_MS, CDP_WS_HANDSHAKE_TIMEOUT_MS } from "./cdp-timeouts.js"; import { BrowserCdpEndpointBlockedError } from "./errors.js"; import { resolveBrowserRateLimitMessage } from "./rate-limit-message.js"; +import { withAllowedHostname } from "./ssrf-policy-helpers.js"; export { isLoopbackHost }; @@ -70,12 +71,7 @@ export async function assertCdpEndpointAllowed( } try { const policy = isLoopbackHost(parsed.hostname) - ? { - ...ssrfPolicy, - allowedHostnames: Array.from( - new Set([...(ssrfPolicy?.allowedHostnames ?? []), parsed.hostname]), - ), - } + ? withAllowedHostname(ssrfPolicy, parsed.hostname) : ssrfPolicy; await resolvePinnedHostnameWithPolicy(parsed.hostname, { policy, @@ -273,12 +269,7 @@ export async function fetchCdpChecked( const res = await withNoProxyForCdpUrl(url, async () => { const parsedUrl = new URL(url); const policy = isLoopbackHost(parsedUrl.hostname) - ? { - ...ssrfPolicy, - allowedHostnames: Array.from( - new Set([...(ssrfPolicy?.allowedHostnames ?? []), parsedUrl.hostname]), - ), - } + ? withAllowedHostname(ssrfPolicy, parsedUrl.hostname) : (ssrfPolicy ?? { allowPrivateNetwork: true }); const guarded = await fetchWithSsrFGuard({ url, diff --git a/extensions/browser/src/browser/ssrf-policy-helpers.ts b/extensions/browser/src/browser/ssrf-policy-helpers.ts new file mode 100644 index 00000000000..3b5b5980981 --- /dev/null +++ b/extensions/browser/src/browser/ssrf-policy-helpers.ts @@ -0,0 +1,11 @@ +import type { SsrFPolicy } from "../infra/net/ssrf.js"; + +export function withAllowedHostname( + ssrfPolicy: SsrFPolicy | undefined, + hostname: string, +): SsrFPolicy { + return { + ...ssrfPolicy, + allowedHostnames: Array.from(new Set([...(ssrfPolicy?.allowedHostnames ?? []), hostname])), + }; +}