mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
a2d8b94dd5
Inspect bounded recorded raw HTTP/XML SAZ archives and page retained capture Evidence. Preserve exact stored bytes and provenance, validate ZIP/XML integrity, and require an enforceable decoder memory bound. Verified 356 real recorded sessions against independent byte and metadata oracles, public CLI/MCP parity, retained pages, and related interface checks. Current Fiddler Everywhere GUI and non-Linux producer workflows remain unverified. Co-authored-by: zahhak <1792039+zahhak@users.noreply.github.com> Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
142 lines
5.0 KiB
TypeScript
142 lines
5.0 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { truncate, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { expect, it } from "vitest";
|
|
|
|
import { NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES } from "../../../src/browser/history/CaptureRelease.js";
|
|
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../../src/domain/webNetworkCapture.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
const execute = promisify(execFile);
|
|
const cli = fileURLToPath(new URL("../../../scripts/rea.mjs", import.meta.url));
|
|
const entry = JSON.stringify({
|
|
startedDateTime: "2026-01-01T00:00:00.000Z",
|
|
time: 1,
|
|
request: {
|
|
method: "GET",
|
|
url: "https://example.invalid/data",
|
|
httpVersion: "HTTP/1.1",
|
|
cookies: [],
|
|
headers: [],
|
|
queryString: [],
|
|
headersSize: -1,
|
|
bodySize: 0,
|
|
},
|
|
response: {
|
|
status: 200,
|
|
statusText: "OK",
|
|
httpVersion: "HTTP/1.1",
|
|
cookies: [],
|
|
headers: [],
|
|
content: { size: 512, mimeType: "text/plain", text: "x".repeat(512) },
|
|
redirectURL: "",
|
|
headersSize: -1,
|
|
bodySize: 512,
|
|
},
|
|
cache: {},
|
|
timings: { send: 0, wait: 1, receive: 0 },
|
|
});
|
|
const capture = (count: number) =>
|
|
`{"log":{"version":"1.2","creator":{"name":"synthetic","version":"1"},"entries":[${Array<string>(count).fill(entry).join(",")}]}}`;
|
|
|
|
// Node.js enforces the decoder's V8 heap flags. Bun ignores them; on Windows
|
|
// the decoder's Job Object commit limit is the enforced bound instead, and
|
|
// Bun elsewhere has no bound this capture can reach.
|
|
const jobLimit = NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES;
|
|
it.skipIf(process.versions.bun !== undefined && jobLimit === null)(
|
|
"reports real HAR memory exhaustion through CLI and MCP and preserves server usability",
|
|
async () => {
|
|
const root = await createTestTempDirectory("rea-har-capacity-");
|
|
const path = join(root, "caller-marked.har");
|
|
const dense = capture(34_098);
|
|
expect(Buffer.byteLength(dense)).toBeLessThan(
|
|
WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
|
|
);
|
|
await writeFile(path, dense);
|
|
const { call, client } = await connectLocalToolsMcp();
|
|
const response = await call("inspect_web_network_capture", {
|
|
capture_path: path,
|
|
format: "har",
|
|
sensitive_values: ["caller-marked"],
|
|
});
|
|
expect(response.isError).toBe(true);
|
|
expect(parseMcpToolError(response)).toMatchObject({
|
|
error: {
|
|
code: "resource_constraint",
|
|
retryable: false,
|
|
message: expect.stringContaining(
|
|
jobLimit === null
|
|
? "fixed V8 heap"
|
|
: "Windows Job Object memory limit",
|
|
),
|
|
remediation: {
|
|
action: expect.stringContaining(
|
|
"smaller capture exported by its producer",
|
|
),
|
|
},
|
|
details: {
|
|
resource: "memory",
|
|
reported_limits:
|
|
jobLimit === null
|
|
? {
|
|
old_generation_heap_mib: 192,
|
|
observed_exit_code: process.platform === "win32" ? 134 : null,
|
|
observed_signal:
|
|
process.platform === "win32" ? null : "SIGABRT",
|
|
}
|
|
: { process_memory_limit_bytes: jobLimit, observed_signal: null },
|
|
captured_output:
|
|
jobLimit === null
|
|
? {
|
|
stderr: expect.stringContaining("heap out of memory"),
|
|
truncated: false,
|
|
}
|
|
: { truncated: false },
|
|
},
|
|
},
|
|
});
|
|
expect(JSON.stringify(response)).not.toContain("caller-marked");
|
|
expect(JSON.stringify(response)).not.toContain("rea doctor");
|
|
const failed: unknown = await execute(process.execPath, [
|
|
cli,
|
|
"inspect-web-network-capture",
|
|
path,
|
|
"har",
|
|
"--json",
|
|
]).then(
|
|
() => undefined,
|
|
(cause: unknown) => cause,
|
|
);
|
|
expect(failed).toMatchObject({ code: 1 });
|
|
const stdout =
|
|
typeof failed === "object" && failed !== null && "stdout" in failed
|
|
? String(failed.stdout)
|
|
: "";
|
|
expect(JSON.parse(stdout)).toMatchObject({ code: "resource_constraint" });
|
|
const small = join(root, "small.har");
|
|
await writeFile(small, capture(2));
|
|
await client.ping();
|
|
const control = await call("inspect_web_network_capture", {
|
|
capture_path: small,
|
|
format: "har",
|
|
});
|
|
expect(control.isError, JSON.stringify(control)).not.toBe(true);
|
|
expect(control.structuredContent).toMatchObject({
|
|
normalized_result: { total_records: 2 },
|
|
});
|
|
await truncate(path, WEB_NETWORK_CAPTURE_LIMITS.inputBytes + 1);
|
|
const oversized = await call("inspect_web_network_capture", {
|
|
capture_path: path,
|
|
format: "har",
|
|
});
|
|
expect(parseMcpToolError(oversized)).toMatchObject({
|
|
error: { code: "invalid_request" },
|
|
});
|
|
},
|
|
60_000,
|
|
);
|