Verify three repaired zlib mappings, a SQLite call guard and all ordinary 16-bit header values against retained native evidence. Record producer omissions, failed-run costs and the small-function context counterexample. Preserve existing capture outputs with exclusive writes. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
141 lines
4.3 KiB
JavaScript
141 lines
4.3 KiB
JavaScript
// Capture the existing public MCP function dossier, without changing its contract.
|
|
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { dirname, resolve } from "node:path";
|
|
import { Client } from "@modelcontextprotocol/client";
|
|
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
import {
|
|
requireMcpOperationResult,
|
|
requireMcpToolError,
|
|
} from "../../scripts/lib/mcp-verifier-results.mjs";
|
|
import {
|
|
createVerifierRun,
|
|
completeVerifierRun,
|
|
assertVerifierCleanupComplete,
|
|
} from "../../scripts/lib/verifier-run.mjs";
|
|
|
|
const [provider, binary, procedure, prefix] = process.argv.slice(2);
|
|
assert.ok(
|
|
["ghidra", "kuna"].includes(provider) && binary && procedure && prefix,
|
|
);
|
|
await mkdir(dirname(prefix), { recursive: true });
|
|
const verifier = createVerifierRun();
|
|
const client = new Client({
|
|
name: "rea-relation-experiment",
|
|
version: "0.1.0",
|
|
});
|
|
const transport = new StdioClientTransport({
|
|
command: process.execPath,
|
|
args: [resolve("dist/main.js")],
|
|
env: {
|
|
...process.env,
|
|
REA_LOG_LEVEL: "error",
|
|
REA_MCP_OUTPUT_SCHEMA_PROFILE: "off",
|
|
GHIDRA_HEADLESS_MAXMEM: "1G",
|
|
REA_GHIDRA_STARTUP_TIMEOUT_MS: "120000",
|
|
REA_KUNA_CACHE_DIR: resolve(`${prefix}.kuna-cache`),
|
|
REA_KUNA_JOBS: "1",
|
|
},
|
|
stderr: "pipe",
|
|
});
|
|
let stderr = "";
|
|
transport.stderr?.on("data", (part) => {
|
|
stderr = (stderr + part).slice(-32_768);
|
|
});
|
|
const wire = [],
|
|
calls = [],
|
|
failures = [];
|
|
let catalog;
|
|
const call = async (name, arguments_ = {}, acceptRetained = false) => {
|
|
const start = performance.now();
|
|
const response = await client.callTool(
|
|
{ name, arguments: arguments_ },
|
|
{ timeout: 180000 },
|
|
);
|
|
wire.push({ request: { name, arguments: arguments_ }, response });
|
|
calls.push({ name, elapsed_ms: performance.now() - start });
|
|
if (response.isError === true && acceptRetained) {
|
|
const error = requireMcpToolError(response);
|
|
const source = error.details?.reported_limits?.evidence_reference;
|
|
assert.equal(error.code, "resource_constraint");
|
|
assert.equal(source?.kind, "retained-evidence");
|
|
await call("inspect_analysis_view", {
|
|
source,
|
|
view: { kind: "native", facet: "value_flow_summary" },
|
|
});
|
|
return { retained: source, initial_error: error };
|
|
}
|
|
assert.notEqual(
|
|
response.isError,
|
|
true,
|
|
JSON.stringify(response).slice(0, 1600),
|
|
);
|
|
return requireMcpOperationResult(response, name);
|
|
};
|
|
try {
|
|
await client.connect(transport);
|
|
catalog = await client.listTools();
|
|
assert.ok(
|
|
catalog.tools.every((tool) => !Object.hasOwn(tool, "outputSchema")),
|
|
);
|
|
await call("open_binary", { path: resolve(binary), provider_id: provider });
|
|
await call("binary_session");
|
|
for (const phase of ["first", "warm"]) {
|
|
const dossier = await call("analyze_function", { procedure }, true);
|
|
await writeFile(`${prefix}.${phase}.json`, JSON.stringify(dossier), {
|
|
flag: "wx",
|
|
});
|
|
}
|
|
await call("export_evidence_bundle", {
|
|
path: resolve(`${prefix}.bundle.json`),
|
|
});
|
|
} catch (error) {
|
|
failures.push(String(error.stack ?? error));
|
|
} finally {
|
|
try {
|
|
await call("close_binary");
|
|
} catch (error) {
|
|
failures.push(String(error));
|
|
}
|
|
await client.close().catch((error) => failures.push(String(error)));
|
|
await transport.close().catch((error) => failures.push(String(error)));
|
|
}
|
|
const lifecycle = await completeVerifierRun(verifier);
|
|
let cleanupVerified = false;
|
|
try {
|
|
assertVerifierCleanupComplete(lifecycle.process_lineage);
|
|
cleanupVerified = true;
|
|
} catch (error) {
|
|
failures.push(String(error));
|
|
}
|
|
await writeFile(`${prefix}.wire.json`, JSON.stringify(wire), { flag: "wx" });
|
|
await writeFile(`${prefix}.catalog.json`, JSON.stringify(catalog), {
|
|
flag: "wx",
|
|
});
|
|
await writeFile(`${prefix}.stderr.txt`, stderr, { flag: "wx" });
|
|
const report = {
|
|
provider,
|
|
binary: resolve(binary),
|
|
procedure,
|
|
target_sha256: createHash("sha256")
|
|
.update(await readFile(binary))
|
|
.digest("hex"),
|
|
node_version: process.versions.node,
|
|
calls,
|
|
failures,
|
|
lifecycle,
|
|
};
|
|
await writeFile(`${prefix}.report.json`, JSON.stringify(report, null, 2), {
|
|
flag: "wx",
|
|
});
|
|
console.log(
|
|
JSON.stringify({
|
|
provider,
|
|
calls,
|
|
failures,
|
|
cleanup_verified: cleanupVerified,
|
|
}),
|
|
);
|
|
if (failures.length) process.exitCode = 1;
|