




















@@ -70,7 +70,11 @@ describe("existing-session interaction navigation guard", () => {
7070for (const fn of Object.values(navigationGuardMocks)) {
7171fn.mockClear();
7272}
73+navigationGuardMocks.assertBrowserNavigationResultAllowed.mockImplementation(
74+async (_opts?: { url: string; ssrfPolicy?: unknown }) => {},
75+);
7376chromeMcpMocks.evaluateChromeMcpScript.mockResolvedValue("https://example.com");
77+routeState.tab.url = "https://example.com";
7478routeState.profileCtx.listTabs.mockReset();
7579routeState.profileCtx.listTabs.mockResolvedValue([
7680{
@@ -136,11 +140,10 @@ describe("existing-session interaction navigation guard", () => {
136140expect(clickResponse.statusCode).toBe(200);
137141expect(typeResponse.statusCode).toBe(200);
138142expect(chromeMcpMocks.clickChromeMcpElement).toHaveBeenCalledOnce();
139-const keyPressCalls = chromeMcpMocks.pressChromeMcpKey.mock.calls as unknown as Array<
140-[{ key?: string }]
141->;
142-expect(keyPressCalls[0]?.[0]?.key).toBe("Enter");
143-expectNavigationProbeUrls(Array.from({ length: 6 }, () => "https://example.com"));
143+expect(chromeMcpMocks.pressChromeMcpKey).toHaveBeenCalledWith(
144+expect.objectContaining({ key: "Enter" }),
145+);
146+expectNavigationProbeUrls(Array.from({ length: 8 }, () => "https://example.com"));
144147});
145148146149it("rechecks the page url after delayed navigation-triggering interactions", async () => {
@@ -155,12 +158,32 @@ describe("existing-session interaction navigation guard", () => {
155158expect(response.statusCode).toBe(200);
156159expect(chromeMcpMocks.evaluateChromeMcpScript).toHaveBeenCalledTimes(4);
157160expectNavigationProbeUrls([
161+"https://example.com",
158162"https://example.com",
159163"http://169.254.169.254/latest/meta-data/",
160164"http://169.254.169.254/latest/meta-data/",
161165]);
162166});
163167168+it("blocks evaluate before execution when the current tab URL is disallowed", async () => {
169+routeState.tab.url = "http://169.254.169.254/latest/meta-data/";
170+navigationGuardMocks.assertBrowserNavigationResultAllowed.mockImplementation(
171+async (opts?: { url: string }) => {
172+const url = opts?.url ?? "";
173+if (url.includes("169.254.169.254")) {
174+throw new Error("blocked current tab");
175+}
176+},
177+);
178+179+await expectActionToThrow(
180+{ kind: "evaluate", fn: "() => document.body.innerText" },
181+"blocked current tab",
182+);
183+expect(chromeMcpMocks.evaluateChromeMcpScript).not.toHaveBeenCalled();
184+expectNavigationProbeUrls(["http://169.254.169.254/latest/meta-data/"]);
185+});
186+164187it("checks URLs for tabs opened during the interaction window", async () => {
165188routeState.profileCtx.listTabs
166189.mockResolvedValueOnce([
@@ -188,6 +211,7 @@ describe("existing-session interaction navigation guard", () => {
188211"https://example.com",
189212"https://example.com",
190213"https://example.com",
214+"https://example.com",
191215"http://169.254.169.254/latest/meta-data/",
192216]);
193217});
@@ -231,7 +255,7 @@ describe("existing-session interaction navigation guard", () => {
231255.mockResolvedValueOnce(" " as never);
232256233257await expectActionToReject({ kind: "evaluate", fn: "() => 1" });
234-expect(navigationGuardMocks.assertBrowserNavigationResultAllowed).not.toHaveBeenCalled();
258+expectNavigationProbeUrls(["https://example.com"]);
235259});
236260237261it("fails closed when a later post-action probe becomes unreadable", async () => {
@@ -243,7 +267,7 @@ describe("existing-session interaction navigation guard", () => {
243267.mockResolvedValueOnce(undefined as never); // follow-up probe - still unreadable
244268245269await expectActionToReject({ kind: "evaluate", fn: "() => 1" });
246-expectNavigationProbeUrls(["https://example.com"]);
270+expectNavigationProbeUrls(["https://example.com", "https://example.com"]);
247271});
248272249273it("confirms stability via follow-up probe when URL changes on the last loop iteration", async () => {
@@ -265,6 +289,7 @@ describe("existing-session interaction navigation guard", () => {
265289// 1 action call + 5 location probes (3 in loop + 1 failed + 1 follow-up)
266290expect(chromeMcpMocks.evaluateChromeMcpScript).toHaveBeenCalledTimes(5);
267291expectNavigationProbeUrls([
292+"https://example.com",
268293"https://example.com",
269294"https://safe-redirect.com",
270295"https://safe-redirect.com",
@@ -284,6 +309,7 @@ describe("existing-session interaction navigation guard", () => {
284309expect(response.statusCode).toBe(200);
285310expect(chromeMcpMocks.evaluateChromeMcpScript).toHaveBeenCalledTimes(5);
286311expectNavigationProbeUrls([
312+"https://example.com",
287313"https://example.com",
288314"https://example.com",
289315"https://safe-redirect.com",
@@ -313,7 +339,11 @@ describe("existing-session interaction navigation guard", () => {
313339.mockRejectedValueOnce(new Error("context destroyed") as never); // follow-up → still errored
314340315341await expectActionToReject({ kind: "evaluate", fn: "() => 1" });
316-expectNavigationProbeUrls(["https://example.com", "https://example.com"]);
342+expectNavigationProbeUrls([
343+"https://example.com",
344+"https://example.com",
345+"https://example.com",
346+]);
317347});
318348319349it("skips the guard when no SSRF policy is configured", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。