fix(browser): unify SSRF guard path for navigation

This commit is contained in:
Peter Steinberger
2026-02-19 13:43:48 +01:00
parent 3c419b7bd3
commit 6195660b1a
15 changed files with 269 additions and 18 deletions

View File

@@ -1,4 +1,5 @@
import fs from "node:fs";
import { SsrFBlockedError } from "../infra/net/ssrf.js";
import { fetchJson, fetchOk } from "./cdp.helpers.js";
import { appendCdpPath, createTargetViaCdp, normalizeCdpWsUrl } from "./cdp.js";
import {
@@ -14,6 +15,7 @@ import {
ensureChromeExtensionRelayServer,
stopChromeExtensionRelayServer,
} from "./extension-relay.js";
import { assertBrowserNavigationAllowed } from "./navigation-guard.js";
import type { PwAiModule } from "./pw-ai-module.js";
import { getPwAiModule } from "./pw-ai-module.js";
import {
@@ -130,13 +132,20 @@ function createProfileContext(
};
const openTab = async (url: string): Promise<BrowserTab> => {
const ssrfPolicy = state().resolved.ssrfPolicy;
await assertBrowserNavigationAllowed({ url, ssrfPolicy });
// For remote profiles, use Playwright's persistent connection to create tabs
// This ensures the tab persists beyond a single request
if (!profile.cdpIsLoopback) {
const mod = await getPwAiModule({ mode: "strict" });
const createPageViaPlaywright = (mod as Partial<PwAiModule> | null)?.createPageViaPlaywright;
if (typeof createPageViaPlaywright === "function") {
const page = await createPageViaPlaywright({ cdpUrl: profile.cdpUrl, url });
const page = await createPageViaPlaywright({
cdpUrl: profile.cdpUrl,
url,
...(ssrfPolicy ? { ssrfPolicy } : {}),
});
const profileState = getProfileState();
profileState.lastTargetId = page.targetId;
return {
@@ -151,6 +160,7 @@ function createProfileContext(
const createdViaCdp = await createTargetViaCdp({
cdpUrl: profile.cdpUrl,
url,
...(ssrfPolicy ? { ssrfPolicy } : {}),
})
.then((r) => r.targetId)
.catch(() => null);
@@ -632,7 +642,13 @@ export function createBrowserRouteContext(opts: ContextOptions): BrowserRouteCon
const getDefaultContext = () => forProfile();
const mapTabError = (err: unknown) => {
if (err instanceof SsrFBlockedError) {
return { status: 400, message: err.message };
}
const msg = String(err);
if (msg.includes("Invalid URL:")) {
return { status: 400, message: msg };
}
if (msg.includes("ambiguous target id prefix")) {
return { status: 409, message: "ambiguous target id prefix" };
}