






















1+import { beforeEach, describe, expect, it, vi } from "vitest";
2+import { createBrowserRouteApp, createBrowserRouteResponse } from "./test-helpers.js";
3+import type { BrowserRequest } from "./types.js";
4+5+const routeState = vi.hoisted(() => ({
6+profileCtx: {
7+profile: {
8+driver: "openclaw" as const,
9+name: "openclaw",
10+cdpUrl: "http://127.0.0.1:18800",
11+cdpIsLoopback: true,
12+},
13+ensureTabAvailable: vi.fn(async () => ({
14+targetId: "7",
15+url: "http://127.0.0.1:8080/admin",
16+wsUrl: "ws://127.0.0.1/devtools/page/7",
17+})),
18+},
19+}));
20+21+const cdpMocks = vi.hoisted(() => ({
22+snapshotAria: vi.fn(async () => ({
23+nodes: [{ ref: "1", role: "link", name: "private", depth: 0 }],
24+})),
25+snapshotRoleViaCdp: vi.fn(async () => ({
26+snapshot: '- link "private" [ref=e1]',
27+refs: { e1: { role: "link", name: "private" } },
28+stats: { lines: 1, chars: 25, refs: 1, interactive: 1 },
29+})),
30+}));
31+32+const navigationGuardMocks = vi.hoisted(() => ({
33+assertBrowserNavigationAllowed: vi.fn(async () => {}),
34+assertBrowserNavigationResultAllowed: vi.fn(async () => {
35+throw new Error("browser navigation blocked by policy");
36+}),
37+withBrowserNavigationPolicy: vi.fn((ssrfPolicy?: unknown) => (ssrfPolicy ? { ssrfPolicy } : {})),
38+}));
39+40+vi.mock("../cdp.js", () => ({
41+captureScreenshot: vi.fn(),
42+snapshotAria: cdpMocks.snapshotAria,
43+snapshotRoleViaCdp: cdpMocks.snapshotRoleViaCdp,
44+}));
45+46+vi.mock("../chrome-mcp.js", () => ({
47+evaluateChromeMcpScript: vi.fn(),
48+navigateChromeMcpPage: vi.fn(),
49+takeChromeMcpScreenshot: vi.fn(),
50+takeChromeMcpSnapshot: vi.fn(),
51+}));
52+53+vi.mock("../navigation-guard.js", () => ({
54+assertBrowserNavigationAllowed: navigationGuardMocks.assertBrowserNavigationAllowed,
55+assertBrowserNavigationResultAllowed: navigationGuardMocks.assertBrowserNavigationResultAllowed,
56+withBrowserNavigationPolicy: navigationGuardMocks.withBrowserNavigationPolicy,
57+}));
58+59+vi.mock("../screenshot.js", () => ({
60+DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES: 128,
61+DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE: 64,
62+normalizeBrowserScreenshot: vi.fn(async (buffer: Buffer) => ({
63+ buffer,
64+contentType: "image/png",
65+})),
66+}));
67+68+vi.mock("../../media/store.js", () => ({
69+ensureMediaDir: vi.fn(async () => {}),
70+saveMediaBuffer: vi.fn(async () => ({ path: "/tmp/fake.png" })),
71+}));
72+73+vi.mock("./agent.shared.js", () => ({
74+getPwAiModule: vi.fn(async () => null),
75+handleRouteError: vi.fn(
76+(
77+_ctx: unknown,
78+res: { status: (code: number) => unknown; json: (body: unknown) => void },
79+err: unknown,
80+) => {
81+const message = err instanceof Error ? err.message : String(err);
82+res.status(400);
83+res.json({ error: message });
84+},
85+),
86+readBody: vi.fn((req: BrowserRequest) => req.body ?? {}),
87+requirePwAi: vi.fn(async () => null),
88+resolveProfileContext: vi.fn(() => routeState.profileCtx),
89+withPlaywrightRouteContext: vi.fn(),
90+withRouteTabContext: vi.fn(),
91+}));
92+93+const { registerBrowserAgentSnapshotRoutes } = await import("./agent.snapshot.js");
94+95+function getSnapshotGetHandler() {
96+const { app, getHandlers } = createBrowserRouteApp();
97+registerBrowserAgentSnapshotRoutes(app, {
98+state: () => ({
99+resolved: {
100+extraArgs: [],
101+ssrfPolicy: { dangerouslyAllowPrivateNetwork: false },
102+},
103+}),
104+} as never);
105+const handler = getHandlers.get("/snapshot");
106+expect(handler).toBeTypeOf("function");
107+return handler;
108+}
109+110+describe("local-managed browser snapshot routes", () => {
111+beforeEach(() => {
112+routeState.profileCtx.ensureTabAvailable.mockClear();
113+cdpMocks.snapshotAria.mockClear();
114+cdpMocks.snapshotRoleViaCdp.mockClear();
115+navigationGuardMocks.assertBrowserNavigationResultAllowed.mockClear();
116+navigationGuardMocks.withBrowserNavigationPolicy.mockClear();
117+});
118+119+it("blocks ARIA CDP snapshots when the current tab violates browser navigation policy", async () => {
120+const handler = getSnapshotGetHandler();
121+const response = createBrowserRouteResponse();
122+123+await handler?.({ params: {}, query: { format: "aria" } }, response.res);
124+125+expect(response.statusCode).toBe(400);
126+expect(response.body).toEqual({ error: "browser navigation blocked by policy" });
127+expect(navigationGuardMocks.assertBrowserNavigationResultAllowed).toHaveBeenCalledWith({
128+url: "http://127.0.0.1:8080/admin",
129+ssrfPolicy: { dangerouslyAllowPrivateNetwork: false },
130+});
131+expect(cdpMocks.snapshotAria).not.toHaveBeenCalled();
132+});
133+134+it("blocks AI CDP role snapshots when the current tab violates browser navigation policy", async () => {
135+const handler = getSnapshotGetHandler();
136+const response = createBrowserRouteResponse();
137+138+await handler?.({ params: {}, query: { format: "ai", interactive: "true" } }, response.res);
139+140+expect(response.statusCode).toBe(400);
141+expect(response.body).toEqual({ error: "browser navigation blocked by policy" });
142+expect(navigationGuardMocks.assertBrowserNavigationResultAllowed).toHaveBeenCalledWith({
143+url: "http://127.0.0.1:8080/admin",
144+ssrfPolicy: { dangerouslyAllowPrivateNetwork: false },
145+});
146+expect(cdpMocks.snapshotRoleViaCdp).not.toHaveBeenCalled();
147+});
148+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。