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

@@ -34,6 +34,7 @@ function makeState(
headless: true,
noSandbox: false,
attachOnly: false,
ssrfPolicy: { allowPrivateNetwork: true },
defaultProfile: profile,
profiles: {
remote: {
@@ -65,12 +66,12 @@ function createRemoteRouteHarness(fetchMock?: ReturnType<typeof vi.fn>) {
describe("browser server-context remote profile tab operations", () => {
it("uses Playwright tab operations when available", async () => {
const listPagesViaPlaywright = vi.fn(async () => [
{ targetId: "T1", title: "Tab 1", url: "https://a.example", type: "page" },
{ targetId: "T1", title: "Tab 1", url: "https://example.com", type: "page" },
]);
const createPageViaPlaywright = vi.fn(async () => ({
targetId: "T2",
title: "Tab 2",
url: "https://b.example",
url: "http://127.0.0.1:3000",
type: "page",
}));
const closePageByTargetIdViaPlaywright = vi.fn(async () => {});
@@ -86,7 +87,7 @@ describe("browser server-context remote profile tab operations", () => {
const tabs = await remote.listTabs();
expect(tabs.map((t) => t.targetId)).toEqual(["T1"]);
const opened = await remote.openTab("https://b.example");
const opened = await remote.openTab("http://127.0.0.1:3000");
expect(opened.targetId).toBe("T2");
expect(state.profiles.get("remote")?.lastTargetId).toBe("T2");
@@ -102,21 +103,21 @@ describe("browser server-context remote profile tab operations", () => {
const responses = [
// ensureTabAvailable() calls listTabs twice
[
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
{ targetId: "A", title: "A", url: "https://example.com", type: "page" },
{ targetId: "B", title: "B", url: "https://www.example.com", type: "page" },
],
[
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
{ targetId: "A", title: "A", url: "https://example.com", type: "page" },
{ targetId: "B", title: "B", url: "https://www.example.com", type: "page" },
],
// second ensureTabAvailable() calls listTabs twice, order flips
[
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://www.example.com", type: "page" },
{ targetId: "A", title: "A", url: "https://example.com", type: "page" },
],
[
{ targetId: "B", title: "B", url: "https://b.example", type: "page" },
{ targetId: "A", title: "A", url: "https://a.example", type: "page" },
{ targetId: "B", title: "B", url: "https://www.example.com", type: "page" },
{ targetId: "A", title: "A", url: "https://example.com", type: "page" },
],
];
@@ -148,7 +149,7 @@ describe("browser server-context remote profile tab operations", () => {
it("uses Playwright focus for remote profiles when available", async () => {
const listPagesViaPlaywright = vi.fn(async () => [
{ targetId: "T1", title: "Tab 1", url: "https://a.example", type: "page" },
{ targetId: "T1", title: "Tab 1", url: "https://example.com", type: "page" },
]);
const focusPageByTargetIdViaPlaywright = vi.fn(async () => {});
@@ -195,7 +196,7 @@ describe("browser server-context remote profile tab operations", () => {
{
id: "T1",
title: "Tab 1",
url: "https://a.example",
url: "https://example.com",
webSocketDebuggerUrl: "wss://browserless.example/devtools/page/T1",
type: "page",
},
@@ -226,7 +227,7 @@ describe("browser server-context tab selection state", () => {
{
id: "CREATED",
title: "New Tab",
url: "https://created.example",
url: "http://127.0.0.1:8080",
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/CREATED",
type: "page",
},
@@ -240,7 +241,7 @@ describe("browser server-context tab selection state", () => {
const ctx = createBrowserRouteContext({ getState: () => state });
const openclaw = ctx.forProfile("openclaw");
const opened = await openclaw.openTab("https://created.example");
const opened = await openclaw.openTab("http://127.0.0.1:8080");
expect(opened.targetId).toBe("CREATED");
expect(state.profiles.get("openclaw")?.lastTargetId).toBe("CREATED");
});