fix(verifiers): preserve results and own cleanup through completion

Attempt independent resource cleanup without losing primary failures, retain unresolved owners, and publish completion after release. Bound existing producer commands, preserve helper return values, and settle startup and fixture children on failure.

Validation: 29 focused boundary tests passed; actual helper-value and import-bound filesystem cleanup controls passed. The real inert-child termination control and targeted JavaScript binding checks passed, alongside typecheck, lint and formatting. Live Ghidra, Cutter, Electron GUI and Windows addon qualification was not run.
This commit is contained in:
morluto committed 2026-11-10 17:03:59 -05:00
1 parent dcee59f273
commit 4082375e73
24 files changed
+3303 -2901

No files matched your search

+15 -8
View File
@@ -1,9 +1,9 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createPackageVerifierCommands } from "../../lib/verify-package-commands.mjs";
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
import { mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { promisify } from "node:util";
import {
artifactCliEvidence,
@@ -19,8 +19,6 @@ import { verifyDylibResolution } from "./macos-dylib-e2e.mjs";
import { verifyMachOOccurrences } from "./macho-occurrences.mjs";
import { verifyNativeProvenance } from "./native-provenance.mjs";
const exec = promisify(execFile);
await preflightMacosBundleFixture();
const root = await mkdtemp(join(tmpdir(), "rea-macos-bundle-"));
@@ -67,8 +65,19 @@ const EXPECTED_BUNDLES = [
["Contents/XPCServices/Svc.xpc", "xpc-service", "macos-deep", "."],
];
const commands = await createPackageVerifierCommands({
workspace: {
path: root,
close: () => rm(root, { recursive: true, force: true }),
},
});
const exec = (command, args) =>
commands.exec(command, args, {
timeout: 120_000,
maxBuffer: 16 * 1024 * 1024,
});
let report;
try {
await withVerifierCleanup(async () => {
const { app, executable } = await buildMacosBundleFixture(root);
const nativeProvenance = await verifyNativeProvenance(app, executable);
const machoOccurrences = await verifyMachOOccurrences(app, executable);
@@ -173,7 +182,5 @@ try {
dylib_resolution: dylibs,
dyld_environment: dyldEnvironment,
};
} finally {
await rm(root, { recursive: true, force: true });
}
}, [() => commands.close()]);
process.stdout.write(`${JSON.stringify(report)}\n`);
+15 -8
View File
@@ -1,10 +1,10 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createPackageVerifierCommands } from "../../lib/verify-package-commands.mjs";
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { promisify } from "node:util";
import { toolContract } from "../../../dist/contracts/toolContracts.js";
import {
@@ -14,8 +14,6 @@ import {
withBundleResourcesMcp,
} from "../../lib/artifact-e2e.mjs";
const exec = promisify(execFile);
// dyld_info abbreviates a terminal .dylib suffix; padding is part of the name.
const INSTALL_NAME_CASES = [
{ installName: "leading.dylib", importSource: "leading" },
@@ -182,7 +180,18 @@ export async function verifyNativeProvenance(app, executable) {
async function verifyInstallNames() {
const root = await mkdtemp(join(tmpdir(), "rea-macho-install-names-"));
const observations = [];
try {
const commands = await createPackageVerifierCommands({
workspace: {
path: root,
close: () => rm(root, { recursive: true, force: true }),
},
});
const exec = (command, args) =>
commands.exec(command, args, {
timeout: 120_000,
maxBuffer: 16 * 1024 * 1024,
});
return withVerifierCleanup(async () => {
const librarySource = join(root, "library.c");
const consumerSource = join(root, "consumer.c");
await writeFile(librarySource, "int exported_value(void) { return 42; }\n");
@@ -279,7 +288,5 @@ async function verifyInstallNames() {
});
}
return observations;
} finally {
await rm(root, { recursive: true, force: true });
}
}, [() => commands.close()]);
}
+242 -203
View File
@@ -1,3 +1,7 @@
import {
closeVerifierResources,
withVerifierCleanup,
} from "../../lib/verifier-cleanup.mjs";
import { requireMcpToolError } from "../../lib/mcp-verifier-results.mjs";
import assert from "node:assert/strict";
import { execFile, spawn } from "node:child_process";
@@ -92,207 +96,221 @@ bridge.exposeInMainWorld("fixture", {
});
let application;
let stderr = "";
try {
await readProcessRunId(process.pid);
application = spawn(
executable,
[
"--remote-debugging-address=127.0.0.1",
"--remote-debugging-port=0",
`--user-data-dir=${join(root, "profile")}`,
main,
],
{
cwd: root,
env: { ...process.env, REA_PROCESS_RUN_ID: runId },
detached: true,
stdio: ["ignore", "ignore", "pipe"],
},
);
application.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
const endpoint = await waitForEndpoint(application, () => stderr);
await client.connect(transport);
const ajv = new Ajv2020({ strict: false, validateFormats: false });
const validators = new Map(
(await client.listTools()).tools.map((tool) => {
ajv.compile(tool.inputSchema);
return [
tool.name,
tool.outputSchema
? (tool.outputSchema.$id && ajv.getSchema(tool.outputSchema.$id)) ||
ajv.compile(tool.outputSchema)
: undefined,
];
}),
);
const call = async (name, arguments_) => {
console.log(`PROBE ${name}`);
const result = await client.callTool(
{ name, arguments: arguments_ },
await withVerifierCleanup(async () => {
try {
await readProcessRunId(process.pid);
application = spawn(
executable,
[
"--remote-debugging-address=127.0.0.1",
"--remote-debugging-port=0",
`--user-data-dir=${join(root, "profile")}`,
main,
],
{
cwd: root,
env: { ...process.env, REA_PROCESS_RUN_ID: runId },
detached: true,
stdio: ["ignore", "ignore", "pipe"],
},
);
application.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
const endpoint = await waitForEndpoint(application, () => stderr);
await client.connect(transport);
const ajv = new Ajv2020({ strict: false, validateFormats: false });
const validators = new Map(
(await client.listTools()).tools.map((tool) => {
ajv.compile(tool.inputSchema);
return [
tool.name,
tool.outputSchema
? (tool.outputSchema.$id &&
ajv.getSchema(tool.outputSchema.$id)) ||
ajv.compile(tool.outputSchema)
: undefined,
];
}),
);
const call = async (name, arguments_) => {
console.log(`PROBE ${name}`);
const result = await client.callTool(
{ name, arguments: arguments_ },
{ timeout: 150_000 },
);
assert.notEqual(result.isError, true, JSON.stringify(result));
const validate = validators.get(name);
assert.ok(
validate?.(result.structuredContent),
JSON.stringify(validate?.errors),
);
return result.structuredContent;
};
let targets;
for (let attempt = 0; attempt < 30; attempt++) {
targets = await call("list_electron_targets", {
cdp_endpoint: endpoint,
});
if (targets.normalized_result.targets.length > 0) break;
await pause(100);
}
const target = targets.normalized_result.targets.find(
(item) => item.file_path === join(root, "renderer.html"),
);
assert.ok(target, JSON.stringify(targets));
const page = await call("inspect_electron_page", {
cdp_endpoint: endpoint,
target_id: target.target_id,
include_script_sources: true,
observation_ms: 50,
});
assert.ok(
page.normalized_result.scripts.items.some(
(script) =>
script.file_path === join(root, "renderer.js") &&
script.source.included,
),
);
const staticAnalysis = await call("analyze_javascript_application", {
input_path: root,
});
assert.equal(staticAnalysis.normalized_result.summary.browser_windows, 1);
assert.equal(
staticAnalysis.normalized_result.summary.preload_entrypoints,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.context_bridge_apis,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.ipc.main_handlers,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.ipc.renderer_transmissions,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.ipc
.paired_renderer_transmissions,
1,
);
const reconciled = await call("reconcile_javascript_runtime", {
static_layers: [
{
analysis: staticAnalysis,
runtime_mappings: [{ kind: "file-root", root }],
},
],
runtime_observations: [page],
});
assert.ok(reconciled.normalized_result.runtime_captures.length > 0);
const cli = await execute(
process.execPath,
[
entrypoint,
"inspect-electron-page",
endpoint,
target.target_id,
"--include-script-sources",
"--json",
],
{
env: { ...process.env, REA_LOG_LEVEL: "silent" },
timeout: 60_000,
maxBuffer: 32 * 1024 * 1024,
},
);
assert.equal(
JSON.parse(cli.stdout).normalized_result.target.file_path,
target.file_path,
);
const input = {
executable_path: executable,
application_path: main,
application_root: root,
actions: [{ step_id: "ipc", kind: "click", selector: "#run" }],
};
const active = await client.callTool(
{ name: "capture_electron_scenario", arguments: input },
{ timeout: 150_000 },
);
assert.notEqual(result.isError, true, JSON.stringify(result));
const validate = validators.get(name);
const activeError = active.isError
? requireMcpToolError(active)
: undefined;
if (activeError !== undefined)
assert.equal(
activeError.code,
"cleanup_incomplete",
JSON.stringify(activeError),
);
const capture =
activeError === undefined
? active.structuredContent.normalized_result
: activeError.details?.partial_observation?.capture;
assert.ok(capture, JSON.stringify(activeError ?? active));
if (activeError !== undefined) {
assert.equal(capture.application.cleanup, "unverified");
} else {
assert.ok(
validators.get("capture_electron_scenario")(active.structuredContent),
);
assert.equal(capture.application.cleanup, "terminated-owned-process");
}
assert.equal(capture.actions[0].status, "completed");
assert.ok(
validate?.(result.structuredContent),
JSON.stringify(validate?.errors),
capture.ipc.events.some(
({ kind, channel }) =>
kind === "main-handler-invocation" && channel === "fixture:echo",
),
JSON.stringify(capture.ipc),
);
return result.structuredContent;
};
let targets;
for (let attempt = 0; attempt < 30; attempt++) {
targets = await call("list_electron_targets", { cdp_endpoint: endpoint });
if (targets.normalized_result.targets.length > 0) break;
await pause(100);
}
const target = targets.normalized_result.targets.find(
(item) => item.file_path === join(root, "renderer.html"),
);
assert.ok(target, JSON.stringify(targets));
const page = await call("inspect_electron_page", {
cdp_endpoint: endpoint,
target_id: target.target_id,
include_script_sources: true,
observation_ms: 50,
});
assert.ok(
page.normalized_result.scripts.items.some(
(script) =>
script.file_path === join(root, "renderer.js") &&
script.source.included,
),
);
const staticAnalysis = await call("analyze_javascript_application", {
input_path: root,
});
assert.equal(staticAnalysis.normalized_result.summary.browser_windows, 1);
assert.equal(
staticAnalysis.normalized_result.summary.preload_entrypoints,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.context_bridge_apis,
1,
);
assert.equal(staticAnalysis.normalized_result.summary.ipc.main_handlers, 1);
assert.equal(
staticAnalysis.normalized_result.summary.ipc.renderer_transmissions,
1,
);
assert.equal(
staticAnalysis.normalized_result.summary.ipc
.paired_renderer_transmissions,
1,
);
const reconciled = await call("reconcile_javascript_runtime", {
static_layers: [
{
analysis: staticAnalysis,
runtime_mappings: [{ kind: "file-root", root }],
},
],
runtime_observations: [page],
});
assert.ok(reconciled.normalized_result.runtime_captures.length > 0);
const cli = await execute(
process.execPath,
[
entrypoint,
"inspect-electron-page",
endpoint,
target.target_id,
"--include-script-sources",
"--json",
],
{
env: { ...process.env, REA_LOG_LEVEL: "silent" },
timeout: 60_000,
maxBuffer: 32 * 1024 * 1024,
},
);
assert.equal(
JSON.parse(cli.stdout).normalized_result.target.file_path,
target.file_path,
);
const input = {
executable_path: executable,
application_path: main,
application_root: root,
actions: [{ step_id: "ipc", kind: "click", selector: "#run" }],
};
const active = await client.callTool(
{ name: "capture_electron_scenario", arguments: input },
{ timeout: 150_000 },
);
const activeError = active.isError
? requireMcpToolError(active)
: undefined;
if (activeError !== undefined)
assert.equal(
activeError.code,
"cleanup_incomplete",
JSON.stringify(activeError),
capture.coverage.status,
"partial_attach",
JSON.stringify(capture),
);
const capture =
activeError === undefined
? active.structuredContent.normalized_result
: activeError.details?.partial_observation?.capture;
assert.ok(capture, JSON.stringify(activeError ?? active));
if (activeError !== undefined) {
assert.equal(capture.application.cleanup, "unverified");
} else {
assert.ok(
validators.get("capture_electron_scenario")(active.structuredContent),
capture.timeline.events.some(
(event) =>
event.kind === "window-lifecycle" && event.event === "created",
),
);
await client.ping();
console.log(
`PASS live Electron CLI/MCP discovery, inspection, reconciliation, and active observations; active cleanup=${capture.application.cleanup}`,
);
if (process.platform !== "win32")
await verifyCancellation({ input, ready, entrypoint, root });
} catch (cause) {
throw new Error(
`Electron public-tool verification failed: ${cause.message}\nMCP stderr:\n${serverStderr}`,
{ cause },
);
assert.equal(capture.application.cleanup, "terminated-owned-process");
}
assert.equal(capture.actions[0].status, "completed");
assert.ok(
capture.ipc.events.some(
({ kind, channel }) =>
kind === "main-handler-invocation" && channel === "fixture:echo",
),
JSON.stringify(capture.ipc),
);
assert.equal(
capture.coverage.status,
"partial_attach",
JSON.stringify(capture),
);
assert.ok(
capture.timeline.events.some(
(event) =>
event.kind === "window-lifecycle" && event.event === "created",
),
);
await client.ping();
console.log(
`PASS live Electron CLI/MCP discovery, inspection, reconciliation, and active observations; active cleanup=${capture.application.cleanup}`,
);
if (process.platform !== "win32")
await verifyCancellation({ input, ready, entrypoint, root });
} catch (cause) {
throw new Error(
`Electron public-tool verification failed: ${cause.message}\nMCP stderr:\n${serverStderr}`,
{ cause },
);
} finally {
await client.close();
await transport.close();
if (application?.pid) {
const cleanup = await cleanupOwnedProcessGroup({
runId,
leaderPid: application.pid,
processGroupId: application.pid,
expectedParentPid: process.pid,
expectedCommand: executable,
});
assert.equal(cleanup.cleaned, true, JSON.stringify(cleanup));
}
await rm(root, { recursive: true, force: true });
}
}, [
async () => {
await closeVerifierResources([
() => client.close(),
() => transport.close(),
async () => {
if (application?.pid) {
const cleanup = await cleanupOwnedProcessGroup({
runId,
leaderPid: application.pid,
processGroupId: application.pid,
expectedParentPid: process.pid,
expectedCommand: executable,
});
assert.equal(cleanup.cleaned, true, JSON.stringify(cleanup));
}
},
]);
await rm(root, { recursive: true, force: true });
},
]);
}
const pause = (milliseconds) =>
@@ -345,7 +363,7 @@ async function verifyCancellation({ input, ready, entrypoint, root }) {
child.once("exit", (code, signal) => resolve({ code, signal }));
});
let ownership;
try {
await withVerifierCleanup(async () => {
const deadline = Date.now() + 30_000;
while (Date.now() < deadline) {
const table = (
@@ -419,14 +437,35 @@ async function verifyCancellation({ input, ready, entrypoint, root }) {
console.log(
`PASS real Electron CLI SIGTERM, retained cancelled actions, and owned group termination; reported code=${error.code}`,
);
} finally {
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGTERM");
await exited;
}
if (ownership) {
const cleanup = await cleanupOwnedProcessGroup(ownership);
assert.equal(cleanup.cleaned, true, JSON.stringify(cleanup));
}
}
}, [
async () => {
for (const signal of ["SIGTERM", "SIGKILL"]) {
if (child.exitCode !== null || child.signalCode !== null) return;
child.kill(signal);
let timer;
try {
if (
await Promise.race([
exited.then(() => true),
new Promise((resolve) => {
timer = setTimeout(() => resolve(false), 2000);
}),
])
)
return;
} finally {
clearTimeout(timer);
}
}
throw new Error(
"Electron verifier CLI did not exit after SIGKILL; application cleanup remains independently owned",
);
},
async () => {
if (ownership) {
const cleanup = await cleanupOwnedProcessGroup(ownership);
assert.equal(cleanup.cleaned, true, JSON.stringify(cleanup));
}
},
]);
}
+27 -18
View File
@@ -1,17 +1,29 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { createPackageVerifierCommands } from "../../lib/verify-package-commands.mjs";
import { withVerifierCleanup } from "../../lib/verifier-cleanup.mjs";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
// Optional verification tool only; the runtime inspector never starts dtc.
const dtc = process.env.REA_DTC_PATH ?? "dtc";
assert.match(
execFileSync(dtc, ["--version"], { encoding: "utf8" }),
/DTC 1\.8\.1\b/,
);
const root = mkdtempSync(join(tmpdir(), "rea-dtb-verification-"));
try {
const commands = await createPackageVerifierCommands({
workspace: {
path: root,
close: async () => rmSync(root, { recursive: true, force: true }),
},
});
const exec = async (command, args) =>
(
await commands.exec(command, args, {
timeout: 120_000,
maxBuffer: 16 * 1024 * 1024,
})
).stdout;
await withVerifierCleanup(async () => {
assert.match(await exec(dtc, ["--version"]), /DTC 1\.8\.1\b/);
const source = join(root, "board.dts"),
blob = join(root, "board.dtb");
writeFileSync(
@@ -30,16 +42,15 @@ try {
consumer { remote-endpoint = <&serial>; };
};\n`,
);
execFileSync(dtc, ["-I", "dts", "-O", "dtb", "-o", blob, source]);
const decompiled = execFileSync(dtc, ["-I", "dtb", "-O", "dts", blob], {
encoding: "utf8",
});
await exec(dtc, ["-I", "dts", "-O", "dtb", "-o", blob, source]);
const decompiled = await exec(dtc, ["-I", "dtb", "-O", "dts", blob]);
const evidence = JSON.parse(
execFileSync(
process.execPath,
["scripts/rea.mjs", "inspect-devicetree", blob, "--json"],
{ encoding: "utf8" },
),
await exec(process.execPath, [
"scripts/rea.mjs",
"inspect-devicetree",
blob,
"--json",
]),
);
const report = evidence.normalized_result;
// This controlled fixture uses one opening/closing brace per node line.
@@ -83,6 +94,4 @@ try {
2,
),
);
} finally {
rmSync(root, { recursive: true, force: true });
}
}, [() => commands.close()]);
@@ -1,3 +1,7 @@
import {
closeVerifierResources,
withVerifierCleanup,
} from "../../lib/verifier-cleanup.mjs";
import assert from "node:assert/strict";
import { execFile, spawn } from "node:child_process";
import { once } from "node:events";
@@ -38,7 +42,7 @@ const names = [
...(process.platform === "win32" ? [] : ['with"quote.cjs']),
];
try {
await withVerifierCleanup(async () => {
await client.connect(transport);
for (const name of names) {
const targetPath =
@@ -54,19 +58,20 @@ try {
stdio: ["ignore", "ignore", "pipe"],
},
);
try {
await verifyTarget(child, targetPath, name);
} finally {
await terminate(child);
}
await withVerifierCleanup(
() => verifyTarget(child, targetPath, name),
[() => terminate(child)],
);
}
} finally {
try {
await client.close();
} finally {
}, [
async () => {
await closeVerifierResources([
() => client.close(),
() => transport.close(),
]);
await rm(root, { recursive: true, force: true });
}
}
},
]);
async function verifyTarget(child, targetPath, name) {
const socket = new URL(await inspectorUrl(child));
@@ -192,15 +197,37 @@ async function runMcp(name, arguments_) {
}
async function inspectorUrl(process_) {
let captured = "";
for await (const chunk of process_.stderr) {
captured += chunk.toString("utf8");
const match = /Debugger listening on (ws:\/\/[^\s]+)/u.exec(captured);
if (match?.[1] !== undefined) return match[1];
if (captured.length > 64 * 1_024)
throw new Error("Inspector startup output exceeded its bound");
}
throw new Error("Node exited before reporting its Inspector URL");
return new Promise((resolveUrl, rejectUrl) => {
let captured = "";
const timer = setTimeout(
() =>
finish(new Error("Inspector startup exceeded its 10000ms deadline")),
10_000,
);
const finish = (error, url) => {
clearTimeout(timer);
process_.stderr.off("data", onData);
process_.stderr.off("end", onEnd);
process_.stderr.off("error", onError);
process_.off("error", onError);
if (error !== undefined) rejectUrl(error);
else resolveUrl(url);
};
const onData = (chunk) => {
captured += chunk.toString("utf8");
const match = /Debugger listening on (ws:\/\/[^\s]+)/u.exec(captured);
if (match?.[1] !== undefined) finish(undefined, match[1]);
else if (captured.length > 64 * 1_024)
finish(new Error("Inspector startup output exceeded its bound"));
};
const onEnd = () =>
finish(new Error("Node exited before reporting its Inspector URL"));
const onError = (error) => finish(error);
process_.stderr.on("data", onData);
process_.stderr.once("end", onEnd);
process_.stderr.once("error", onError);
process_.once("error", onError);
});
}
async function runCli(arguments_) {
@@ -236,7 +263,14 @@ async function terminate(process_) {
)
return;
process_.kill("SIGKILL");
await exited;
clearTimeout(timer);
const killed = await Promise.race([
exited.then(() => true),
new Promise((resolveTimeout) => {
timer = setTimeout(() => resolveTimeout(false), 2_000);
}),
]);
assert.equal(killed, true, "Inspector fixture did not exit after SIGKILL");
} finally {
clearTimeout(timer);
}
+14 -6
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env node
import {
closeVerifierResources,
withVerifierCleanup,
} from "../../lib/verifier-cleanup.mjs";
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
@@ -51,7 +55,7 @@ const call = async (name, args) => {
const fingerprint = (bytes) => createHash("sha256").update(bytes).digest("hex");
const counts = [];
let finalReport;
try {
await withVerifierCleanup(async () => {
const fixtures = await buildRecoveryFixtures(
root,
process.env.REA_JAVASCRIPT_FIXTURE_TOOLS,
@@ -165,11 +169,15 @@ try {
results: counts,
verified: true,
};
} finally {
await client.close();
await transport.close();
await rm(root, { recursive: true, force: true });
}
}, [
async () => {
await closeVerifierResources([
() => client.close(),
() => transport.close(),
]);
await rm(root, { recursive: true, force: true });
},
]);
console.log(JSON.stringify(finalReport));
async function assertRecoveredArtifacts(evidence, result, fixture) {