Files
rea/tests/acceptance/browser/webBundleContainmentCli.test.ts
morluto 9aca543f46 fix(browser): isolate web bundle analysis with bounded partial results
Run inert captured-script analysis in owned children with per-script and aggregate deadlines, bounded source/result transfer and explicit per-script coverage. Preserve completed facts and exact cleanup authority across cancellation, mixed descriptor/process failures and cross-consumer retry.

Share aggregate source admission across page and Electron capture, expose CLI/MCP resource controls and retain selected parameters in Evidence. Verified with controlled CDP public workflows, real owned Node failure controls and the reporter-matching ReactDOM UMD artifact; real Chrome/Electron and Windows/Bun remain unqualified.
2026-10-19 13:22:54 +08:00

99 lines
2.8 KiB
TypeScript

import { expect } from "vitest";
import { z } from "zod";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const capturedSource = `
fetch("/api/orders");
navigator.modelContext.registerTool({
name: "search_orders",
description: "Search orders",
inputSchema: {
type: "object",
properties: { query: { type: "string" } }
}
});
`;
cliTest(
"retains an aggregate web-analysis timeout and analyzes the next CLI request",
async ({ cli }) => {
const browser = await startFakeCdpBrowser({
commandResult: ({ method }) =>
method === "Debugger.getScriptSource"
? { scriptSource: capturedSource }
: undefined,
});
const baseArguments = [
"analyze-web-bundle",
browser.endpoint,
"allowed-page",
"--allowed-origins",
browser.allowedOrigin,
"--observation-ms",
"0",
"--json",
];
try {
const interrupted = await cli.run({
arguments: [
...baseArguments,
"--max-heap-mb",
"128",
"--analysis-timeout-ms",
"1",
"--total-analysis-timeout-ms",
"1",
],
timeoutMs: 30_000,
});
expect(
interrupted.exitCode,
interrupted.stdout + interrupted.stderr,
).toBe(1);
const timeout = z
.object({
code: z.literal("provider_timeout"),
details: z.object({ partial_observation: z.unknown() }),
})
.parse(interrupted.json);
const partial = parseEvidence(timeout.details.partial_observation);
expect(partial.parameters).toMatchObject({
max_heap_mb: 128,
analysis_timeout_ms: 1,
total_analysis_timeout_ms: 1,
});
expect(partial.normalized_result).toMatchObject({
completeness: { status: "partial", parse_failures: 0 },
unknowns: expect.arrayContaining([
expect.objectContaining({ affected_script_keys: expect.any(Array) }),
]),
});
const recovered = await cli.run({
arguments: baseArguments,
timeoutMs: 30_000,
});
expect(recovered.exitCode, recovered.stdout + recovered.stderr).toBe(0);
expect(recovered.json).toMatchObject({
normalized_result: {
observations: {
endpoints: [expect.objectContaining({ value: "/api/orders" })],
webmcp_declarations: [
expect.objectContaining({
name: "search_orders",
trust: "page-declared-untrusted",
}),
],
},
},
});
expect(browser.activeConnectionCount()).toBe(0);
} finally {
await browser.close();
}
},
);