Files
rea/scripts/verify-pcap.mjs
T
morluto 131fd5ddcd fix(filesystem): retain stable hash cleanup owners across callers
Preserve completed digests and primary failures when descriptor close fails.
Carry the exact handle owner through provider, readiness, cache, doctor and
verifier boundaries, and retry cleanup without rehashing or retrying a raw fd.
Close candidate-owned resources even when no target client was opened.
Keep verifier workspaces when provider shutdown remains incomplete.

Refs #4266
2026-10-16 06:46:25 +08:00

163 lines
5.6 KiB
JavaScript

import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { mkdtemp, readdir, rmdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { promisify } from "node:util";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { toolContract } from "../dist/contracts/toolContracts.js";
import { hashStableFile } from "../dist/filesystem/StableFileHash.js";
import { OwnedRegularFileCleanupFailure } from "../dist/filesystem/RegularFile.js";
import { mcpTextValue } from "./lib/mcp-verifier-results.mjs";
import { closeVerifierResources } from "./lib/verifier-cleanup.mjs";
const args = process.argv.slice(2);
if (args.includes("--help") || args.includes("-h")) {
console.log(
"Usage: npm run verify:pcap -- [--pcap PATH]\nRequires caller-supplied tshark/capinfos. Without --pcap, only client readiness is verified.",
);
process.exit(0);
}
if (!(args.length === 0 || (args.length === 2 && args[0] === "--pcap")))
throw new Error("Expected --pcap PATH");
const path = args[1] === undefined ? null : resolve(args[1]);
// This parent is shared by independent CLI/MCP owners. A leased Windows
// runtime root would prevent their native admission of its directory handle.
const root = await mkdtemp(join(tmpdir(), "rea-pcap-verifier-"));
const environment = Object.fromEntries(
Object.entries(process.env).filter(([, value]) => typeof value === "string"),
);
Object.assign(environment, {
TMPDIR: root,
TMP: root,
TEMP: root,
});
const entrypoint = resolve("scripts/rea.mjs");
const execute = promisify(execFile);
const client = new Client({ name: "pcap-real-verifier", version: "1" });
let primaryError;
const transport = new StdioClientTransport({
command: process.execPath,
args: [entrypoint, "mcp"],
env: environment,
stderr: "pipe",
});
const hashOwners = new Map();
const hashOwned = async (...args) => {
try {
return await hashStableFile(...args);
} catch (cause) {
if (cause instanceof OwnedRegularFileCleanupFailure)
hashOwners.set(cause.owner, cause.path);
throw cause;
}
};
transport.stderr?.resume();
try {
await client.connect(transport);
const readiness = await run("inspect_pcap_client", {});
assert(readiness.client.tshark_version && readiness.client.capinfos_version);
if (path === null)
console.log("[unverified] Capture inspection: no --pcap was supplied");
else {
const identity = await hashOwned(path);
const result = await run("inspect_pcap_capture", { path });
assert.deepEqual(result.target, { path, ...identity });
assert(result.packet_count >= 0);
assert(
result.endpoints.every(
(entry) => entry.packet_count <= result.packet_count,
),
);
assert(
result.protocol_distribution.every(
(entry) => entry.packet_count <= result.packet_count,
),
);
const partial =
result.endpoint_count > result.endpoints.length ||
result.dns_query_count > result.dns_queries.length ||
result.tls_sni_count > result.tls_sni_values.length ||
result.protocol_count > result.protocol_distribution.length ||
result.http_request_uri_count > result.http_request_uris.length ||
result.unattributed_dns_packet_count > 0 ||
result.dns_queries.some(
(entry) =>
entry.response_address_count > entry.response_addresses.length,
);
assert.equal(result.coverage, partial ? "partial" : "complete");
assert.deepEqual(
await hashOwned(path),
identity,
"Input capture must remain unchanged",
);
console.log(
`[ok] ${result.packet_count} packets; ${result.endpoint_count} addresses; coverage=${result.coverage}`,
);
}
} catch (cause) {
primaryError = cause;
throw cause;
} finally {
const cleanup = [];
for (const [owner, path] of hashOwners) {
cleanup.push(async () => {
try {
await owner.close();
hashOwners.delete(owner);
} catch (cause) {
throw new Error(`Could not close hash file handle for ${path}`, {
cause,
});
}
});
}
cleanup.push(async () => {
await closeVerifierResources([
() => client.close(),
() => transport.close(),
]);
assert.deepEqual(
await readdir(root),
[],
`Owned capture resources remain under ${root}`,
);
await rmdir(root);
});
await closeVerifierResources(cleanup, primaryError);
}
console.log("All asserted pcap CLI/MCP checks passed");
async function run(name, input) {
const contract = toolContract(name);
const { stdout } = await execute(
process.execPath,
[
entrypoint,
name.replaceAll("_", "-"),
...(input.path === undefined ? [] : [input.path]),
"--json",
],
{ env: environment, timeout: 360_000, maxBuffer: 8 * 1024 * 1024 },
);
const cli = contract.outputSchema.parse(JSON.parse(stdout));
const response = await client.callTool(
{ name, arguments: input },
{ timeout: 360_000 },
);
assert.notEqual(response.isError, true, mcpTextValue(response));
const mcp = contract.outputSchema.parse(JSON.parse(mcpTextValue(response)));
assert.deepEqual(mcp.normalized_result, cli.normalized_result);
assert.deepEqual(mcp.subject, cli.subject);
if (input.path !== undefined) {
assert.equal(cli.subject.local_path, input.path);
assert.equal(
cli.subject.digest.sha256,
cli.normalized_result.target.sha256,
);
}
console.log(`[ok] ${name}: named output schema and CLI/MCP Evidence parity`);
return cli.normalized_result;
}