test: strengthen and prune audited test coverage (#8573)

* test: strengthen and prune audited test coverage

* test: require delivered DeepSeek skill instructions

* test: keep schema fixture types private

* test: verify Gemini receives skill instructions

* test: bind Qwen skill evidence to its tool result

* test: report NativeAOT verifier checks precisely

* fix: verify Ghidra cleanup paths reliably

* fix: verify Command Code skill activation

* test: tighten cleanup and fixture checks

* test: verify packet capture evidence is recorded

* fix: report HTTP3 capture provenance accurately

* test: preserve resource coverage through MCP

* test: prune repeated assertions and retain cancellation identity

* fix: validate client verifier fixture paths

* test: skip Copilot verifier cases on Windows

* test: keep verifier setup target relative
This commit is contained in:
morluto authored and GitHub committed 2026-11-02 02:13:32 +08:00
1 parent ce9095d727
commit 7bcefead69
167 files changed
+2396 -1012

No files matched your search

+4 -4
View File
@@ -257,12 +257,12 @@ beyond already-connected devices (`adb connect`) are not exposed yet.
## Real verification
`npm run verify:adb` exercises every operation against a live device. It
`npm run verify:adb` exercises observation and transfer operations against a live device. It
requires `adb` (or `REA_ADB_PATH`) and any attached emulator or phone;
`--serial` selects one when several are attached, `--pull PACKAGE` pulls a
real package and verifies the returned digests against the files on disk,
and `--install-apk PATH` drives a full install → resolve → uninstall
lifecycle with a lane-owned probe.
real package and verifies the returned digests against the files on disk.
The optional `--install-apk` lifecycle is unavailable until the selected APK
identity and device cleanup ownership can be established safely.
Record: verified against adb 34.0.5 on Linux against an Android 14
(API 34) x86_64 emulator — all operations above, a 224-package inventory,
+5 -5
View File
@@ -1423,12 +1423,12 @@ budgets.
## ADB device acquisition
`npm run verify:adb` exercises every ADB operation against a live device or
`npm run verify:adb` exercises ADB observation and transfer operations against a live device or
emulator through the caller's adb binary (`REA_ADB_PATH` or PATH). No SDK
installation or emulator management is involved; the device-mutating tools
run only in the explicit lifecycle section (`--install-apk` drives a
lane-owned install → resolve → start → observe → force-stop → uninstall
probe). `--pull` acquires a real package and re-digests the pulled files on
installation or emulator management is involved. `--install-apk` is unavailable
until the selected APK identity and device cleanup ownership can be established
safely. The transfer probe creates and removes a temporary device file. `--pull`
acquires a real package and re-digests the pulled files on
disk against the returned SHA-256 values; `--serial` selects a device when
several are attached.
+5
View File
@@ -10,5 +10,10 @@ export function compareTestDiscovery(
readonly file: string;
readonly projects: readonly string[];
}[];
readonly wrongProjects: readonly {
readonly file: string;
readonly expected: string;
readonly actual: string;
}[];
readonly discovered: number;
};
+9
View File
@@ -1,4 +1,5 @@
import { relative, resolve } from "node:path";
import { projectForTest } from "./test-projects.mjs";
/** Compare actual Vitest discovery with repository-owned test paths. */
export const compareTestDiscovery = (expectedPaths, discovery, root) => {
@@ -36,6 +37,14 @@ export const compareTestDiscovery = (expectedPaths, discovery, root) => {
const projects = projectsByFile.get(file);
return projects.length > 1 ? [{ file, projects: sorted(projects) }] : [];
}),
wrongProjects: sorted(expected).flatMap((file) => {
const projects = projectsByFile.get(file);
if (!projects || projects.length !== 1) return [];
const expectedProject = projectForTest(file).name;
return projects[0] === expectedProject
? []
: [{ file, expected: expectedProject, actual: projects[0] }];
}),
discovered: projectsByFile.size,
};
};
+4 -2
View File
@@ -539,11 +539,13 @@ class WebsiteMaintenanceChecks(unittest.TestCase):
self.assertIn(b"color: black", response.read())
with self.assertRaises(HTTPError) as error:
urlopen(base + prefix + "missing")
self.assertEqual(error.exception.code, 404)
with contextlib.closing(error.exception) as response:
self.assertEqual(response.code, 404)
if prefix != "/":
with self.assertRaises(HTTPError) as error:
urlopen(base + "/guide/")
self.assertEqual(error.exception.code, 404)
with contextlib.closing(error.exception) as response:
self.assertEqual(response.code, 404)
finally:
server.shutdown()
worker.join()
+10 -78
View File
@@ -15,8 +15,10 @@ if (options.help) {
process.stdout.write(
"Usage: npm run verify:adb [-- --serial SERIAL --pull PACKAGE]\n" +
"Requires a caller-selected adb binary (REA_ADB_PATH or PATH) and a\n" +
"connected device or emulator. Verifies all five operations against\n" +
"the live device; --pull acquires a real package and re-digests the\n" +
"connected device or emulator. Verifies observation and transfer operations\n" +
"against the live device. --install-apk is unavailable until APK identity\n" +
"and device cleanup ownership can be established safely.\n" +
"--pull acquires a real package and re-digests the\n" +
"files on disk against the returned SHA-256 values.\n",
);
process.exit(0);
@@ -356,79 +358,7 @@ try {
`versionCode=${String(details.version_code)}`,
);
}
if (options.installApk !== null && options.installApk !== undefined) {
const probePackage = "com.rea.adb.probe";
const installedProbe = await run("install_adb_package", {
serial,
apk_path: options.installApk,
replace: false,
});
if (installedProbe !== null) {
check(
"install reports the APK digest",
typeof installedProbe.sha256 === "string" &&
installedProbe.sha256.length === 64,
`${installedProbe.bytes} bytes`,
);
const resolved = await run("resolve_adb_packages", {
serial,
query: "rea.adb.probe",
});
if (resolved !== null)
check(
"resolve finds the installed probe uniquely",
resolved.matches.length === 1 &&
resolved.matches[0]?.package_name === probePackage,
`${resolved.matches.length} match(es)`,
);
const started = await run("start_adb_app", {
serial,
package: probePackage,
});
if (started !== null)
check(
"app start resolves and launches the launcher activity",
typeof started.component === "string" &&
started.component.startsWith(probePackage),
String(started.component),
);
const observedProcesses = await run("list_adb_processes", { serial });
if (observedProcesses !== null)
check(
"started app appears in the process listing",
observedProcesses.processes.some(
(process) => process.name === probePackage,
),
);
const stopped = await run("stop_adb_app", {
serial,
package: probePackage,
});
if (stopped !== null)
check(
"force-stop completes the dynamic-analysis run",
stopped.package_name === probePackage,
);
const removed = await run("uninstall_adb_package", {
serial,
package: probePackage,
});
if (removed !== null)
check(
"uninstall completes the lifecycle",
removed.package_name === probePackage,
);
const afterRemoval = await run("resolve_adb_packages", {
serial,
query: "rea.adb.probe",
});
if (afterRemoval !== null)
check(
"resolve reports zero matches after removal",
afterRemoval.matches.length === 0,
);
}
}
} finally {
await rm(transferRoot, { recursive: true, force: true });
}
@@ -459,7 +389,7 @@ if (failures > 0) {
}
function parseArguments(argv) {
const parsed = { help: false, serial: null, pull: null, installApk: null };
const parsed = { help: false, serial: null, pull: null };
for (let index = 0; index < argv.length; index += 1) {
const argument = argv[index];
if (argument === "--help" || argument === "-h") parsed.help = true;
@@ -470,8 +400,10 @@ function parseArguments(argv) {
parsed.pull = argv[index + 1] ?? null;
index += 1;
} else if (argument === "--install-apk") {
parsed.installApk = argv[index + 1] ?? null;
index += 1;
process.stderr.write(
"--install-apk is unavailable: APK identity and device cleanup ownership cannot be established safely\n",
);
process.exit(1);
} else {
process.stderr.write(`unknown argument: ${argument}\n`);
process.exit(2);
+3 -2
View File
@@ -8,7 +8,7 @@ import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
const root = await PrivateRuntimeRoot.create({ prefix: "rea-bun-verifier-" });
const commands = await createVerifierCommands();
let commands;
const run = (command, args, options = {}) =>
commands.exec(command, args, {
timeout: 120_000,
@@ -19,6 +19,7 @@ const run = (command, args, options = {}) =>
// only after every owned command family has settled.
let commandsClosed = false;
await withVerifierCleanup(async () => {
commands = await createVerifierCommands();
const source = join(root.path, "hello.ts");
await writeFile(source, 'console.log("REA_BUN_VERIFICATION");\n');
const docker = process.argv.includes("--docker");
@@ -104,7 +105,7 @@ await withVerifierCleanup(async () => {
);
}, [
async () => {
await commands.close();
if (commands !== undefined) await commands.close();
commandsClosed = true;
},
async () => {
+5 -2
View File
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { isAbsolute, join } from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import { createCatalogIdentity } from "../dist/catalogIdentity.js";
@@ -15,6 +15,7 @@ import {
TOOL_CONTRACTS,
toolContract,
} from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
@@ -306,7 +307,9 @@ await withVerifierCleanup(async () => {
assert(
parsed.plannedActions.every(
(a) =>
a.target === configPath || a.target.startsWith(skillDirectory),
a.target === configPath ||
(isAbsolute(a.target) &&
isPathWithinRoot(skillDirectory, a.target)),
),
);
}
+31 -8
View File
@@ -6,6 +6,7 @@ import { isAbsolute, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
@@ -54,6 +55,7 @@ const expectedNames = TOOL_CONTRACTS.map(
).sort();
let clientVersion = null;
let expectedSkill;
let skillCallId;
let inventoryChecked = false;
let catalogSize = null;
let skillLoaded = false;
@@ -180,10 +182,24 @@ await withVerifierCleanup(async () => {
finalSeen = true;
return { role: "assistant", content: "REA_CLIENT_COMPATIBILITY_OK" };
}
if (stage === 0)
return call("activate_skill", { name: "reverse-engineer-anything" });
if (stage === 0) {
const request = call("activate_skill", {
name: "reverse-engineer-anything",
});
skillCallId = request.tool_calls[0].id;
return request;
}
assert(
strings.some((text) => text.includes(expectedSkill)),
skillCallId !== undefined &&
expectedSkill !== undefined &&
body.messages.some(
(message) =>
message.role === "tool" &&
message.tool_call_id === skillCallId &&
textValues(message.content).some((text) =>
text.includes(expectedSkill),
),
),
"The model must receive the complete activated skill body",
);
skillLoaded = true;
@@ -322,8 +338,8 @@ await withVerifierCleanup(async () => {
result.plannedActions.every(
(action) =>
action.target === configPath ||
action.target === skillDirectory ||
action.target.startsWith(skillDirectory + "/"),
(isAbsolute(action.target) &&
isPathWithinRoot(skillDirectory, action.target)),
),
"Refuse setup writes outside the disposable account's configuration and skill",
);
@@ -342,9 +358,16 @@ await withVerifierCleanup(async () => {
assert.deepEqual(repeated.appliedActions, []);
assert.equal(await readFile(configPath, "utf8"), configured);
assert.equal(await readFile(`${configPath}.rea.backup`, "utf8"), original);
expectedSkill = (await readFile(join(skillDirectory, "SKILL.md"), "utf8"))
.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "")
.trim();
const installedSkill = await readFile(
join(skillDirectory, "SKILL.md"),
"utf8",
);
const frontmatter = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/u.exec(
installedSkill,
);
assert(frontmatter, "Installed skill must have authored frontmatter");
expectedSkill = installedSkill.slice(frontmatter[0].length).trim();
assert(expectedSkill.length > 0, "Installed skill body must not be empty");
const response = await saveVerifierCommandOutput(
() =>
commands.exec(
+3 -2
View File
@@ -12,6 +12,7 @@ import {
import { advertisedInputJsonSchema } from "../dist/contracts/toolSchemaMetadata.js";
import { presentAdvertisedInputSchema } from "../dist/contracts/compactInputSchema.js";
import { compactPresentationForMcpInputSchemaProfile } from "../dist/config/mcpInputSchemaProfile.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
@@ -300,8 +301,8 @@ await withVerifierCleanup(async () => {
parsed.plannedActions.every(
(a) =>
a.target === configPath ||
a.target === skillDirectory ||
a.target.startsWith(skillDirectory + "/"),
(isAbsolute(a.target) &&
isPathWithinRoot(skillDirectory, a.target)),
),
);
}
+37 -9
View File
@@ -56,6 +56,7 @@ let catalog;
let patternCount = 0;
let skillRequested = false;
let skillLoaded = false;
let expectedSkillBody;
let evidenceSeen = false;
let finalSeen = false;
let artifactDigest;
@@ -169,11 +170,34 @@ await withVerifierCleanup(async () => {
const toolMessages = (body.messages ?? []).filter(
(message) => message.role === "tool",
);
const serialized = JSON.stringify(toolMessages);
skillLoaded ||=
serialized.includes(
'<skill_content name=\\"reverse-engineer-anything\\">',
) && serialized.includes(dirname(skillPath));
skillLoaded ||= toolMessages.some((message) => {
if (
typeof message.content !== "string" ||
expectedSkillBody === undefined
)
return false;
const open =
'<skill_content name="reverse-engineer-anything">\n<skill_resources>\n';
const start = message.content.indexOf(open);
if (start < 0) return false;
const resourcesStart = start + open.length;
const instructions = `\n</skill_resources>\n\n<skill_instructions>\n${expectedSkillBody}\n</skill_instructions>\n</skill_content>`;
const resourcesEnd = message.content.indexOf(
instructions,
resourcesStart,
);
if (resourcesEnd < 0) return false;
const expectedPath = dirname(skillPath)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
const resources = message.content.slice(resourcesStart, resourcesEnd);
return (
!resources.includes("</skill_content>") &&
!resources.includes("</skill_resources>") &&
resources.includes(expectedPath)
);
});
for (const message of toolMessages) {
const found = findClientResult(
message.content,
@@ -281,11 +305,15 @@ await withVerifierCleanup(async () => {
const configured = await readFile(configPath, "utf8");
assert.deepEqual(withoutRea(configured), withoutRea(original));
assert(configured.includes("!!js process.cwd()"));
assert(
(await readFile(skillPath, "utf8")).includes(
"name: reverse-engineer-anything",
),
const installedSkill = await readFile(skillPath, "utf8");
assert(installedSkill.includes("name: reverse-engineer-anything"));
const frontmatter = /^---\r?\n[\s\S]*?^---(?:\r?\n|$)/mu.exec(
installedSkill,
);
assert(frontmatter, "Installed skill must have its authored frontmatter");
expectedSkillBody = installedSkill.slice(frontmatter[0].length).trim();
assert(expectedSkillBody.length > 0);
lifecycle.push("setup");
const repeated = await runRea(
["setup", "--client", "dsh", "--yes"],
+4
View File
@@ -104,6 +104,10 @@ await withVerifierCleanup(
}
// The same checks apply to the default selection and each remaining ABI.
const preferred = await run("inspect_dart_aot", { path: apk });
assert.ok(
abis.some((abi) => abi.abi === preferred.abi),
"Default AOT selection must be one of the identified ABIs",
);
for (const abi of abis) {
const aot =
abi.abi === preferred.abi
+5 -1
View File
@@ -2,7 +2,7 @@ import { basename } from "node:path";
import { FridaInstrumentationService } from "../dist/application/frida/FridaInstrumentationService.js";
import { FridaInstrumentationManager } from "../dist/frida/FridaInstrumentationManager.js";
import { isValidFridaRemoteAddress } from "../dist/contracts/fridaRemoteAddress.js";
import { isValidFridaRemoteAddress } from "../dist/domain/fridaRemoteAddress.js";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
const options = parseArguments(process.argv.slice(2));
@@ -80,6 +80,10 @@ try {
!Array.isArray(observed.messages)
)
throw new Error("Frida returned no message observation");
if (!Number.isSafeInteger(observed.pid) || observed.pid <= 0)
throw new Error("Frida returned no valid target PID");
if (remote !== undefined && observed.pid !== options.pid)
throw new Error("Frida observed a different target PID");
const messageObserved = observed.messages.some(
(message) =>
typeof message === "object" &&
+49 -19
View File
@@ -2,13 +2,14 @@ import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve, sep } from "node:path";
import { isAbsolute, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import {
TOOL_CONTRACTS,
toolContract,
} from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
@@ -65,6 +66,7 @@ let fixture;
let passed = false;
let evidenceSeen = false;
let skillLoaded = false;
let expectedSkillBody;
let skillRequested = false;
let finalSeen = false;
let catalogSize = null;
@@ -150,13 +152,21 @@ await withVerifierCleanup(async () => {
if (args.includes("--dry-run")) {
assert(parsed.plannedActions.some((a) => a.target === configPath));
assert(
parsed.plannedActions.some((a) =>
a.target.startsWith(join(profile, ".agents", "skills") + sep),
),
parsed.plannedActions.some((a) => {
const skills = join(profile, ".agents", "skills");
return (
isAbsolute(a.target) &&
relative(skills, a.target) !== "" &&
isPathWithinRoot(skills, a.target)
);
}),
);
assert(
parsed.plannedActions.every((a) =>
a.target.startsWith(profile + sep),
parsed.plannedActions.every(
(a) =>
isAbsolute(a.target) &&
relative(profile, a.target) !== "" &&
isPathWithinRoot(profile, a.target),
),
);
}
@@ -189,11 +199,15 @@ await withVerifierCleanup(async () => {
assert.deepEqual(JSON.parse(repeated.stdout).appliedActions, []);
assert.equal(await readFile(configPath, "utf8"), configured);
assert.equal(await readFile(`${configPath}.rea.backup`, "utf8"), original);
assert(
(await readFile(skillPath, "utf8")).includes(
"name: reverse-engineer-anything",
),
const installedSkill = await readFile(skillPath, "utf8");
assert(installedSkill.includes("name: reverse-engineer-anything"));
const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/u.exec(
installedSkill,
);
assert(frontmatter, "Installed skill must have authored frontmatter");
expectedSkillBody = (frontmatter[2] ?? "").trim();
assert(expectedSkillBody.length > 0);
const target = join(lab, "fixture α测试");
await mkdir(target);
await writeFile(join(target, "app.js"), CLIENT_JAVASCRIPT_FIXTURE_SOURCE);
@@ -268,16 +282,32 @@ await withVerifierCleanup(async () => {
.flatMap((c) => c.parts ?? [])
.filter((p) => p.functionResponse)
.map((p) => p.functionResponse);
skillLoaded ||= responses.some(
(r) =>
r.name === "activate_skill" &&
r.response?.output?.includes(
`<activated_skill name="reverse-engineer-anything">`,
) &&
r.response.output.includes(
skillLoaded ||= responses.some((response) => {
const output = response.response?.output;
if (
response.name !== "activate_skill" ||
typeof output !== "string" ||
expectedSkillBody === undefined
)
return false;
const start = `<activated_skill name="reverse-engineer-anything">\n <instructions>\n ${expectedSkillBody}\n </instructions>\n\n <available_resources>\n `;
const at = output.indexOf(start);
if (at < 0) return false;
const resourcesStart = at + start.length;
const resourcesEnd = output.indexOf(
"\n </available_resources>\n</activated_skill>",
resourcesStart,
);
if (resourcesEnd < 0) return false;
const resources = output.slice(resourcesStart, resourcesEnd);
return (
!resources.includes("</activated_skill>") &&
!resources.includes("</available_resources>") &&
resources.includes(
join(profile, ".agents", "skills", "reverse-engineer-anything"),
),
);
)
);
});
const observed = findClientResult(
responses,
isClientJavaScriptEvidence,
+3 -2
View File
@@ -6,6 +6,7 @@ import { isAbsolute, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
@@ -402,8 +403,8 @@ enabled = false
parsed.plannedActions.every(
(action) =>
action.target === configPath ||
action.target === skillDirectory ||
action.target.startsWith(skillDirectory + "/"),
(isAbsolute(action.target) &&
isPathWithinRoot(skillDirectory, action.target)),
),
"Refuse writes outside the owned profile/skill",
);
+5 -1
View File
@@ -373,6 +373,7 @@ try {
}
const report = {
run_id: run.run_id,
input_mode: inspectExisting ? "existing-captures" : "producer-attempt",
status: primaryError || cleanupFailures.length ? "failed" : "passed",
finished_at: new Date().toISOString(),
...(primaryError
@@ -386,8 +387,11 @@ const report = {
verifier,
producer_audit: producerAudit,
scope:
"Real Linux reverse HTTP/3, mitmproxy 12.2.3, aioquic 1.2.0 and guest cURL",
"Linux reverse HTTP/3 capture verification for mitmproxy 12.2.3, aioquic 1.2.0 and guest cURL",
limitations: [
...(inspectExisting
? ["Existing capture producer provenance was not authenticated by this attempt."]
: []),
"No local/WireGuard, browser, QUIC v2, migration, 0-RTT, mTLS or replay claim.",
"No HAR, interruption, certificate-failure or body-completeness coverage in this profile.",
],
+6 -2
View File
@@ -17,7 +17,7 @@ import { hashVerifierFile } from "./lib/verifier-file-resources.mjs";
const args = process.argv.slice(2);
if (args.includes("--help") || args.includes("-h")) {
console.log(
"Usage: npm run verify:il2cpp:metadata -- [--metadata PATH]\nPATH is a caller-owned, unencrypted global-metadata.dat (version 24, 27, 29, 31 or 39). Without --metadata, only the advertised contract is verified.",
"Usage: npm run verify:il2cpp:metadata -- [--metadata PATH]\nPATH is a caller-owned, unencrypted global-metadata.dat (version 24, 27, 29, 31 or 39). Without --metadata, only the MCP tool name is checked.",
);
process.exit(0);
}
@@ -178,4 +178,8 @@ await withVerifierCleanup(async () => {
);
}
}, [() => client.close(), () => transport.close(), () => resources.close()]);
console.log("All asserted IL2CPP metadata CLI/MCP checks passed");
console.log(
path === null
? "IL2CPP metadata MCP tool presence check passed"
: "All asserted IL2CPP metadata CLI/MCP checks passed",
);
+7 -3
View File
@@ -68,7 +68,9 @@ try {
});
if (!decompiled.ok) throw new Error(decompiled.error.message);
const text = decompiled.value.normalized_result.text;
if (!text.includes(target.marker))
if (text.trim().length === 0)
throw new Error("Decompiled pseudo-code was empty");
if (target.marker !== undefined && !text.includes(target.marker))
throw new Error(
"Decompiled pseudo-code did not contain the fixture marker",
);
@@ -83,7 +85,7 @@ try {
open_project_observed: openRefusal === null,
...(openRefusal === null ? {} : { open_project_refusal: openRefusal }),
unit_count: units.value.normalized_result.units.length,
decompiled_marker_observed: true,
decompiled_marker_observed: target.marker !== undefined,
};
}, [
async () => {
@@ -110,7 +112,7 @@ async function resolveTarget(selected, directory) {
return {
path: "/engine-side/target",
itemAddress: selected.itemAddress,
marker: selected.marker ?? "",
marker: selected.marker,
};
await writeFile(
join(directory, "Hello.java"),
@@ -153,6 +155,8 @@ function parseArguments(args) {
values.set(flag, value);
index += 1;
}
if (values.get("--marker") === "")
throw new Error("--marker must not be empty");
return {
help: false,
...(values.has("--item-address")
+4 -3
View File
@@ -2,9 +2,10 @@ import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { homedir, tmpdir } from "node:os";
import { join, relative } from "node:path";
import { isAbsolute, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
@@ -314,8 +315,8 @@ await withVerifierCleanup(async () => {
parsed.plannedActions.every(
(action) =>
action.target === configPath ||
action.target === skillDirectory ||
action.target.startsWith(skillDirectory + "/"),
(isAbsolute(action.target) &&
isPathWithinRoot(skillDirectory, action.target)),
),
"Refuse setup writes outside the owned config/skill",
);
+11 -11
View File
@@ -2,10 +2,11 @@ import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { isAbsolute, join, relative, sep } from "node:path";
import { isAbsolute, join } from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js";
import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
@@ -49,19 +50,15 @@ const skillDirectory = join(
"skills",
"reverse-engineer-anything",
);
const isManagedSkillTarget = (target) => {
const value = relative(skillDirectory, target);
return (
value === "" ||
(value !== ".." && !value.startsWith(`..${sep}`) && !isAbsolute(value))
);
};
assert.equal(
isManagedSkillTarget(`${skillDirectory}-sibling/AGENTS.md`),
isPathWithinRoot(skillDirectory, `${skillDirectory}-sibling/AGENTS.md`),
false,
);
assert.equal(
isManagedSkillTarget(join(skillDirectory, "..", "outside", "AGENTS.md")),
isPathWithinRoot(
skillDirectory,
join(skillDirectory, "..", "outside", "AGENTS.md"),
),
false,
);
let clientVersion = null;
@@ -271,7 +268,10 @@ await withVerifierCleanup(async () => {
);
assert(
parsed.plannedActions.every(
(a) => a.target === configPath || isManagedSkillTarget(a.target),
(a) =>
a.target === configPath ||
(isAbsolute(a.target) &&
isPathWithinRoot(skillDirectory, a.target)),
),
);
}
-1
View File
@@ -266,7 +266,6 @@ await withVerifierCleanup(async () => {
extractedPluginMcp: true,
targetFree: true,
targetLifecycle: true,
boundedRegexBridge: true,
update,
mcpStartup,
mcpModuleLoading,
+28 -11
View File
@@ -9,9 +9,10 @@ import {
symlink,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve, sep } from "node:path";
import { isAbsolute, join, relative, resolve, sep } from "node:path";
import { fileURLToPath } from "node:url";
import { createHash } from "node:crypto";
import { isPathWithinRoot } from "../dist/domain/localPath.js";
import { createVerifierCommands } from "./lib/verify-commands.mjs";
import { saveVerifierCommandOutput } from "./lib/verifier-command-output.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
@@ -58,9 +59,10 @@ let clientVersion = null;
let fixture;
let passed = false;
let evidenceSeen = false;
let skillSeen = false;
let finalSeen = false;
let skillRequested = false;
let skillCallId;
let expectedSkillBody;
let searchCallId;
let analysisCallId;
let artifactReadByAgent = false;
@@ -133,8 +135,11 @@ await withVerifierCleanup(async () => {
assert(
result.plannedActions.every(
(a) =>
a.target.startsWith(profile + sep) ||
a.target.startsWith(sharedSkills + sep),
isAbsolute(a.target) &&
((relative(profile, a.target) !== "" &&
isPathWithinRoot(profile, a.target)) ||
(relative(sharedSkills, a.target) !== "" &&
isPathWithinRoot(sharedSkills, a.target))),
),
);
await writeFile(
@@ -162,6 +167,20 @@ await withVerifierCleanup(async () => {
command: "unrelated-server",
disabled: true,
});
const skillFile = join(
sharedSkills,
"reverse-engineer-anything",
"SKILL.md",
);
const normalizedSkill = (await readFile(skillFile, "utf8"))
.replace(/^\uFEFF/u, "")
.replace(/\r\n|\r/gu, "\n");
const frontmatter = /^---\n([\s\S]*?)\n---(?:\n|$)([\s\S]*)$/u.exec(
normalizedSkill,
);
assert(frontmatter, "Installed skill must have authored frontmatter");
expectedSkillBody = frontmatter[2].trim();
assert(expectedSkillBody.length > 0);
clientPath = baseEnvironment.PATH;
if (reducedPath) {
assert.equal(
@@ -200,9 +219,6 @@ await withVerifierCleanup(async () => {
directory: lab,
prefix: `${client}-${mode}`,
onRequest: async ({ body, tools, requestIndex }) => {
skillSeen ||= JSON.stringify(body.messages ?? []).includes(
"reverse-engineer-anything",
);
const messages = (body.messages ?? []).map((message) => ({
role: message.role,
callId: message.tool_call_id,
@@ -213,14 +229,14 @@ await withVerifierCleanup(async () => {
.map((part) => part.text ?? "")
.join("\n"),
}));
const messageText = messages.map((message) => message.text).join("\n");
const toolResponseText = (callId) =>
messages.find(
(message) => message.role === "tool" && message.callId === callId,
)?.text ?? "";
selectedSkillLoaded ||= messageText.includes(
join(sharedSkills, "reverse-engineer-anything"),
);
const expectedSkill = `Base directory for this skill: ${join(sharedSkills, "reverse-engineer-anything")}\nImportant: ALWAYS resolve absolute paths from this base directory when working with skills.\n\n${expectedSkillBody}\n`;
selectedSkillLoaded ||=
skillCallId !== undefined &&
toolResponseText(skillCallId).includes(expectedSkill);
const toolText = JSON.stringify(
(body.messages ?? []).filter((m) => m.role === "tool"),
);
@@ -298,6 +314,7 @@ await withVerifierCleanup(async () => {
} else if (skill && !skillRequested) {
skillRequested = true;
message = call(skill.name, { skill: "reverse-engineer-anything" });
skillCallId = message.tool_calls[0].id;
} else if (persisted && shell) {
artifactPath = persisted[1];
assert(
+6 -3
View File
@@ -32,8 +32,6 @@ const runCli = async (...arguments_) => {
return JSON.parse(stdout);
};
const projectRoot = await mkdtemp(join(tmpdir(), "rea-cutter-smoke-"));
const projectPath = join(projectRoot, "smoke.rzdb");
const rizinQuote = (value) =>
`"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
const unavailableAnalysis = {
@@ -44,16 +42,21 @@ const unavailableAnalysis = {
},
};
const expectedPlatform = {
linux: "linux",
Linux: "linux",
macOS: "darwin",
Windows: "win32",
}[process.env.EXPECTED_PLATFORM];
if (expectedPlatform !== undefined)
if (process.env.EXPECTED_PLATFORM !== undefined) {
assert.ok(expectedPlatform, "Unrecognized Cutter runner platform.");
assert.equal(
process.platform,
expectedPlatform,
"Cutter runner did not match its declared platform.",
);
}
const projectRoot = await mkdtemp(join(tmpdir(), "rea-cutter-smoke-"));
const projectPath = join(projectRoot, "smoke.rzdb");
try {
const discovery = await runCli("list-cutter-sessions");
assert.equal(
+5 -1
View File
@@ -79,7 +79,11 @@ const output = {
windows: capture.windows,
verified:
capture.coverage.status === "partial_attach" &&
capture.actions.every(({ status }) => status === "completed") &&
capture.actions.length === input.actions.length &&
capture.actions.every(
({ step_id, status }, index) =>
step_id === input.actions[index].step_id && status === "completed",
) &&
capture.actions.some(
({ kind, window_index }) =>
kind === "renderer-reload" && window_index === 1,
+5 -4
View File
@@ -1,6 +1,6 @@
import { hasTypedSwitchEvidence } from "./verify-real-ghidra-switch-assertions.mjs";
import { access } from "node:fs/promises";
import { lstat } from "node:fs/promises";
import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js";
import { SUPPORTED_GHIDRA_VERSION } from "../dist/ghidra/GhidraReleasePolicy.js";
@@ -437,10 +437,11 @@ export function summary(observed) {
async function exists(path) {
try {
await access(path);
await lstat(path);
return true;
} catch {
return false;
} catch (cause) {
if (cause?.code === "ENOENT") return false;
throw cause;
}
}
+2 -4
View File
@@ -415,7 +415,7 @@ async function verifyRequestCancellationAndTimeout(client, entry) {
}
async function verifyConcurrentRequests(client, entry, leaf) {
const concurrent = await Promise.all([
await Promise.all([
functionCall(client, "procedure_info", {
document: null,
procedure: entry.address,
@@ -425,9 +425,7 @@ async function verifyConcurrentRequests(client, entry, leaf) {
address: leaf.address,
}),
]);
if (concurrent.length !== 2)
throw new Error("Ghidra serial request queue lost a concurrent result");
return "serialized-two-results";
return "two-concurrent-requests-completed";
}
/** Prove dense jump-table completeness on the optimized jump-table fixture. */
+6 -1
View File
@@ -375,8 +375,13 @@ try {
JSON.stringify(report, null, 2) + "\n",
);
}
const outcome = report.typed_failure
? "MCP refusal"
: mode === "default-native"
? "native load image without metadata recovery"
: "MCP metadata";
console.log(
`PASS real Ghidra NativeAOT ${oracle.runtime}/${mode}: MCP metadata, source identity and owned cleanup${report?.compute ? ", independent type/slot oracle and pseudocode" : ""}${report?.cli_metadata ? ", equivalent CLI" : ""}`,
`PASS real Ghidra NativeAOT ${oracle.runtime}/${mode}: ${outcome}, source unchanged and runtime directory empty${report?.compute ? ", independent type/slot oracle and pseudocode" : ""}${report?.cli_metadata ? ", equivalent CLI" : ""}`,
);
} catch (error) {
console.error(stderr);
+2 -1
View File
@@ -55,7 +55,8 @@ try {
if (
report.missing.length ||
report.unexpected.length ||
report.duplicates.length
report.duplicates.length ||
report.wrongProjects.length
) {
console.error(JSON.stringify(report, null, 2));
process.exitCode = 1;
@@ -41,10 +41,18 @@ const [leftPath, rightPath] = await Promise.all([
]);
if (leftPath === rightPath)
throw new Error("--left and --right must resolve to distinct paths");
const [left, right] = await Promise.all([
const analyses = await Promise.allSettled([
analyze(options.left),
analyze(options.right),
]);
const failures = analyses.filter((result) => result.status === "rejected");
if (failures.length === 1) throw failures[0].reason;
if (failures.length > 1)
throw new AggregateError(
failures.map((result) => result.reason),
"Application analyses failed",
);
const [left, right] = analyses.map((result) => result.value);
const comparison = compareApplicationVersionsEvidence({ left, right });
if (!comparison.ok) throw comparison.error;
const comparisonResult = comparison.value.result;
@@ -1,8 +1,17 @@
#!/usr/bin/env node
import { readFile, realpath } from "node:fs/promises";
import { realpath } from "node:fs/promises";
import { parseArgs } from "node:util";
import { readStableArtifact } from "../../../dist/artifacts/readStableArtifact.js";
import { ArtifactReaderFailure } from "../../../dist/artifacts/ArtifactReader.js";
import { ArtifactResourceScope } from "../../../dist/artifacts/ArtifactResourceScope.js";
import {
ArtifactFileCleanupFailure,
artifactFileCompletion,
artifactFilePrimaryFacts,
} from "../../../dist/artifacts/ArtifactFileOperation.js";
import { reconcileJavaScriptRuntimeEvidence } from "../../../dist/application/javascript/JavaScriptRuntimeReconciliationService.js";
import { analyzeJavaScriptApplicationWithOwnedScope } from "../../lib/javascript-application-analysis.mjs";
import {
@@ -52,9 +61,11 @@ for (const encoded of options.mapping) {
const mapping = parseMapping(encoded, staticLayers.length);
staticLayers[mapping.layer].runtime_mappings.push(mapping.value);
}
const runtimeObservations = (
await Promise.all(options["runtime-evidence"].map(readEvidenceFile))
).flat();
const runtimeObservations = [];
for (const path of options["runtime-evidence"]) {
const observations = await readEvidenceFile(path);
for (const observation of observations) runtimeObservations.push(observation);
}
const reconciled = reconcileJavaScriptRuntimeEvidence({
static_layers: staticLayers,
runtime_observations: runtimeObservations,
@@ -95,10 +106,47 @@ process.stdout.write(
);
async function readEvidenceFile(path) {
const bytes = await readFile(await realpath(path));
if (bytes.byteLength > 64 * 1024 * 1024)
throw new Error(`Runtime Evidence exceeds 64 MiB: ${path}`);
const value = JSON.parse(bytes.toString("utf8"));
const scope = new ArtifactResourceScope();
let outcome;
try {
outcome = {
ok: true,
value: await readStableArtifact(
await realpath(path),
64 * 1024 * 1024,
undefined,
undefined,
scope,
),
};
} catch (cause) {
outcome = { ok: false, cause };
}
try {
await scope.close();
} catch (cleanupCause) {
if (!outcome.ok)
throw new AggregateError(
[outcome.cause, cleanupCause],
"Verifier cleanup failed",
{ cause: outcome.cause },
);
throw cleanupCause;
}
if (!outcome.ok && outcome.cause instanceof ArtifactFileCleanupFailure) {
const completion = artifactFileCompletion(outcome.cause);
outcome =
completion === undefined
? { ok: false, cause: artifactFilePrimaryFacts(outcome.cause).cause }
: { ok: true, value: completion.value };
}
if (!outcome.ok) {
const cause = outcome.cause;
if (cause instanceof ArtifactReaderFailure && cause.reason === "limit")
throw new Error(`Runtime Evidence exceeds 64 MiB: ${path}`, { cause });
throw cause;
}
const value = JSON.parse(outcome.value.bytes.toString("utf8"));
return Array.isArray(value) ? value : [value];
}
@@ -114,6 +114,38 @@ it.each([
},
);
it("retains completed scenario Evidence when cancellation arrives during capture", async () => {
const caller = new AbortController();
const reason = { requested: "stop after capture" };
const provider: BrowserScenarioCapturePort = {
identity: () => scenarioIdentity,
captureScenario: async (received, options) => {
expect(received).toBe(scenario);
expect(options?.signal).toBe(caller.signal);
caller.abort(reason);
return ok(capture);
},
};
const result = await captureBrowserScenario(provider, scenario, {
signal: caller.signal,
});
if (result.ok) throw new Error("Expected late cancellation");
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
expect(result.error.cause).toBe(reason);
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
operation: "capture_browser_scenario",
provider: scenarioIdentity,
confidence: "observed",
authority: "controlled-replay",
parameters: {
browser: scenario.browser,
start_url: scenario.start_url,
actions: scenario.actions,
},
normalized_result: capture,
});
});
it("keeps startup cancellation and its incomplete capture", async () => {
const reason = { requested: "stop during initial navigation" };
const completed = scriptScenarioFixture();
@@ -139,6 +139,13 @@ it("retains completed execution Evidence on late abort and keeps invalid output
expect(completed.error.cause).toBe(cancellationReason);
expect(parseEvidence(completed.error.partialObservation)).toMatchObject({
operation: "observe_web_execution",
provider: { id: "web-runtime", name: "Runtime fixture", version: "1" },
parameters: input,
confidence: "observed",
authority: "external-service",
limitations: expect.arrayContaining(
webExecutionFixture(input.target_id, input.observation_ms).limitations,
),
normalized_result: webExecutionFixture(
input.target_id,
input.observation_ms,
@@ -220,7 +220,8 @@ it("honors cancellation both before acquisition and across decoder completion",
if (!response.ok) expect(response.error.cause).toBe(firstReason);
const controller = new AbortController();
const lateReason = { caller: "after source-map provider completion" };
const late = await service(undefined, {
const fixture = webSourceLocationFixture();
const late = await service(fixture, {
trace: (input) => {
controller.abort(lateReason);
return decoder.trace(input);
@@ -232,6 +233,28 @@ it("honors cancellation both before acquisition and across decoder completion",
expect(parseEvidence(late.error.partialObservation)).toMatchObject({
operation: "trace_web_source_location",
confidence: "derived",
authority: "historical-reference",
subject: {
local_path: fixture.sourceFile.path,
digest: { sha256: fixture.sourceFile.sha256 },
},
parameters: args,
raw_result: { source_map_text: fixture.sourceMap.text },
normalized_result: {
source_map: {
...fixture.sourceMap.file,
url: args.source_map.url,
association: "caller-selected",
},
lookup: { requested: args.generated_position },
source_authenticity: "unknown",
execution: "unknown",
},
locations: [
fixture.sourceFile.path,
fixture.manifestFile.path,
fixture.sourceMap.file.path,
].map((path) => ({ kind: "artifact-path", path })),
});
const malformedDecoder: WebSourceMapPort = {
@@ -30,11 +30,12 @@ const report: ElectronFuseReport = {
it("retains completed fuse Evidence when the provider completes after abort", async () => {
const controller = new AbortController();
const reason = { requested: "stop after fuse inspection" };
const result = await new ElectronFuseService({
identity,
close: async () => {},
inspect: async () => {
controller.abort();
controller.abort(reason);
return ok(report);
},
}).inspect(
@@ -44,8 +45,19 @@ it("retains completed fuse Evidence when the provider completes after abort", as
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error._tag).toBe("AnalysisCancelledError");
expect(result.error.cause).toBe(reason);
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
operation: "inspect_electron_fuses",
provider: identity,
subject: {
local_path: report.artifact.resolved_path,
digest: { sha256: report.artifact.sha256 },
format: report.artifact.format,
},
parameters: { path: report.artifact.selected_path },
confidence: "observed",
limitations: expect.arrayContaining(report.limitations),
locations: [{ kind: "artifact-path", path: report.artifact.selected_path }],
normalized_result: report,
});
}
@@ -150,6 +150,7 @@ it("returns a typed output error for malformed Electron target output after abor
it("retains completed target facts when cancellation arrives before Evidence publication", async () => {
const controller = new AbortController();
const reason = { requested: "stop after listing targets" };
const targets = electronTargetListSchema.parse({
browser: inspection.browser,
targets: [],
@@ -159,7 +160,7 @@ it("retains completed target facts when cancellation arrives before Evidence pub
const provider: ElectronObservationPort = {
identity: () => identity,
listTargets: async () => {
controller.abort();
controller.abort(reason);
return ok(targets);
},
inspectPage: async () =>
@@ -173,8 +174,22 @@ it("retains completed target facts when cancellation arrives before Evidence pub
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error).toBeInstanceOf(AnalysisCancelledError);
expect(result.error.cause).toBe(reason);
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
operation: "list_electron_targets",
provider: identity,
parameters: { cdp_endpoint: input.cdp_endpoint },
confidence: "observed",
authority: "external-service",
environment: {
id: `${inspection.browser.product}@${inspection.browser.revision}`,
platform: "unknown",
architecture: "unknown",
isolation: "none",
},
limitations: expect.arrayContaining([
"Browser host platform and architecture were not observed.",
]),
normalized_result: targets,
});
}
@@ -1,5 +1,6 @@
import { setImmediate } from "node:timers/promises";
import { describe, expect, it } from "vitest";
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
import { completeJavaScriptAnalysisSteps } from "./JavaScriptAnalysisControl.js";
describe("JavaScript cooperative computation lifecycle", () => {
@@ -15,9 +16,19 @@ describe("JavaScript cooperative computation lifecycle", () => {
}
}
const cancellation = setImmediate().then(() => controller.abort(reason));
await expect(
completeJavaScriptAnalysisSteps(work(), controller.signal),
).rejects.toMatchObject({ reason: "cancelled", cause: reason });
const failure = await completeJavaScriptAnalysisSteps(
work(),
controller.signal,
).then(
() => {
throw new Error("Expected cancellation");
},
(cause: unknown) => cause,
);
expect(failure).toBeInstanceOf(ArtifactReaderFailure);
if (!(failure instanceof ArtifactReaderFailure)) throw failure;
expect(failure.reason).toBe("cancelled");
expect(failure.cause).toBe(reason);
await cancellation;
expect(released).toBe(true);
});
@@ -32,9 +43,19 @@ describe("JavaScript cooperative computation lifecycle", () => {
yield;
return 42;
}
await expect(
completeJavaScriptAnalysisSteps(work(), controller.signal),
).rejects.toMatchObject({ reason: "cancelled", cause: reason });
const failure = await completeJavaScriptAnalysisSteps(
work(),
controller.signal,
).then(
() => {
throw new Error("Expected cancellation");
},
(cause: unknown) => cause,
);
expect(failure).toBeInstanceOf(ArtifactReaderFailure);
if (!(failure instanceof ArtifactReaderFailure)) throw failure;
expect(failure.reason).toBe("cancelled");
expect(failure.cause).toBe(reason);
expect(started).toBe(false);
expect(await completeJavaScriptAnalysisSteps(work())).toBe(42);
});
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js";
import { err, ok } from "../../domain/result.js";
import { parseEvidence } from "../../domain/evidence.js";
import type { JavaScriptRecoveryResult } from "../../domain/javascript/javascriptRecovery.js";
import { createAnalysisExecution } from "../AnalysisProvider.js";
import { JavaScriptRecoveryService } from "./JavaScriptRecoveryService.js";
@@ -179,7 +180,17 @@ it("returns cancellation with the validated Evidence when the caller aborts afte
if (result.ok) throw new Error("expected cancellation");
expect(result.error._tag).toBe("AnalysisCancelledError");
expect(result.error.cause).toBe(reason);
expect(result.error.partialObservation).toMatchObject({
expect(parseEvidence(result.error.partialObservation)).toMatchObject({
operation: "recover_javascript_sources",
provider: providerIdentity,
parameters: input,
subject: {
local_path: artifact.path,
digest: { sha256: artifact.sha256 },
format: "javascript",
},
confidence: "derived",
authority: "shipped-artifact",
normalized_result: recoveryResult(),
});
});
@@ -41,36 +41,6 @@ it("managed reconstruction reports UTF-8 byte totals while preserving UTF-16 tex
});
});
it("managed reconstruction rejects text hash drift", () => {
const rejected = resolveManagedReconstructionImport({
...exampleInput(),
methods: [
{
...exampleMethod(),
reconstruction: {
...exampleMethod().reconstruction,
text_sha256: "0".repeat(64),
},
},
],
});
expect(rejected).toMatchObject({
ok: false,
error: {
_tag: "AnalysisInputError",
operation: "import_managed_reconstruction",
issues: [
{
path: ["methods", 0, "reconstruction", "text_sha256"],
reason: "invalid_value",
message: expect.stringContaining("Reconstruction text hash mismatch"),
},
],
},
});
if (!rejected.ok) expect(rejected.error).toBeInstanceOf(AnalysisInputError);
});
it("managed reconstruction rejects stale method locks", () => {
const staleInput = {
...exampleInput(),
@@ -234,13 +204,15 @@ describe("managed reconstruction input diagnostics", () => {
});
expect(result.ok).toBe(false);
if (result.ok) return;
expect(result.error).toBeInstanceOf(AnalysisInputError);
expect(result.error).toMatchObject({
_tag: "AnalysisInputError",
operation: "import_managed_reconstruction",
issues: [
{
path: ["methods", 0, "reconstruction", "text_sha256"],
reason: "invalid_value",
message: expect.stringMatching(/text hash mismatch/u),
message: expect.stringContaining("Reconstruction text hash mismatch"),
},
],
});
+1 -1
View File
@@ -181,7 +181,7 @@ it("retains both source spellings and the native failure in a destination collis
expect(collision?.cause).toBe(cause);
});
it.each([["app.js"], ["app.js", "App.js"], ["other.js"]])(
it.each([["app.js", "App.js"], ["other.js"]])(
"does not invent a destination alias from listing %j",
(...names) => {
expect(destinationCaseCollision("res/app.js", names)).toBeUndefined();
@@ -1,60 +0,0 @@
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { build } from "plist";
import { afterEach, expect, it } from "vitest";
import { ArtifactProvider } from "./ArtifactProvider.js";
import type { ResolvedAppBundleResourceRoot } from "../application/AppBundleExecutable.js";
const temporaryDirectories: string[] = [];
afterEach(async () => {
await Promise.all(
temporaryDirectories
.splice(0)
.map((path) => rm(path, { recursive: true, force: true })),
);
});
it("returns keyed archive evidence with root provenance and unknown executable identity", async () => {
const bundlePath = await mkdtemp(join(tmpdir(), "rea-resource-reader-"));
temporaryDirectories.push(bundlePath);
await mkdir(join(bundlePath, "Resources"));
await writeFile(
join(bundlePath, "Info.plist"),
'<?xml version="1.0"?><plist><dict/></plist>',
);
const archive = build({
$archiver: "NSKeyedArchiver",
$version: 100000,
$objects: ["$null", "sample"],
$top: { root: { UID: 1 } },
});
await writeFile(join(bundlePath, "Resources", "Archive.plist"), archive);
const root: ResolvedAppBundleResourceRoot = {
callerPath: bundlePath,
canonicalPath: bundlePath,
infoPlistPath: join(bundlePath, "Info.plist"),
layout: "root",
executableSha256: null,
};
const provider = new ArtifactProvider({}, "linux");
const client = provider.createBundleResourceClient(root);
const execution = await client.execute("inspect_keyed_archive", {
path: "Resources/Archive.plist",
});
expect(execution.ok).toBe(true);
if (execution.ok) {
expect(execution.value.result).toMatchObject({
target_sha256: null,
resource_root: {
caller_path: bundlePath,
canonical_path: bundlePath,
executable_sha256: null,
},
archive_path: "Resources/Archive.plist",
});
}
expect((await client.close()).ok).toBe(true);
});
+35
View File
@@ -55,6 +55,41 @@ const firstEntry = async (reader: FitArtifactReader) => {
return entry;
};
it.each(["close", "cancel"] as const)(
"refuses to finish a paused FIT inventory after %s",
async (action) => {
const reader = new FitArtifactReader(
await writeFit("paused-inventory.itb", FIT_EXTERNAL),
);
const controller = new AbortController();
const reason = { stage: "after final entry" };
const iterator = reader.entries(controller.signal)[Symbol.asyncIterator]();
try {
expect((await iterator.next()).done).toBe(false);
expect((await iterator.next()).done).toBe(false);
if (action === "close") await reader.close();
else controller.abort(reason);
const failure: unknown = await iterator.next().then(
() => {
throw new Error("Expected the paused inventory to fail");
},
(cause: unknown) => cause,
);
expect(failure).toMatchObject(
action === "close"
? { reason: "unavailable" }
: { reason: "cancelled" },
);
if (action === "cancel") {
if (!(failure instanceof ArtifactReaderFailure)) throw failure;
expect(failure.cause).toBe(reason);
}
} finally {
await reader.close();
}
},
);
it.each([
["embedded", FIT_EMBEDDED, "embedded"],
["external", FIT_EXTERNAL, "data-offset"],
+1
View File
@@ -82,6 +82,7 @@ export class FitArtifactReader implements ArtifactReader {
this.#assertOpen(signal);
yield copyArtifactEntry(entry);
}
this.#assertOpen(signal);
}
async open(entry: ArtifactEntry, signal?: AbortSignal): Promise<Readable> {
@@ -184,21 +184,6 @@ describe("GodotPckArtifactReader", () => {
},
);
it("preserves declared MD5 for the shared inventory integrity verifier", async () => {
const bytes = buildGodotPck(3, [
{ path: "a.gd", data: "real", md5: Buffer.alloc(16, 1) },
]);
await withReader(bytes, async (reader) => {
const [entry] = await list(reader);
if (entry === undefined) throw new Error("expected one entry");
expect(entry.declaredIntegrity).toEqual({
algorithm: "md5",
digest: "01".repeat(16),
});
expect(await text(await reader.open(entry))).toBe("real");
});
});
it("reports encrypted members without reading them", async () => {
const bytes = buildGodotPck(3, [
{ path: "secret.gd", data: "x", flags: 1 },
@@ -10,6 +10,8 @@ import { buildUImage, buildMultiPayload } from "../../tests/fixtures/uimage.js";
import {
parseUImageHeaderFields,
parseMultiImageTable,
UIMAGE_HEADER_SIZE,
UIMAGE_MULTI_TABLE_WINDOW,
} from "../domain/uimageFormat.js";
const fixture = async (bytes: Buffer): Promise<string> => {
@@ -110,7 +112,31 @@ it("admits a multi-image count before scanning the remaining payload", async ()
reason: "limit",
boundary: "capacity",
});
expect(reads).toHaveBeenCalledTimes(2); // header and bounded table, no payload scan
expect(reads.mock.calls.length).toBeGreaterThan(0);
for (const call of reads.mock.calls) {
const [, , length, position] = call as unknown as [
Buffer,
number,
number,
number,
];
expect(Number.isSafeInteger(position)).toBe(true);
expect(Number.isSafeInteger(length)).toBe(true);
expect(position).toBeGreaterThanOrEqual(0);
expect(length).toBeGreaterThanOrEqual(0);
if (length > 0)
expect(position + length).toBeLessThanOrEqual(
UIMAGE_HEADER_SIZE + UIMAGE_MULTI_TABLE_WINDOW,
);
}
const results = await Promise.all(
reads.mock.results.map(
({ value }) => value as Promise<{ bytesRead: number }>,
),
);
expect(
results.reduce((sum, result) => sum + result.bytesRead, 0),
).toBeLessThanOrEqual(UIMAGE_HEADER_SIZE + UIMAGE_MULTI_TABLE_WINDOW);
} finally {
await reader.close();
reads.mockRestore();
@@ -5,7 +5,8 @@ import {
pythonPycHeader,
} from "../../python/PythonPyc.fixture.js";
import { PYINSTALLER_COOKIE_MAGIC } from "./CArchiveLayout.js";
// PyInstaller CArchiveWriter's on-disk cookie signature, independent of REA's reader.
const CARCHIVE_COOKIE_MAGIC = Buffer.from("4d45490c0b0a0b0e", "hex");
/** Final CPython 3.9 bytecode magic, little-endian with the CR LF trailer. */
export const PYTHON_39_MAGIC = Buffer.from("610d0d0a", "hex");
@@ -172,7 +173,7 @@ export const cArchiveBytecodeExecutable = () => {
/** Cookie whose package range starts before the file. */
export const cookieRangeOutsideFile = (): Buffer => {
const cookie = Buffer.alloc(88);
PYINSTALLER_COOKIE_MAGIC.copy(cookie, 0);
CARCHIVE_COOKIE_MAGIC.copy(cookie, 0);
cookie.writeUInt32BE(200, 8);
cookie.writeUInt32BE(80, 12);
cookie.writeUInt32BE(32, 16);
@@ -255,7 +256,7 @@ const cookieBytes = (input: {
}): Buffer => {
const size = input.layout === "2.1" ? 88 : 24;
const bytes = Buffer.alloc(size);
PYINSTALLER_COOKIE_MAGIC.copy(bytes, 0);
CARCHIVE_COOKIE_MAGIC.copy(bytes, 0);
bytes.writeUInt32BE(input.tocOffset + input.tocLength + size, 8);
bytes.writeUInt32BE(input.tocOffset, 12);
bytes.writeUInt32BE(input.tocLength, 16);
@@ -909,67 +909,6 @@ it("retains the descriptor owner when admission cleanup fails", async () => {
}
});
it("inventories and extracts a Windows member name under its canonical path", async () => {
const storedName = String.raw`pkg\data.bin`;
const pyzName = String.raw`res\icon.bin`;
const payload = Buffer.from("nested-bytes");
const icon = Buffer.from("icon-bytes");
const path = await writeArchive(
"windows",
cArchiveExecutable({
carrier: "pe",
layout: "2.1",
pythonVersion: 309,
pythonLibrary: "python39.dll",
members: [
{ name: storedName, typeCode: "x", payload, compress: false },
{
name: "PYZ.pyz",
typeCode: "z",
payload: pyzArchive({
magic: PYTHON_39_MAGIC,
modules: [{ name: pyzName, typeCode: 2, body: icon }],
}),
compress: false,
},
],
}),
);
const snapshot = await scanCanonicalArtifactInventory(path);
const member = snapshot.occurrences.find(
(occurrence) => occurrence.logical_path === "pkg/data.bin",
);
const resource = snapshot.occurrences.find(
(occurrence) => occurrence.logical_path === "PYZ.pyz/res/icon.bin",
);
expect(member?.format_metadata).toMatchObject({ source_name: storedName });
expect(resource?.format_metadata).toMatchObject({ source_name: pyzName });
expect(member?.source_path).toBe(storedName);
expect(resource?.source_path).toBe(pyzName);
const directory = await createTestTempDirectory("rea-pyinstaller-out-");
const output = join(directory, "extracted");
await materializeArtifactInventory(
{
inputPath: path,
inputFormat: "pe",
outputRoot: output,
environment: process.env,
integrityPolicy: "fail",
},
path,
snapshot,
);
expect(
Buffer.compare(await readFile(join(output, "pkg", "data.bin")), payload),
).toBe(0);
expect(
Buffer.compare(
await readFile(join(output, "PYZ.pyz", "res", "icon.bin")),
icon,
),
).toBe(0);
});
const posixPathFixture = async () => {
const backslashName = String.raw`a\b.txt`;
const slashName = "a/b.txt";
-3
View File
@@ -227,9 +227,6 @@ describe("browserScenarioSchema", () => {
at_end: ["dom", "storage"],
events: ["console", "page-errors", "network", "websockets"],
});
expect(parsed.capture.limits).toEqual(
DEFAULT_BROWSER_SCENARIO_CAPTURE_LIMITS,
);
});
it("allows callers to choose visible browsers and service workers", () => {
@@ -188,8 +188,6 @@ it("preserves local Playwright action diagnostics", async () => {
status: "failed",
error: `locator ${selector} failed with token=raw-secret`,
});
expect(result[0]?.error).toContain(selector);
expect(result[0]?.error).toContain("raw-secret");
});
it("retains complete action errors and Electron window text", async () => {
+6
View File
@@ -359,6 +359,12 @@ describe("configuration failure diagnostics", () => {
it.each(["1", "90000", "2147483647"])(
"accepts Swift compile deadline %s without changing Hopper's deadline",
(value) => {
expect(
parseEnvironmentValues({ REA_SWIFTC_COMPILE_TIMEOUT_MS: value }),
).toMatchObject({
ok: true,
value: { REA_SWIFTC_COMPILE_TIMEOUT_MS: Number(value) },
});
expect(
parseConfig({ REA_SWIFTC_COMPILE_TIMEOUT_MS: value }),
).toMatchObject({
-33
View File
@@ -77,39 +77,6 @@ describe("bundle comparison explicit cross-pairs", () => {
});
describe("bundle comparison", () => {
it("does not report display-only bundle differences as unexplained changes", () => {
const target = {
path: "/left/artifact.elf",
sha256: "a".repeat(64),
format: "elf" as const,
architecture: "x86_64" as const,
};
const record = createEvidence(target, PROVIDER, {
operation: "observe",
parameters: {},
result: { observed: true },
confidence: "observed",
authority: "shipped-artifact",
});
const left = createEvidenceBundle([record]);
const rightRecord = {
...record,
subject: {
...record.subject!,
name: "renamed.elf",
local_path: "/right/renamed.elf",
},
};
const right = { ...left, records: [rightRecord] };
const result = compareBundles(left, right);
expect(result).toMatchObject({
status: "unchanged",
summary: { records_unchanged: 1, unresolved: 0 },
changes: [],
});
expect(result.left_bundle_sha256).not.toBe(result.right_bundle_sha256);
});
it("accepts long caller-selected bundle paths", () => {
const path = `/${"deep/".repeat(1_000)}bundle.json`;
expect(
-8
View File
@@ -229,14 +229,6 @@ describe("changed behavior", () => {
expect(aggregate([changed, unknown]).result).toEqual(result);
});
it("uses complete inline artifact deltas as static candidates", () => {
const complete = comparison("compare_artifacts", artifactResult());
expect(aggregate([complete]).result.findings.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ dimension: "artifact:main.js" }),
]),
);
});
});
describe("changed-behavior input and Evidence validation", () => {
+1 -1
View File
@@ -6,7 +6,7 @@ export const ghidraFunctionClassification = () => ({
});
/** Wire-boundary fixture, not evidence that a real Ghidra run was performed. */
export const ghidraFunctionBody = () => ({
const ghidraFunctionBody = () => ({
available: true as const,
provenance: "ghidra-function-body-address-set",
ranges: [{ start: "0x401000", end: "0x401005" }],
@@ -31,7 +31,7 @@ describe("managed/native verification source", () => {
const [observation] = source.value.nativeObservations;
expect(observation?.supported).toBe(true);
expect(observation?.symbols.map(({ name }) => name)).toEqual(
legacy.exports.items.map(({ name }) => name),
["open_native"],
);
});
});
-44
View File
@@ -1,44 +0,0 @@
import { expect, it } from "vitest";
import { inspectPeImportsInputSchema, peImportsSchema } from "./peImports.js";
it("rejects extra caller arguments and admits an empty import inventory", () => {
expect(
inspectPeImportsInputSchema.safeParse({
path: "/artifacts/app.exe",
extra: true,
}).success,
).toBe(false);
const report = peImportsSchema.parse({
artifact: {
path: "/artifacts/app.exe",
sha256: "a".repeat(64),
bytes: 512,
},
format: "pe32-plus",
machine: 0x8664,
bits: 64,
architecture: "amd64",
import_directory: null,
delay_import_directory: null,
imports: [],
delay_imports: [],
imphash: {
algorithm: "pefile",
md5: null,
reason: "IMAGE_DIRECTORY_ENTRY_IMPORT is absent.",
},
coverage: {
status: "complete",
reason: null,
examined_dlls: 0,
imports: 0,
delay_imports: 0,
},
issues: [],
limitations: [
"IMAGE_DIRECTORY_ENTRY_IMPORT is absent. That is not proof the image never imported APIs through another table or overlay.",
],
});
expect(report.imports).toEqual([]);
expect(report.imphash.md5).toBeNull();
});
@@ -613,23 +613,6 @@ it.each([false, true])(
);
describe("managed artifact reference inventory", () => {
it("qualifies local custom-attribute names with their enclosing types", () => {
const bytes = buildManagedPeFixture({
additionalEmptyTypes: 1,
customAttributeTypeRaw: (1 << 3) | 2,
nestedTypes: [[2, 1]],
typeName: "InnerAttribute",
typeNamespace: "",
});
const result = inspectManagedArtifactBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(result.attributes[0]?.type_name).toBe("Empty0+InnerAttribute");
expect(result.coverage).toEqual({ state: "complete", issues: [] });
});
it("qualifies referenced custom-attribute names from TypeRef scopes", () => {
const bytes = buildManagedPeFixture({
customAttributeTypeRaw: (1 << 3) | 3,
-8
View File
@@ -473,14 +473,6 @@ describe("managed complete operand decoding", () => {
});
});
it("reports a switch whose target count is itself truncated", () => {
expect(fatBody(Buffer.from([0x45, 0x02]))).toMatchObject({
status: "malformed",
decoded_instruction_count: 0,
issue: "Instruction switch at IL offset 0 leaves method body",
});
});
it("preserves instructions decoded before a truncated operand", () => {
expect(fatBody(Buffer.from([0x00, 0x20]))).toMatchObject({
status: "malformed",
+7 -4
View File
@@ -192,9 +192,12 @@ it("does not invent an inspected constant on historical member Evidence", () =>
expect(historical.fields[0]).not.toHaveProperty("constant");
});
it.each([7, 8])(
"retains the common coded-index diagnostic for invalid parent %s",
(parentRaw) => {
it.each([
{ parentRaw: 7, reason: "tag 3" },
{ parentRaw: 8, reason: "row 2" },
])(
"retains the coded-index diagnostic for invalid parent $parentRaw",
({ parentRaw, reason }) => {
const bytes = buildManagedPeFixture({
fields: [{ name: "Default", flags: 0x8056 }],
constants: [
@@ -226,7 +229,7 @@ it.each([7, 8])(
expect.objectContaining({
code: "invalid-row",
scope: "metadata.Constant",
detail: parent.reason,
detail: expect.stringContaining(reason),
}),
]),
);
+3 -8
View File
@@ -101,14 +101,6 @@ it.each([
);
describe("managed compressed integer framing", () => {
it.each([[0x80], [0xc0], [0xc0, 1], [0xc0, 1, 2]])(
"keeps an incomplete width %j distinct from a reserved prefix",
(...value) => {
expect(() => readCompressed(Buffer.from(value), 0)).toThrow(
"truncated compressed integer",
);
},
);
it("preserves complete widths and rejects reserved leading bits", () => {
expect(readCompressed(Buffer.from([0x80, 1]), 0)).toEqual({
value: 1,
@@ -2418,6 +2410,9 @@ it.each([
it.each([
[[], "truncated compressed integer"],
[[0x80], "truncated compressed integer"],
[[0xc0], "truncated compressed integer"],
[[0xc0, 1], "truncated compressed integer"],
[[0xc0, 1, 2], "truncated compressed integer"],
[[0xc0, 0, 0], "truncated compressed integer"],
[[0xe0], "reserved compressed integer"],
] as const)(
@@ -228,14 +228,7 @@ it("reports field imports as declarations without inventing method implementatio
});
describe("managed native boundary inspection", () => {
it("returns complete member inventories inline and reports unavailable metadata", () => {
const bytes = buildManagedPeFixture();
const paged = inspectManagedMembersBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(paged.methods).toHaveLength(1);
it("reports absent or malformed member metadata", () => {
const nativeBytes = buildNativePeFixture();
const native = inspectManagedMembersBytes(
nativeBytes,
@@ -610,7 +610,7 @@ describe("signature display name validity", () => {
const result = inspectDisplayFixture({ fieldSignature: blob });
expect(result.fields[0]?.signature.parse_status).toBe(status);
expect(result.fields[0]?.signature.raw_sha256).toBe(
signature(blob).raw_sha256,
createHash("sha256").update(blob).digest("hex"),
);
});
});
+6 -6
View File
@@ -266,11 +266,11 @@ describe("ELF embedded ASAR integrity applicability", () => {
it("preserves an enabled ASAR integrity fuse without claiming enforcement on ELF", async () => {
const root = await createTestTempDirectory("rea-electron-fuses-elf-asar-");
const fuses = [
ELECTRON_FUSE_STATE_BYTES.disabled,
ELECTRON_FUSE_STATE_BYTES.disabled,
ELECTRON_FUSE_STATE_BYTES.disabled,
ELECTRON_FUSE_STATE_BYTES.disabled,
ELECTRON_FUSE_STATE_BYTES.enabled,
0x30,
0x30,
0x30,
0x30,
0x31,
];
const image = elfFuseImage(fuses);
const path = join(root, "electron");
@@ -281,7 +281,7 @@ describe("ELF embedded ASAR integrity applicability", () => {
index: 4,
name: "EnableEmbeddedAsarIntegrityValidation",
state: "enabled",
raw_byte: ELECTRON_FUSE_STATE_BYTES.enabled,
raw_byte: 0x31,
});
expect(
report.runtime_implications.find(
+1 -7
View File
@@ -136,7 +136,7 @@ describe("LabeledStringScanner", () => {
};
it("collects a full clang line within the 500-character bound", () => {
assertSingleToolchainLine(
"Android (5900059 based on r365631c1) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)",
"Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc1a2abf3ef8d4301736d6a7ebc224a290) (based on LLVM 9.0.8svn)",
);
});
@@ -155,12 +155,6 @@ describe("LabeledStringScanner", () => {
);
});
it("collects the labeled Android clang line", () => {
assertSingleToolchainLine(
"Android (5900059 based on r365631c) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 207d7abc) (based on LLVM 9.0.8svn)",
);
});
it("does not claim a toolchain cap for unrelated overlong strings", () => {
const scanner = new LabeledStringScanner();
const license = `Apache License Version 2.0 ${"x".repeat(700)}`;
+11
View File
@@ -803,6 +803,15 @@ const malformedOutputs = (): Array<
length: 1,
signature: null,
locals: [],
body: {
available: true,
provenance: "ghidra-function-body-address-set",
ranges: [{ start: "0x401000", end: "0x401000" }],
total_bytes: 1,
span_bytes: 1,
non_contiguous: false,
contains_entry: true,
},
},
],
[
@@ -814,6 +823,8 @@ const malformedOutputs = (): Array<
references: [
{ ...edge, kind: { available: false, reason: "unknown" } },
],
reference_kinds_available: true,
unresolved_calls: [],
},
],
[
+8 -2
View File
@@ -458,7 +458,10 @@ describe("headless IDA containment lookup failures", () => {
});
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error._tag).toBe("AnalysisCancelledError");
if (!result.ok) {
expect(result.error._tag).toBe("AnalysisCancelledError");
expect(result.error.cause).toBe(controller.signal.reason);
}
expect(
producer.calls.some(({ name }) => name === "survey_binary"),
"nothing is dispatched after cancellation",
@@ -606,7 +609,10 @@ describe("headless IDA document identity", () => {
);
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error._tag).toBe("AnalysisCancelledError");
if (!result.ok) {
expect(result.error._tag).toBe("AnalysisCancelledError");
expect(result.error.cause).toBe(controller.signal.reason);
}
expect(producer.calls).toEqual([]);
} finally {
expect((await client.close()).ok).toBe(true);
+1
View File
@@ -302,6 +302,7 @@ describe("headless IDA partial operation evidence", () => {
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error._tag).toBe("AnalysisCancelledError");
expect(result.error.cause).toBe(controller.signal.reason);
expect(result.error.partialObservation).toMatchObject({
analysis_profile: null,
normalized_result: {
+12 -2
View File
@@ -556,7 +556,11 @@ export class KunaProjectCache {
request: KunaProjectRequest,
): Promise<Result<KunaProject, AnalysisError>> {
if (request.signal?.aborted === true)
return err(new AnalysisCancelledError(KUNA_EXPORT_OPERATION));
return err(
new AnalysisCancelledError(KUNA_EXPORT_OPERATION, {
cause: request.signal.reason,
}),
);
const key = kunaProjectKey({
targetSha256: request.targetSha256,
targetBasename: basename(request.targetPath),
@@ -878,7 +882,13 @@ export class KunaProjectCache {
settled = true;
leave();
if (entry.waiters === 0) entry.controller.abort();
resolve(err(new AnalysisCancelledError(KUNA_EXPORT_OPERATION)));
resolve(
err(
new AnalysisCancelledError(KUNA_EXPORT_OPERATION, {
cause: request.signal?.reason,
}),
),
);
};
request.signal?.addEventListener("abort", onAbort, { once: true });
void entry.promise.then((result) => {
+18 -2
View File
@@ -97,7 +97,14 @@ export class KunaTargetCommands {
if (this.#closing || request.signal.aborted) {
request.releaseRun?.();
return Promise.resolve(
err(new AnalysisCancelledError(request.operation)),
err(
new AnalysisCancelledError(
request.operation,
request.signal.aborted
? { cause: request.signal.reason }
: undefined,
),
),
);
}
return this.#run(request);
@@ -106,7 +113,16 @@ export class KunaTargetCommands {
/** Establish the immutable input before waiting for a foreground run slot. */
prepare(): Promise<Result<null, AnalysisError>> {
if (this.#closing || this.lifetime.signal.aborted)
return Promise.resolve(err(new AnalysisCancelledError("kuna:workspace")));
return Promise.resolve(
err(
new AnalysisCancelledError(
"kuna:workspace",
this.lifetime.signal.aborted
? { cause: this.lifetime.signal.reason }
: undefined,
),
),
);
return this.#admit("kuna:workspace", () =>
this.#ensureWorkspace().then((workspace) =>
workspace.ok ? ok(null) : workspace,
+7 -3
View File
@@ -1,5 +1,3 @@
import { MODULE_SIGNATURE_MAGIC } from "../../domain/native/kernelModule.js";
const concat = (...parts: readonly Buffer[]): Buffer => Buffer.concat(parts);
const write16 = (
@@ -75,7 +73,13 @@ export const moduleSignatureTrailer = (
const info = Buffer.alloc(12);
info[2] = 2;
info.writeUInt32BE(sigLen >>> 0, 8);
return concat(pkcs7, info, Buffer.from(MODULE_SIGNATURE_MAGIC, "latin1"));
// Linux MODULE_SIG_STRING includes the final newline:
// https://code.googlesource.com/linux/torvalds/linux/+/78273df7f646f8daf2604ec714bea0897cd03aae/include/linux/module_signature.h
return concat(
pkcs7,
info,
Buffer.from("~Module signature appended~\n", "latin1"),
);
};
export interface KernelModuleFixtureOptions {
@@ -551,13 +551,14 @@ it("honors cancellation during token-open exit settlement", async () => {
lifecycle: { state: "observed", lifecycle: "non-zombie" },
};
const controller = new AbortController();
const reason = new Error("cancelled");
const host: ProcessOwnershipHost = {
platform: "linux",
listProcesses: async () => [row],
environment: async () => ({}),
signalGroup: vi.fn(),
processIdentities: async () => {
controller.abort(new Error("cancelled"));
controller.abort(reason);
return new Map([[row.pid, identity]]);
},
};
@@ -576,7 +577,7 @@ it("honors cancellation during token-open exit settlement", async () => {
{ deadline: undefined },
controller.signal,
),
).rejects.toThrow("cancelled");
).rejects.toBe(reason);
});
it("does not signal a stale group after its last member exits during revalidation", async () => {
-3
View File
@@ -416,9 +416,6 @@ it("describes the effective heap and the configurable matching deadline", () =>
regexWorkerHeapRequestRecovery(DEFAULT_REGEX_WORKER_HEAP_REQUEST),
);
if (heap.kind === "unknown") {
expect(projected.remediation.action).toContain(
"--max-old-space-size-percentage",
);
expect(projected.remediation.action).toContain(
"--experimental-config-file",
);
+1 -1
View File
@@ -57,7 +57,7 @@ describe("unobserved internal symlink reconciliation", () => {
]);
});
it("retains unreadable targets below the nearest failed directory", () => {
it("retains unreadable targets below a failed directory", () => {
const entries: ReferenceSourceEntry[] = [
internalLink("alias.txt", "hidden/target.txt"),
failedDirectory("hidden", "io"),
@@ -1,26 +0,0 @@
import { expect, it } from "vitest";
import { machoImageWithUnknownCommand } from "../artifacts/apple/MachoImage.fixture.js";
import { readRustBinaryImage } from "./RustBinaryImage.js";
import { RustAuditableFormatFailure } from "./RustAuditableReader.js";
it("maps malformed 64-bit Mach-O load-command alignment to Rust input failure", () => {
expect(() =>
readRustBinaryImage(Buffer.from(machoImageWithUnknownCommand(true, 12))),
).toThrow(RustAuditableFormatFailure);
expect(() =>
readRustBinaryImage(Buffer.from(machoImageWithUnknownCommand(true, 12))),
).toThrow(expect.objectContaining({ kind: "malformed" }));
});
it.each([
{ wide: true, size: 16 },
{ wide: false, size: 12 },
])(
"accepts $size-byte Rust Mach-O command for wide=$wide",
({ wide, size }) => {
expect(
readRustBinaryImage(Buffer.from(machoImageWithUnknownCommand(wide, size)))
.format,
).toBe("macho");
},
);
+41 -10
View File
@@ -2,7 +2,7 @@ import type { Client } from "@modelcontextprotocol/client";
import { Ajv2020 } from "ajv/dist/2020.js";
import type { AnySchema } from "ajv";
import { expect, it } from "vitest";
import { mkdir, writeFile } from "node:fs/promises";
import { mkdir, realpath, symlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { z } from "zod";
@@ -178,7 +178,8 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
const parent = await createTestTempDirectory(
"rea-bundle-resource-production-mcp-",
);
const bundle = join(parent, "Fixture.app");
const bundle = join(parent, "Fixture");
const alias = join(parent, "Caller");
const resourceDirectory = join(bundle, "Resources");
await mkdir(resourceDirectory, { recursive: true });
await writeFile(join(bundle, "Info.plist"), "<plist><dict/></plist>");
@@ -192,6 +193,9 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
}),
);
await symlink(bundle, alias, "junction");
const canonical = await realpath(bundle);
const session = await createArtifactBinarySession({}, process.platform);
const { client } = await createApplicationMcpHarness({
session,
@@ -200,7 +204,7 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
const opened = await client.callTool({
name: "open_bundle_resources",
arguments: { path: bundle },
arguments: { path: alias },
});
expect(opened.isError).not.toBe(true);
expect(
@@ -208,7 +212,11 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
opened.structuredContent,
),
).toMatchObject({
result: { canonical_path: bundle, executable_sha256: null },
result: {
caller_path: alias,
canonical_path: canonical,
executable_sha256: null,
},
});
const decoded = await client.callTool({
@@ -223,7 +231,11 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
subject: null,
normalized_result: {
target_sha256: null,
resource_root: { canonical_path: bundle, executable_sha256: null },
resource_root: {
caller_path: alias,
canonical_path: canonical,
executable_sha256: null,
},
},
});
const decodedEvidenceId = z
@@ -252,10 +264,10 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
),
).toMatchObject({
normalized_result: {
artifact: { path: bundle, sha256: null },
artifact: { path: canonical, sha256: null },
resource_root: {
caller_path: bundle,
canonical_path: bundle,
caller_path: alias,
canonical_path: canonical,
executable_sha256: null,
},
},
@@ -275,7 +287,11 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
normalized_result: {
archive_path: "Resources/Archive.plist",
target_sha256: null,
resource_root: { canonical_path: bundle, executable_sha256: null },
resource_root: {
caller_path: alias,
canonical_path: canonical,
executable_sha256: null,
},
objects: expect.arrayContaining([
expect.objectContaining({ value: "resource-only-value" }),
]),
@@ -288,9 +304,24 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
subject: null,
operation: "inspect_keyed_archive",
normalized_result: {
resource_root: { canonical_path: bundle, executable_sha256: null },
resource_root: {
caller_path: alias,
canonical_path: canonical,
executable_sha256: null,
},
},
});
const closed = await client.callTool({ name: "close_binary", arguments: {} });
expect(closed.isError).not.toBe(true);
const statusAfterClose = await client.callTool({
name: "binary_session",
arguments: {},
});
expect(
toolContract("binary_session").outputSchema.parse(
statusAfterClose.structuredContent,
),
).toMatchObject({ result: { open: false } });
});
const expectResourceSchemaBranches = async (client: Client): Promise<void> => {
-17
View File
@@ -125,23 +125,6 @@ describe("derived MCP operation progress and cancellation", () => {
expect(result.ok ? undefined : result.error.cause).toBe(reason);
});
it("lets cancellation win immediately before result publication", async () => {
const controller = new AbortController();
const result = await runDerivedOperation(
context(controller),
"compare",
() => {
controller.abort();
return "must-not-publish";
},
);
expect(result).toMatchObject({
ok: false,
error: { _tag: "AnalysisCancelledError" },
});
});
it("lets cancellation win while terminal progress delivery is pending", async () => {
const controller = new AbortController();
let terminalReached = false;
@@ -3,7 +3,7 @@ import { expect } from "vitest";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.for(["", " \t "])(
"rejects blank Electron application_root (%j) before filesystem or provider work",
"rejects blank Electron application_root (%j) as invalid input",
async (applicationRoot, { cli }) => {
const response = await cli.run({
arguments: [
@@ -79,7 +79,7 @@ ipcRenderer.send("app:log", 1);`,
for (const surface of ["CLI", "stdio MCP"] as const)
cliTest.for(cases)(
`keeps literal sends unknown beside $shape through ${surface}`,
`classifies literal sends beside $shape through ${surface}`,
async ({ main, preload, pairing }, { cli }) => {
const root = await createTestTempDirectory("rea-electron-dynamic-ipc-");
await writeFixtureFiles(root, {
@@ -6,6 +6,7 @@ import {
symlink,
writeFile,
} from "node:fs/promises";
import { createHash } from "node:crypto";
import { join } from "node:path";
import { createPackageWithOptions, extractFile } from "@electron/asar";
import { expect, onTestFinished } from "vitest";
@@ -28,6 +29,7 @@ cliTest.skipIf(process.platform === "win32")(
const bytes = Buffer.from("selected native companion");
await mkdir(source);
await writeFile(join(source, "native.node"), bytes);
await writeFile(join(source, "packed.txt"), "packed bytes");
await createPackageWithOptions(source, selected, { unpack: "*.node" });
await rename(selected, canonical);
await symlink(canonical, selected);
@@ -54,11 +56,15 @@ cliTest.skipIf(process.platform === "win32")(
const inventory = artifactInventoryResultSchema.parse(
inventoryEvidence.normalized_result,
);
const occurrence = inventory.occurrences.find(
({ logical_path }) => logical_path === "native.node",
);
expect(occurrence?.hash_status).toBe("verified");
expect(
inventory.occurrences.find(
({ logical_path }) => logical_path === "native.node",
)?.hash_status,
).toBe("verified");
inventory.nodes.find(
({ artifact_id }) => artifact_id === occurrence?.artifact_id,
)?.sha256,
).toBe(createHash("sha256").update(bytes).digest("hex"));
const extracted = await cli.run({
arguments: ["extract-artifact", selected, "--json"],
@@ -74,5 +80,8 @@ cliTest.skipIf(process.platform === "win32")(
expect(
(await readFile(join(result.output_root, "native.node"))).equals(bytes),
).toBe(true);
expect(await readFile(join(result.output_root, "packed.txt"), "utf8")).toBe(
"packed bytes",
);
},
);
+1 -1
View File
@@ -16,7 +16,7 @@ import {
} from "../../src/domain/artifactGraph.js";
cliTest(
"inventories and extracts Bun source without executing its carrier",
"inventories and extracts Bun source from its carrier",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-bun-cli-");
const source = join(root, "app");
+1 -1
View File
@@ -387,7 +387,7 @@ cliTest(
);
cliTest(
"rejects repeated-range expansion before hashing members or creating extraction output",
"rejects repeated-range expansion without creating extraction output",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-pck-expansion-");
const source = join(root, "aliased.pck");
@@ -6,7 +6,7 @@ import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
import { cliTest } from "../support/cli/cliFixture.js";
cliTest.skipIf(process.platform === "win32")(
"reports unresolvable source and scenario selections without permission advice",
"reports unresolvable source and scenario selections with path-specific guidance",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-source-path-failures-");
await symlink("loop-b", join(root, "loop-a"));
@@ -58,6 +58,7 @@ cliTest(
expect(await readFile(registrationBackupPath, "utf8")).toBe(original);
expect(refreshed.stderr).toContain(registrationBackupPath);
expect(after.theme).toBe("dark");
expect(after["amp.mcpServers"].other).toEqual({ command: "other" });
// The equals form --agent=amp is parsed as a spaced form before Incur sees
// it, so the Amp registration lands and keeps the environment map.
@@ -69,6 +70,7 @@ cliTest(
command: expect.any(String),
env: { KEEP_ME: "1" },
});
expect(afterEquals["amp.mcpServers"].other).toEqual({ command: "other" });
expect(await readFile(registrationBackupPath, "utf8")).toBe(original);
const recoveryBackupPath = equals.stderr.match(
/Amp MCP registration backup retained at (.+)/,
@@ -91,7 +91,11 @@ cliTest(
const display = await compare(displayPath);
expect(display.exitCode).toBe(0);
const displayResult = bundleComparisonResultSchema.parse(display.json);
expect(displayResult).toMatchObject({ status: "unchanged", changes: [] });
expect(displayResult).toMatchObject({
status: "unchanged",
summary: { records_unchanged: 1, unresolved: 0 },
changes: [],
});
expect(displayResult.left_bundle_sha256).not.toBe(
displayResult.right_bundle_sha256,
);
@@ -55,7 +55,12 @@ it("binds authenticated partial Evidence but preserves raw partial observations"
sha256: "a".repeat(64),
bytes: 1,
},
{ kind: "file-snapshot-hash", path: "/tmp/selected", bytes: 1 },
{
kind: "file-snapshot-hash",
path: "/tmp/selected",
sha256: "a".repeat(64),
bytes: 1,
},
],
] as const) {
const result = await finishCliEvidenceCommand(
@@ -74,5 +79,7 @@ it("binds authenticated partial Evidence but preserves raw partial observations"
expect(result.ok).toBe(false);
if (result.ok) throw new Error("Expected cancellation");
expect(result.error.details?.partial_observation).toMatchObject(expected);
if ("kind" in expected)
expect(result.error.details?.partial_observation).toEqual(expected);
}
});
@@ -85,7 +85,11 @@ const registerParserAdvertisementInventoryTests = (): void => {
it("require exactly the inputs whose omission the parser rejects", async () => {
const cli = createCli({});
const mismatches: string[] = [];
for (const command of createCliInventory(cli).primary) {
const primary = createCliInventory(cli).primary;
expect(primary).toEqual(
expect.arrayContaining(["inspect-pcap-client", "inspect-pcap-capture"]),
);
for (const command of primary) {
const advertised = await advertisedSchema(cli, command);
for (const group of INPUT_GROUPS) {
const parser = parserSchema(cli, command, group);
@@ -0,0 +1,81 @@
import { execFile } from "node:child_process";
import { readFile, readdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { expect, it } from "vitest";
import { parse } from "yaml";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execute = promisify(execFile);
it("enforces workflow platform labels before contacting Cutter", async () => {
const workflow = parse(
await readFile(".github/workflows/real-cutter.yml", "utf8"),
);
const labels = workflow.jobs["cutter-upstream"].strategy.matrix.include.map(
(entry: { platform: string }) => entry.platform,
);
expect(labels).toEqual(["linux", "macOS", "Windows"]);
const root = await createTestTempDirectory("rea-cutter-platform-");
const preload = join(root, "preload.mjs");
await writeFile(
preload,
`
import childProcess from 'node:child_process';
import { syncBuiltinESMExports } from 'node:module';
Object.defineProperty(process, 'platform', { value: process.env.REA_PROBE_PLATFORM });
childProcess.execFile = () => { throw new Error('fixture discovery reached'); };
syncBuiltinESMExports();
`,
);
const check = async (selected: string | undefined, actual: string) => {
const env: NodeJS.ProcessEnv = {
...process.env,
TMPDIR: root,
TMP: root,
TEMP: root,
REA_PROBE_PLATFORM: actual,
};
if (selected === undefined) delete env.EXPECTED_PLATFORM;
else env.EXPECTED_PLATFORM = selected;
const result: unknown = await execute(
process.execPath,
["--import", preload, "scripts/verify-real-cutter.mjs"],
{ env, timeout: 10_000 },
).then(
() => {
throw new Error("Fixture discovery should stop the verifier");
},
(error: unknown) => error,
);
if (
!(result instanceof Error) ||
!("stderr" in result) ||
typeof result.stderr !== "string"
)
throw new Error("Missing verifier diagnostics");
return result.stderr;
};
for (const [selected, actual] of [
["linux", "linux"],
["macOS", "darwin"],
["Windows", "win32"],
] as const)
expect(await check(selected, actual)).toContain(
"fixture discovery reached",
);
expect(await check("linux", "darwin")).toContain(
"Cutter runner did not match its declared platform",
);
expect(await check("unknown", "linux")).toContain(
"Unrecognized Cutter runner platform",
);
expect(await check(undefined, "linux")).toContain(
"fixture discovery reached",
);
expect(
(await readdir(root)).filter((name) =>
name.startsWith("rea-cutter-smoke-"),
),
).toEqual([]);
});
@@ -475,7 +475,7 @@ cliTest(
);
cliTest(
"diagnoses an obsolete explicit-null Evidence commitment without claiming the file was modified",
"diagnoses an obsolete explicit-null Evidence commitment",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-legacy-coverage-identity-");
const path = join(root, "legacy.json");
-9
View File
@@ -2,15 +2,6 @@ import { expect, it } from "vitest";
import { expectConfigurationRefusal, serveCli } from "./cliServeHarness.js";
it.each(["inspect-pcap-client", "inspect-pcap-capture"])(
"%s advertises a schema through the public CLI",
async (command) => {
const result = await serveCli([command, "--schema", "--json"]);
expect(result.exitCode).toBe(0);
expect(JSON.parse(result.stdout)).toBeTruthy();
},
);
it.each([
["inspect-pcap-client"],
["inspect-pcap-capture", "/nonexistent/capture.pcap"],
@@ -373,8 +373,9 @@ it("does not project cancellation cleanup as incomplete after a successful retry
});
it("returns cancellation with the completed observation when the caller interrupts service close", async () => {
const evidence = observation("complete");
const service = {
inspectWithObjdump: vi.fn(async () => ok(observation("complete"))),
inspectWithObjdump: vi.fn(async () => ok(evidence)),
close: vi.fn(async () => {
process.emit("SIGINT");
}),
@@ -382,7 +383,10 @@ it("returns cancellation with the completed observation when the caller interrup
const result = await runObjdump(service);
expect(result).toMatchObject({ code: "cancelled" });
expect(result).toMatchObject({
code: "cancelled",
details: { partial_observation: evidence },
});
expect(process.exitCode).toBe(130);
});
@@ -40,6 +40,7 @@ it.runIf(unsupportedHost)(
encoding: "hex",
});
if (result.ok) throw new Error("Expected a host refusal");
expect(result.error._tag).toBe("AnalysisCapabilityUnavailableError");
const projected = projectAnalysisError(result.error);
expect(projected).toMatchObject({ code: "capability_unavailable" });
expect(projected.message).toContain("Linux x64");
@@ -715,21 +716,6 @@ it.runIf(!unsupportedHost)(
},
);
it.runIf(unsupportedHost)(
"reports unverified host coverage before acquiring a provider",
async () => {
expect(
await new EvmoleInterfaceProvider({}).inspect({
path: "/selected.hex",
encoding: "hex",
}),
).toMatchObject({
ok: false,
error: { _tag: "AnalysisCapabilityUnavailableError" },
});
},
);
it.runIf(!unsupportedHost)(
"preserves actual snapshot-write cancellation and root cleanup",
async () => {
@@ -38,40 +38,6 @@ const fixture = async () => {
return { root, selected, canonical };
};
it.skipIf(process.platform === "win32")(
"inventories and extracts the same selected ASAR companion as Electron tooling",
async () => {
const { root, selected } = await fixture();
expect(extractFile(selected, "native.node").equals(bytes)).toBe(true);
const inventory = await inventoryArtifact(selected);
const occurrence = inventory.occurrences.find(
({ logical_path }) => logical_path === "native.node",
);
expect(occurrence?.hash_status).toBe("verified");
expect(
inventory.nodes.find(
({ artifact_id }) => artifact_id === occurrence?.artifact_id,
)?.sha256,
).toBe(createHash("sha256").update(bytes).digest("hex"));
const result = await withArtifactResourceScope((resourceScope) =>
extractArtifact({
inputPath: selected,
inputFormat: "asar",
outputRoot: join(root, "output"),
environment: {},
integrityPolicy: "fail",
resourceScope,
}),
);
expect(
(await readFile(join(result.output_root, "native.node"))).equals(bytes),
).toBe(true);
expect(await readFile(join(result.output_root, "packed.txt"), "utf8")).toBe(
"packed bytes",
);
},
);
it.skipIf(process.platform === "win32")(
"does not substitute the canonical archive companion for an absent selected companion",
async () => {
@@ -63,14 +63,6 @@ new RuleTester({
expect(bytes).toEqual(value);`,
],
invalid: [
{
// Exact pre-fix ELF test shape: readFile without an encoding returns Buffer.
code: `import { readFile } from "node:fs/promises";
import { expect } from "vitest";
const before = await readFile(path);
expect(await readFile(path)).toEqual(before);`,
errors: [{ messageId: "nativeBytes" }],
},
{
code: `import { readFileSync as read } from "node:fs";
import { expect } from "vitest";
@@ -269,13 +269,20 @@ describe("retention orchestration", () => {
it("deletes nothing when a later list is incomplete", async () => {
const deleted: number[] = [];
const page = Array.from({ length: 100 }, (_, index) =>
cache(index + 1, `${prefix}${String(index)}`, "2026-10-20T01:00:00Z"),
);
const requests: string[] = [];
const page = [
cache(1, `${prefix}old`, "2026-10-20T01:00:00Z"),
cache(2, `${prefix}mid`, "2026-10-20T02:00:00Z"),
cache(3, `${prefix}new`, "2026-10-20T03:00:00Z"),
];
await expect(
retainCaches({
repository,
request: async (_method, endpoint) => {
requests.push(
new URL(endpoint, "https://example.test").searchParams.get("key") ??
"",
);
if (endpoint.includes("npm-download-"))
throw new Error("list failed after the first family");
return { actions_caches: page };
@@ -283,10 +290,39 @@ describe("retention orchestration", () => {
deleteCache: async (id) => {
deleted.push(id);
},
families: [{ prefix, keepKey: `${prefix}0` }],
families: [{ prefix, keepKey: `${prefix}new` }],
dedupeNpmDownloads: true,
}),
).rejects.toThrow(/list failed|exceeded/u);
).rejects.toThrow("list failed after the first family");
expect(requests).toEqual([prefix, "npm-download-"]);
expect(deleted).toEqual([]);
});
});
describe("cache listing and retention", () => {
const repository = "morluto/rea";
const prefix = "rea-static-v1-Linux-X64-";
it("deletes nothing when the cache listing reaches the page cap", async () => {
const deleted: number[] = [];
let requests = 0;
const page = Array.from({ length: 100 }, (_, index) =>
cache(index + 1, `${prefix}${String(index)}`, "2026-10-20T01:00:00Z"),
);
await expect(
retainCaches({
repository,
request: async () => {
requests += 1;
return { actions_caches: page };
},
deleteCache: async (id) => {
deleted.push(id);
},
families: [{ prefix, keepKey: `${prefix}0` }],
}),
).rejects.toThrow(/exceeded.*pages/u);
expect(requests).toBe(10);
expect(deleted).toEqual([]);
});
@@ -12,7 +12,6 @@ import {
parseStaticTaskContext,
recordStaticTaskContext,
} from "../../../scripts/ci/static-task-context.mjs";
import { createGhApiRequest } from "../../../scripts/ci/github-api.mjs";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execute = promisify(execFile);
@@ -339,17 +338,6 @@ it("records the actual static task graph with explicit source identity and no am
expect(() => parseStaticTaskContext(invalid)).toThrow();
});
it("rejects mutation methods at the shared GitHub transport before invoking the CLI", async () => {
const request = createGhApiRequest({ env: { PATH: "" } });
await expect(
request("POST", "repos/owner/repo/actions/runs/1/rerun", {
requests: 0,
startedAt: Date.now(),
limitMs: 1000,
}),
).rejects.toThrow("read-only GET");
});
it("collects basic CI telemetry before optional dependency installation can fail", async () => {
const { jobs } = z
.object({
@@ -91,15 +91,20 @@ it("collects the latest attempt for recent completed CI and Release runs into a
headBranch: null,
},
]);
expect(manifest.runs).toEqual(
expect.arrayContaining([
expect.objectContaining({
status: "collected",
rawPath: expect.any(String),
reportPath: expect.any(String),
}),
]),
);
for (const { id, runAttempt, status, rawPath, reportPath } of manifest.runs) {
const directory = join(
fixture.outputDirectory,
"runs",
`${id}-${runAttempt}`,
);
expect({ status, rawPath, reportPath }).toEqual({
status: "collected",
rawPath: join(directory, "jobs.json"),
reportPath: join(directory, "report.json"),
});
await expect(readFile(rawPath, "utf8")).resolves.toBeTruthy();
await expect(readFile(reportPath, "utf8")).resolves.toBeTruthy();
}
expect(calls).toContainEqual([
"api",
"--paginate",
+38 -18
View File
@@ -11,7 +11,6 @@ import {
matchRunToPullRequest,
writePrCiHealthReport,
} from "../../../scripts/ci/pr-ci-health.mjs";
import { createGhApiRequest } from "../../../scripts/ci/github-api.mjs";
import type {
PrCiPullRequest,
PrCiWorkflowRun,
@@ -453,23 +452,44 @@ it("public CLI uses only GET and produces a report from fixture GitHub responses
it("rejects non-GET methods before launching the GitHub client", async () => {
const fixture = await createFixture();
const request = createGhApiRequest({
env: {
...process.env,
PATH: `${fixture.fakeBin}${delimiter}${process.env.PATH ?? ""}`,
CI_GH_CALLS: join(fixture.directory, "should-not-exist"),
},
});
await expect(
request("POST", "repos/owner/rea/pulls", {
requests: 0,
startedAt: observedAt,
limitMs: 1_000,
}),
).rejects.toThrow("read-only GET");
await expect(
readFile(join(fixture.directory, "should-not-exist")),
).rejects.toMatchObject({ code: "ENOENT" });
const marker = join(fixture.directory, "gh-invoked");
const preload = join(fixture.directory, "watch-gh.cjs");
await writeFile(
preload,
`const fs = require("node:fs");
const util = require("node:util");
const child = require("node:child_process");
child.execFile = () => { throw new Error("Unexpected callback invocation"); };
child.execFile[util.promisify.custom] = async () => {
fs.appendFileSync(process.env.CI_GH_CALLS, "called\\n");
return { stdout: "{}", stderr: "" };
};
require("node:module").syncBuiltinESMExports();
`,
);
const transport = new URL(
"../../../scripts/ci/github-api.mjs",
import.meta.url,
).href;
await executeWorkflowFixture(
process.execPath,
[
"--require",
preload,
"--input-type=module",
"-e",
`
import assert from "node:assert/strict";
import { createGhApiRequest } from ${JSON.stringify(transport)};
const request = createGhApiRequest();
const budget = { requests: 0, startedAt: Date.now(), limitMs: 10_000 };
await assert.rejects(request("POST", "repos/owner/rea/pulls", budget), /read-only GET/);
await request("GET", "repos/owner/rea/pulls", budget);
`,
],
{ cwd: fixture.directory, env: { ...process.env, CI_GH_CALLS: marker } },
);
expect(await readFile(marker, "utf8")).toBe("called\n");
});
function validPull(overrides: Partial<PrCiPullRequest> = {}): PrCiPullRequest {
+23 -20
View File
@@ -132,23 +132,23 @@ it("selects the latest trusted full run that actually published timing history",
expect(output).toBe("run-id=11\nartifact-name=test-timings-full\n");
expect(calls).toContain("name=test-timings-full");
expect(calls).toContain("per_page=100");
expect(calls).not.toContain("actions/runs/13");
expect(calls.match(/actions\/runs\//gu)).toHaveLength(3);
expect(calls).not.toContain("actions/workflows");
});
it("rejects expired, foreign, old, and pull-request candidates", async () => {
it("rejects expired, old, and pull-request candidates", async () => {
const { result, output } = await invoke(
[
artifact(15, { expired: true }),
artifact(14, {
workflow_run: { ...artifact(14).workflow_run, head_repository_id: 2 },
}),
artifact(13, {
created_at: new Date(now - 31 * 24 * 60 * 60 * 1000).toISOString(),
}),
artifact(12),
],
{
15: run(15),
13: run(13),
12: run(12, { event: "pull_request" }),
},
{ tier: "full" },
@@ -197,22 +197,25 @@ it("bounds run lookups to five artifacts even when inventory has more", async ()
expect(calls.match(/actions\/runs\//gu)).toHaveLength(5);
});
it("returns empty history without querying for an invalid repository identity", async () => {
const { result, output, calls } = await invoke(
[],
{},
{
repository: "owner/repo?per_page=100",
repositoryId: "not-an-id",
},
);
expect(JSON.parse(result.stdout)).toEqual({
run_id: "",
artifact_name: "",
});
expect(output).toBe("run-id=\nartifact-name=\n");
expect(calls).toBe("");
});
it.each([
{ repository: "owner/repo?per_page=100", repositoryId: "1" },
{ repository: "example/rea", repositoryId: "not-an-id" },
])(
"returns empty history without querying for an invalid repository identity: %j",
async (identity) => {
const { result, output, calls } = await invoke(
[],
{},
{ ...identity, tier: "full" },
);
expect(JSON.parse(result.stdout)).toEqual({
run_id: "",
artifact_name: "",
});
expect(output).toBe("run-id=\nartifact-name=\n");
expect(calls).toBe("");
},
);
it("selects fast history without reading the full-tier artifact", async () => {
const { result, calls } = await invoke(
@@ -46,6 +46,7 @@ const installation = async (
javac,
[
"#!/bin/sh",
'printf "invoked\\n" >> "$REA_JAVAC_MARKER"',
'if [ "$1" = "-version" ]; then echo "javac 21.0.12"; exit 0; fi',
...(compilerStatus === 0
? ["exit 0"]
@@ -60,8 +61,10 @@ const installation = async (
await mkdir(scratchParent);
return {
scratchParent,
javacMarker: join(root, "javac-invocations"),
environment: {
...process.env,
REA_JAVAC_MARKER: join(root, "javac-invocations"),
GHIDRA_INSTALL_DIR: install,
JAVA_HOME: javaHome,
TMPDIR: scratchParent,
@@ -193,6 +196,9 @@ it.skipIf(process.platform === "win32").each([
),
});
expect(await readdir(fixture.scratchParent)).toEqual([]);
await expect(readFile(fixture.javacMarker)).rejects.toMatchObject({
code: "ENOENT",
});
},
);
@@ -277,6 +277,7 @@ it.each(["read", "capacity", "completed"] as const)(
},
},
});
expect(failure.cause).toBe(reason);
} else if (outcome === "capacity") {
expect(projectAnalysisError(failure).code).toBe("resource_constraint");
} else {
@@ -218,6 +218,7 @@ it.each(["released", "still failing"] as const)(
cause: reason,
cleanupIncomplete: false,
});
expect(cancelled.error.cause).toBe(reason);
expect(first.error.cleanupIncomplete).toBe(false);
expect(registry.hasPendingOwnedResources()).toBe(false);
} else {
@@ -252,7 +253,7 @@ it("keeps the caller's reason and the catalog owner when cancellation lands duri
cleanupIncomplete: true,
cleanupResources: [definition],
});
expect(JSON.stringify(resolved.error.cause)).toContain(reason.caller);
expect(resolved.error.cause).toBe(reason);
expect(provider.hasPendingOwnedResources()).toBe(true);
fault.deny = false;
fault.onClose = undefined;
@@ -39,13 +39,6 @@ it.each(["renderer", "browser", "module"] as const)(
}),
);
const metadata = graph.nodes.find(({ kind }) => kind === "package");
expect(metadata?.observations).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
renderer_declarations: { [field]: "entry.js" },
}),
}),
);
expect(metadata?.observations[0]?.properties).not.toHaveProperty(
"renderer",
);
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff. Show more