Files
reverse-engineering-anything/tests/boundary/browser/cdpWebMcpTransientIdentity.test.ts
T
0154141304 fix(browser): preserve frame identity across captures (#4040)
* fix(browser): preserve capture evidence fidelity

* fix(browser): align frame identity comparisons

* fix(browser): retain historical frame evidence

* fix(electron): retain same-document script owners

* fix(electron): bind frame proof to tree reads

* fix(browser): retain malformed frame scope

* fix(browser): retain WebMCP event scope

* fix(browser): retain malformed timeline identity

* fix(browser): unify frame identity and scope

* fix(electron): prove enclosing frame ancestry

* fix(electron): separate ancestry from file policy

* fix(browser): preserve transient fragment semantics

* fix(browser): retry uncertain initial frame scope

* fix(browser): preserve runtime frame identity

* refactor(browser): separate independent capture fixes

* fix(electron): detect tree-first document replacement

* fix(electron): retain same-document tree authority

---------

Co-authored-by: eco-null <eco-null@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 17:19:35 +08:00

205 lines
6.6 KiB
TypeScript

import { expect, it } from "vitest";
import type { CdpEvent } from "../../../src/browser/CdpConnection.js";
import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js";
import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js";
import { discoverWebMcpToolsInputSchema } from "../../../src/domain/webMcpDiscovery.js";
import { compareWebCaptures } from "../../../src/domain/webCaptureDiff.js";
import { compareWebCapturesInputSchema } from "../../../src/domain/webCaptureDiffSchemas.js";
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";
import { trackBrowser } from "./cdpBrowserProvider.support.js";
it.each(
["about:blank", "about:srcdoc"].flatMap((base) =>
["inline", "split"].flatMap((encoding) =>
["initial", "event"].flatMap((phase) =>
[false, true].map((recover) => ({ base, encoding, phase, recover })),
),
),
),
)(
"keeps temporary document policy separate from fragment identity: $base/$encoding/$phase/$recover",
async ({ base, encoding, phase, recover }) => {
let document = phase === "initial" ? "transient" : "old";
const blank =
encoding === "split"
? { url: base, urlFragment: "#details" }
: { url: base + "#details" };
const frame = (origin: string) => ({
id: "child",
parentId: "frame-main",
loaderId: document,
...(document === "transient"
? blank
: {
url: origin + "/child",
urlFragment: document === "fresh" ? "#current" : "#old",
}),
});
const tool = (name: string) => ({
name,
frameId: "child",
inputSchema: {},
annotations: {
readOnly: true,
untrustedContent: true,
consequential: false,
autosubmit: false,
},
});
const browser = await startFakeCdpBrowser({
webMcpTools: true,
eventsBeforeReply: ["WebMCP.enable"],
commandResult: ({ method }, origin) =>
method === "Page.getFrameTree"
? {
frameTree: {
frame: { id: "frame-main", url: origin + "/app" },
childFrames: [{ frame: frame(origin) }],
},
}
: undefined,
commandEvents: ({ method, sessionId }, origin) => {
if (method !== "WebMCP.enable") return undefined;
const scope = sessionId === undefined ? {} : { sessionId };
const events: CdpEvent[] = [
{
method: "WebMCP.toolsAdded",
...scope,
params: { tools: [tool("stale")] },
},
];
document = "transient";
if (phase === "event")
events.push({
method: "Page.frameNavigated",
...scope,
params: { frame: frame(origin) },
});
if (recover) {
document = "fresh";
events.push({
method: "Page.frameNavigated",
...scope,
params: { frame: frame(origin) },
});
events.push({
method: "WebMCP.toolsAdded",
...scope,
params: { tools: [tool("current")] },
});
}
return events;
},
});
trackBrowser(browser);
const provider = new CdpBrowserProvider();
const input = {
cdp_endpoint: browser.endpoint,
allowed_origins: [browser.allowedOrigin],
target_id: "allowed-page",
observation_ms: 0,
};
const result = await provider.discoverWebMcpTools(
discoverWebMcpToolsInputSchema.parse(input),
);
if (!result.ok) throw result.error;
expect(result.value.tools.items.map(({ name }) => name)).toEqual(
recover ? ["current"] : [],
);
expect(result.value.completeness.policy_filtered_sections).not.toContain(
"webmcp_tools",
);
expect(
result.value.completeness.attach_limited_sections.includes(
"webmcp_tools",
),
).toBe(!recover);
if (recover)
expect(result.value.tools.items[0]?.frame_url).toBe(
browser.allowedOrigin + "/child#current",
);
const inspection = await provider.inspectPage(
inspectWebPageInputSchema.parse(input),
);
if (!inspection.ok) throw inspection.error;
const snapshot = { inspection: inspection.value, webmcp: result.value };
const compared = compareWebCaptures(
compareWebCapturesInputSchema.parse({
before: snapshot,
after: snapshot,
}),
);
expect(compared.dimensions.webmcp.status).toBe(
recover ? "unchanged" : "unknown",
);
},
);
it.each([
"about:blanker#details",
"about:srcdoc-other#details",
"about:blank?policy=1#details",
"https://foreign.invalid/child#about:blank",
"allowed",
])("does not broaden temporary document policy: %s", async (rawUrl) => {
const browser = await startFakeCdpBrowser({
webMcpTools: true,
eventsBeforeReply: ["WebMCP.enable"],
commandResult: ({ method }, origin) =>
method === "Page.getFrameTree"
? {
frameTree: {
frame: { id: "frame-main", url: origin + "/app" },
childFrames: [
{
frame: {
id: "child",
parentId: "frame-main",
url:
rawUrl === "allowed"
? origin + "/child#about:blank"
: rawUrl,
},
},
],
},
}
: undefined,
commandEvents: ({ method, sessionId }) =>
method === "WebMCP.enable"
? [
{
method: "WebMCP.toolsAdded",
...(sessionId === undefined ? {} : { sessionId }),
params: {
tools: [{ name: "current", frameId: "child", inputSchema: {} }],
},
},
]
: undefined,
});
trackBrowser(browser);
const result = await new CdpBrowserProvider().discoverWebMcpTools(
discoverWebMcpToolsInputSchema.parse({
cdp_endpoint: browser.endpoint,
allowed_origins: [browser.allowedOrigin],
target_id: "allowed-page",
observation_ms: 0,
}),
);
if (!result.ok) throw result.error;
expect(result.value.tools.items.map(({ name }) => name)).toEqual(
rawUrl === "allowed" ? ["current"] : [],
);
expect(
result.value.completeness.policy_filtered_sections.includes("webmcp_tools"),
).toBe(rawUrl !== "allowed");
expect(result.value.completeness.attach_limited_sections).not.toContain(
"webmcp_tools",
);
if (rawUrl === "allowed")
expect(result.value.tools.items[0]?.frame_url).toBe(
browser.allowedOrigin + "/child#about:blank",
);
});