test: strengthen integration oracles and verification (#8295)

* test: strengthen integration oracles and verification

* test: bound lifecycle gates and strengthen workflow checks

* test: verify complete paged and extracted results

* test: preserve bytecode and cancellation evidence

* test: strengthen retained evidence and fixture cleanup

* test: require cross-projection bridge identities

* test: retain fixture owners through cleanup failures

* test: verify graph examples and project selectors

* test: tighten provider and lifecycle regressions

* test: consolidate hex coverage and bound cutter cleanup

* test: bound subprocess output waits and strengthen graph checks

* test: check mcp recovery and preserve cleanup owners

* test: verify structured mcp argument failures

* test: preserve artifact cleanup and gdb limit evidence

* test: preserve evidence identities and bound provider gates

* test: retain browser and comparison cleanup owners

* test: bound startup and strengthen fixture oracles

* test: bound lifecycle gates and verify recovered evidence

* test: verify public outcomes and lifecycle ownership

* test: assert precise evidence and release lifecycle gates

* test: verify selected facts and bound cleanup waits

* test: check retained evidence and decoded records

* test: verify final reports and retain cleanup ownership

* test: bound reviewed startup gates and retain cleanup

* test: verify PE errors and preserve MCP cleanup

* test: preserve MCP fixture cleanup ownership

* test: release provider gates before late startup
This commit is contained in:
morluto authored and GitHub committed 2026-11-01 00:15:28 +08:00
1 parent 2d5dec26c3
commit 60149282fc
342 files changed
+8844 -6279

No files matched your search

+5
View File
@@ -53,3 +53,8 @@ jobs:
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
# The preceding verifier builds the runtime used by this request journey.
- run: dbus-run-session -- node scripts/verify-browser-request.mjs
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
mkdir -p .cache
rm -f "$report"
node scripts/run-exact-tests.mjs run --reporter=default --reporter=json --outputFile.json="$report" \
tests/boundary/captures/fieldDiscovery.test.ts src/process/LinuxResourceLimits.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/captures/captureDescriptorCleanup.test.ts tests/boundary/pcap/pcapProvider.test.ts tests/boundary/pcap/pcapProviderCrossPlatform.test.ts tests/boundary/captures/resourceLimits.test.ts tests/boundary/captures/packetOutput.test.ts tests/boundary/mcp/packetCaptureMcp.test.ts tests/boundary/cli/captureCleanup.test.ts
tests/boundary/captures/fieldDiscovery.test.ts src/process/LinuxResourceLimits.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/captures/captureDescriptorCleanup.test.ts tests/boundary/pcap/pcapProvider.test.ts tests/boundary/pcap/pcapProviderCrossPlatform.test.ts tests/boundary/captures/resourceLimits.test.ts tests/boundary/captures/packetOutput.test.ts tests/boundary/mcp/packetCaptureMcp.test.ts tests/boundary/cli/captureCleanup.test.ts tests/acceptance/pcapVerifier.test.ts
node scripts/ci/require-provider-tests.mjs packet "$report"
- run: node scripts/verify-packet-capture.mjs
env:
+1 -1
View File
@@ -291,7 +291,7 @@
"verify:native-ui:permissions": "npm run build:cached && node scripts/verify-native-ui.mjs --allow-permission-denial",
"test:focused": "node scripts/run-development-tests.mjs focused",
"test:sqlite": "npm run test:prepare && npm run test:sqlite:run",
"test:sqlite:run": "vitest run src/application/sqlite/ tests/boundary/sqlite/ tests/acceptance/sqliteDatabaseCli.test.ts tests/boundary/mcp/serverLifecycleMcp.test.ts",
"test:sqlite:run": "node scripts/run-exact-tests.mjs run tests/acceptance/sqliteDatabaseCli.test.ts tests/boundary/mcp/serverLifecycleMcp.test.ts && vitest run src/application/sqlite/ tests/boundary/sqlite/",
"build:windows-native": "node scripts/build-windows-native.mjs",
"verify:windows-native:artifact": "node scripts/verify-windows-native-artifact.mjs",
"prepublishOnly": "npm run verify:windows-native:artifact",
+3
View File
@@ -19,6 +19,9 @@ const descriptors = [
);
const requirements = {
packet: {
"tests/acceptance/pcapVerifier.test.ts": [
"verifies complete and unresolved HTTP coverage with real Wireshark tools",
],
"tests/boundary/captures/resourceLimits.test.ts": [
...resources,
"reports a real kernel file-limit termination with its effective ceiling",
+17
View File
@@ -28,6 +28,8 @@ const PATH_SCOPES = {
"tests/boundary/cli/captureCleanup.test.ts",
"tests/boundary/mcp/packetCaptureMcp.test.ts",
"tests/boundary/pcap/**",
"tests/acceptance/pcapVerifier.test.ts",
"tests/fixtures/pcap/offlineHttpCapture.ts",
"src/captures/**",
"src/pcap/**",
"src/domain/pcap/**",
@@ -43,6 +45,7 @@ const PATH_SCOPES = {
"src/server/registerCaptureTools.ts",
...CAPTURE_SHARED_INPUTS,
"scripts/verify-packet-capture.mjs",
"scripts/verify-pcap.mjs",
".github/workflows/real-packet-capture.yml",
],
signal_recording: [
@@ -351,20 +354,30 @@ const PATH_SCOPES = {
".github/workflows/real-browser.yml",
"scripts/verify-real-browser.mjs",
"scripts/verify-browser-network.mjs",
"scripts/verify-browser-request.mjs",
"scripts/verify-browser-scripts.mjs",
"scripts/verify-browser-modules.mjs",
"scripts/verify-browser-dom-destinations.mjs",
"scripts/lib/browser-*.mjs",
"scripts/fixtures/browser-network-site.mjs",
"scripts/fixtures/browser-request-site.mjs",
"scripts/fixtures/browser-script-site.mjs",
"scripts/fixtures/browser-module-site.mjs",
"scripts/fixtures/browser-verifier-site.mjs",
"scripts/fixtures/page-cdp-proxy.mjs",
"src/browser/**",
"src/domain/browser*.ts",
"src/domain/webCaptureDiffSchemas.ts",
"src/domain/webScreenshot.ts",
"src/domain/webScript*.ts",
"src/domain/webModule*.ts",
"src/domain/analysisView/environment*.ts",
"src/domain/analysisView/{EnvironmentManifest*,EnvironmentState*}.ts",
"src/domain/analysisView/{scenarioCaptureRecords,recordedResponseBody,recordedExchangeBody,recordedMediaType,webNetworkRouteTemplates,WebNetworkProjectionAccumulator}.ts",
"src/application/BrowserScenario*.ts",
"src/application/environment/**",
"src/application/WebModule*.ts",
"src/composition/browserScenario.ts",
"src/composition/webModules.ts",
"src/artifacts/readStableArtifact.ts",
"src/artifacts/ArtifactPaths.ts",
@@ -374,6 +387,8 @@ const PATH_SCOPES = {
"src/server/registerWebModuleTool.ts",
"src/server/createServer.ts",
"src/cli/webModuleCommands.ts",
"src/cliBrowserScenarioCommands.ts",
"src/cli/environmentCommands.ts",
"src/cli.ts",
"src/cliCommandNames.ts",
"src/contracts/toolEffects.ts",
@@ -564,6 +579,8 @@ const PATH_SCOPES = {
".github/workflows/real-web-source-map.yml",
"scripts/verify-browser-source-maps.mjs",
"scripts/lib/browser-source-map-e2e.mjs",
"scripts/lib/browser-nested-source-map-e2e.mjs",
"scripts/lib/browser-runtime-fixture-lifecycle.mjs",
"scripts/fixtures/browser-source-map-site.mjs",
"src/javascript/sourceMaps/**",
"src/browser/assets/**",
@@ -94,6 +94,12 @@ export async function startBrowserRequestSite() {
writes.push({
method: request.method,
path: url.pathname,
query: url.search,
body,
contentType: request.headers["content-type"],
cookie:
request.headers.cookie?.includes(`csrftoken=${requestFixtureCsrf}`) ??
false,
csrf: request.headers["x-csrftoken"] === requestFixtureCsrf,
bearer:
request.headers.authorization === `Bearer ${requestFixtureToken}`,
+17 -4
View File
@@ -67,10 +67,13 @@ const writeSession = (executable, origin) =>
read("list-1", "/api/items"),
read("list-2", "/api/items"),
read("item-1", "/api/items/1"),
write("create-1", "POST", "/api/items", {
json: { title: "Scratch item 1" },
status: 201,
}),
{
...write("create-1", "POST", "/api/items", {
json: { title: "Scratch item 1" },
status: 201,
}),
query: [{ name: "org", value: { source: "literal", value: "1" } }],
},
read("created-1", "/api/items/3"),
read("list-created-1", "/api/items"),
write("create-2", "POST", "/api/items", {
@@ -337,6 +340,16 @@ export async function verifyBrowserRequestSteps(
assertRequestsAttributed(scenario, capture);
assert.equal(site.writes.length, 6);
assert.ok(site.writes.every(({ csrf, bearer }) => csrf && bearer));
assert.deepEqual(site.writes[0], {
method: "POST",
path: "/api/items",
query: "?org=1",
body: { title: "Scratch item 1" },
contentType: "application/json",
cookie: true,
csrf: true,
bearer: true,
});
assertPageValuesRedacted(evidence, capture);
const capturePath = join(root.path, "items.capture.json");
+50 -24
View File
@@ -1,6 +1,7 @@
import { spawn } from "node:child_process";
import { stat } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
developmentTestPlan,
@@ -10,6 +11,7 @@ import {
let child;
let forceTimer;
let shutdownSignal;
let childOwnsTree = false;
const signalExitCodes = { SIGINT: 130, SIGTERM: 143 };
for (const signal of Object.keys(signalExitCodes))
process.on(signal, () => {
@@ -17,8 +19,10 @@ for (const signal of Object.keys(signalExitCodes))
shutdownSignal = signal;
if (child !== undefined && child.exitCode === null) {
child.kill(signal);
forceTimer = setTimeout(() => child?.kill("SIGKILL"), 1_000);
forceTimer.unref?.();
if (!childOwnsTree) {
forceTimer = setTimeout(() => child?.kill("SIGKILL"), 1_000);
forceTimer.unref?.();
}
}
});
@@ -55,28 +59,38 @@ try {
throw new Error(
"Test prerequisites require the locked dependency installation",
);
const prepared = await run(process.execPath, [
"scripts/run-exclusive.mjs",
"artifacts",
const prepared = await run(
process.execPath,
resolve("scripts/run-turbo.mjs"),
"run",
...new Set([
...(plan.needsBuild ? ["artifacts:skills"] : []),
...plan.artifactTasks,
]),
]);
[
"scripts/run-exclusive.mjs",
"artifacts",
process.execPath,
resolve("scripts/run-turbo.mjs"),
"run",
...new Set([
...(plan.needsBuild ? ["artifacts:skills"] : []),
...plan.artifactTasks,
]),
],
false,
"existing",
);
if (prepared.code !== 0) process.exitCode = prepared.code;
}
if (!process.exitCode && !shutdownSignal) {
const tested = await run(process.execPath, [
resolve(
request.paths.length > 0
? "scripts/run-exact-tests.mjs"
: "node_modules/vitest/vitest.mjs",
),
...plan.vitestArguments,
]);
const tested = await run(
process.execPath,
[
resolve(
request.paths.length > 0
? "scripts/run-exact-tests.mjs"
: "node_modules/vitest/vitest.mjs",
),
...plan.vitestArguments,
],
false,
"test",
);
process.exitCode = tested.code;
}
}
@@ -86,15 +100,27 @@ try {
}
if (shutdownSignal) process.exitCode = signalExitCodes[shutdownSignal];
function run(executable, arguments_, capture = false) {
function run(executable, arguments_, capture = false, supervision) {
if (shutdownSignal)
return Promise.resolve({
code: signalExitCodes[shutdownSignal],
output: "",
});
child = spawn(executable, arguments_, {
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
});
// Source-only tests do not require the built Windows Job Object artifact.
const wrap = supervision === "test" && process.platform !== "win32";
childOwnsTree = supervision === "existing" || wrap;
child = spawn(
wrap ? process.execPath : executable,
wrap
? [
fileURLToPath(new URL("./run-exclusive.mjs", import.meta.url)),
"--unlocked",
executable,
...arguments_,
]
: arguments_,
{ stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit" },
);
let output = "";
if (capture) {
child.stdout.on("data", (data) => (output += data.toString()));
+16 -14
View File
@@ -33,7 +33,7 @@ if (!process.connected) {
process.exitCode = signalExitCodes.SIGTERM;
} else if (
typeof lockName !== "string" ||
!/^[a-z][a-z0-9-]*$/u.test(lockName) ||
!(lockName === "--unlocked" || /^[a-z][a-z0-9-]*$/u.test(lockName)) ||
command.length === 0
) {
console.error("Usage: run-exclusive.mjs <lock-name> <command> [args...]");
@@ -45,19 +45,21 @@ if (!process.connected) {
async function run() {
try {
await mkdir(lockRoot, { recursive: true });
// SQLite owns acquisition and OS-backed lock release, including process
// death. Keep the database pathname stable; deleting it would split owners.
database = new DatabaseSync(join(lockRoot, `${lockName}.sqlite`));
try {
database.exec("BEGIN EXCLUSIVE");
} catch (cause) {
if (isBusyError(cause))
throw new Error(
`Another ${lockName} command is already running. Wait for it to finish.`,
{ cause },
);
throw cause;
if (lockName !== "--unlocked") {
await mkdir(lockRoot, { recursive: true });
// SQLite owns acquisition and OS-backed lock release, including process
// death. Keep the database pathname stable; deleting it would split owners.
database = new DatabaseSync(join(lockRoot, `${lockName}.sqlite`));
try {
database.exec("BEGIN EXCLUSIVE");
} catch (cause) {
if (isBusyError(cause))
throw new Error(
`Another ${lockName} command is already running. Wait for it to finish.`,
{ cause },
);
throw cause;
}
}
if (shutdownSignal === undefined) {
const result = await runCommand();
+3 -1
View File
@@ -79,7 +79,9 @@ await withVerifierCleanup(async () => {
(entry) =>
entry.response_address_count > entry.response_addresses.length,
);
assert.equal(result.coverage, partial ? "partial" : "complete");
// Count discrepancies prove partial coverage, but matching counts do not
// rule out unresolved HTTP targets or unavailable DNS identity extraction.
if (partial) assert.equal(result.coverage, "partial");
assert.deepEqual(
await hashVerifierFile(path, resources),
identity,
@@ -11,9 +11,6 @@ import {
APK_SIGNATURE_V3_ID,
LEAF_CERTIFICATE,
LEAF_SHA256,
LEAF_SUBJECT,
OID_ECDSA_SHA256,
OID_SHA256,
OPENSSL_BLOCK_ISSUER_SERIAL,
ROOT_CERTIFICATE,
ROOT_SHA256,
@@ -149,18 +146,6 @@ const reportOf = async (path: string) => {
};
describe("ApkSignatureProvider without the Android SDK", () => {
it("retains the declared digest's signature algorithm ID and bytes", async () => {
const report = await reportOf(
await write(
"algorithm-id.apk",
syntheticSigned([{ id: V2, value: bogusV2Value() }]),
),
);
expect(report.schemes.v2?.signers?.[0]?.digests[0]).toMatchObject({
signature_algorithm_id: 0x0103,
declared_digest_hex: "ab".repeat(32),
});
});
it("reports an unsigned ZIP as having no scheme", async () => {
const r = await reportOf(
await write(
@@ -174,53 +159,6 @@ describe("ApkSignatureProvider without the Android SDK", () => {
expect(r.posture.v1_only).toBe(false);
});
it("detects v1-only signing and names the signer certificate its block declares", async () => {
for (const stored of [false, true]) {
const r = await reportOf(
await write(
`v1-${String(stored)}.apk`,
await zipWith([
{ name: "classes.dex", data: "x" },
{ name: "META-INF/MANIFEST.MF", data: "Manifest-Version: 1.0" },
{ name: "META-INF/CERT.SF", data: "Signature-Version: 1.0" },
{
name: "META-INF/CERT.EC",
data: OPENSSL_BLOCK_ISSUER_SERIAL,
stored,
},
]),
),
);
expect(r.schemes.v1.present).toBe(true);
expect(r.posture.v1_only).toBe(true);
expect(r.posture.strongest_scheme).toBe("v1");
expect(r.posture.v1_signers_match_block_signers).toBeNull();
const [signer] = r.schemes.v1.signers;
expect(signer).toMatchObject({
signature_file: "META-INF/CERT.SF",
block_file: "META-INF/CERT.EC",
certificate: {
sha256_fingerprint: LEAF_SHA256,
subject: LEAF_SUBJECT,
public_key_algorithm: "ec",
},
certificates: [
{ sha256_fingerprint: LEAF_SHA256, subject: LEAF_SUBJECT },
{ sha256_fingerprint: ROOT_SHA256 },
],
signer_infos: [
{
identified_by: "issuer-and-serial",
certificate_sha256: LEAF_SHA256,
digest_algorithm_oid: OID_SHA256,
signature_algorithm_oid: OID_ECDSA_SHA256,
},
],
decode_error: null,
});
}
});
it("decodes a v2 signer, keeps an unparseable certificate null, and flags a digest mismatch", async () => {
const r = await reportOf(
await write(
+1 -1
View File
@@ -60,7 +60,7 @@ describe("v1 signer output projection", () => {
expect(new X509Certificate(certificate).subject).toContain('\\"');
const signer = signerOf(certificate);
const facts = v1SignerFacts(signer);
expect(facts.certificate?.subject).not.toBeNull();
expect(facts.certificate?.subject).toEqual(expect.any(String));
expect(projectedV1SignerBytes(signer)).toBeGreaterThanOrEqual(
Buffer.byteLength(JSON.stringify(facts)),
);
+20 -11
View File
@@ -1,9 +1,10 @@
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { afterEach, expect, it, vi } from "vitest";
import { createDeferred } from "../../tests/fixtures/binarySession.js";
import { AnalysisError } from "../domain/analysisErrorBase.js";
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
import { DarwinProcessOwnershipInspectionError } from "../process/DarwinProcessRunTokenReader.js";
@@ -319,6 +320,7 @@ it("retries failed command peers without repeating successful helper cleanup", a
it("joins preparation during close and retains a late helper handoff", async () => {
const client = provider();
const ready = createDeferred<void>();
const entered = createDeferred<void>();
const cleanup = new ProviderCleanupError(
"process-ownership",
["late-helper"],
@@ -328,6 +330,7 @@ it("joins preparation during close and retains a late helper handoff", async ()
cleanup.resolveCleanup();
});
vi.mocked(prepareOwnedCommand).mockImplementationOnce(async () => {
entered.resolve();
await ready.promise;
throw new DarwinProcessOwnershipInspectionError(
"physical preparation failure",
@@ -335,12 +338,13 @@ it("joins preparation during close and retains a late helper handoff", async ()
);
});
const pending = inspect(client);
await vi.waitFor(() => expect(prepareOwnedCommand).toHaveBeenCalledOnce());
let closed = false;
const closing = client.close().then(() => {
closed = true;
});
try {
let closing: Promise<void> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered.promise, pending, "command preparation");
closing = client.close().then(() => {
closed = true;
});
await Promise.resolve();
expect(closed).toBe(false);
ready.resolve();
@@ -351,11 +355,16 @@ it("joins preparation during close and retains a late helper handoff", async ()
expect(close).toHaveBeenCalledOnce();
expect(result.error.cleanupIncomplete).toBe(false);
expect(runOwnedCommand).not.toHaveBeenCalled();
} finally {
ready.resolve();
await pending;
await closing;
}
}, [
() => ready.resolve(),
async () => {
await pending;
},
async () => {
await closing;
},
() => client.close(),
]);
});
it("preserves an opaque cancellation reason before acquiring a command", async () => {
+34 -20
View File
@@ -1,4 +1,6 @@
import { expect, it } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { AnalysisError } from "../domain/analysisErrorBase.js";
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
import { createDoctorHostFixture } from "./Doctor.fixture.js";
@@ -47,18 +49,24 @@ it("joins a late provider inspection and retries only its unresolved cleanup own
},
});
const inspection = host.providerInspections!();
const signal = await entered;
const closing = host.close!().then(
() => {
closed = true;
return undefined;
},
(cause: unknown) => {
closed = true;
return cause;
},
);
try {
let closing: Promise<unknown> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
entered.then(() => {}),
inspection,
"provider inspection",
);
const signal = await entered;
closing = host.close!().then(
() => {
closed = true;
return undefined;
},
(cause: unknown) => {
closed = true;
return cause;
},
);
expect(signal.aborted).toBe(true);
expect(closed).toBe(false);
handOff();
@@ -78,14 +86,20 @@ it("joins a late provider inspection and retries only its unresolved cleanup own
await expect(host.providerInspections!()).rejects.toThrow(
"scope is closed",
);
} finally {
denied = false;
handOff();
finish();
await inspection;
await closing;
await host.close!();
}
}, [
() => {
denied = false;
handOff();
finish();
},
async () => {
await inspection;
},
async () => {
await closing;
},
() => host.close!(),
]);
});
it("retains a completed healthy report when cancellation arrives during final cleanup", async () => {
+8 -2
View File
@@ -216,7 +216,12 @@ describe("optional Hopper installation failures", () => {
host,
);
expect(result.code).toBe("download_failed");
expect(result.remediation).toMatch(/^Download failed\. ./);
expect(result.remediation).toContain("Download failed.");
expect(result.remediation).toContain("could not be updated");
expect(result.clients.cursor).toMatchObject({
status: "failed",
reason: "write",
});
expect(host.configurations).toBe(1);
});
@@ -234,7 +239,8 @@ describe("optional Hopper installation failures", () => {
host,
);
expect(result.code).toBe("skill_install_failed");
expect(result.remediation).toMatch(/^Download failed\. ./);
expect(result.remediation).toContain("Download failed.");
expect(result.remediation).toContain("injected skill write failure");
expect(host.skillInstalls).toBe(1);
});
});
@@ -540,9 +540,19 @@ it("resolves same-session retained JavaScript Evidence without re-running analys
lookup,
);
if (!item.ok) throw item.error;
expect(JSON.stringify(item.value.normalized_result)).toContain(
"export const secret = 1;",
);
expect(item.value.normalized_result).toMatchObject({
kind: "item",
item: {
path: "renderer.js",
observations: expect.arrayContaining([
expect.objectContaining({
properties: expect.objectContaining({
source: "export const secret = 1;\n",
}),
}),
]),
},
});
});
it("rejects stale retained IDs and unsupported parent operations", async () => {
@@ -859,10 +869,14 @@ it("seals an inferred environment state whose values are the recorded ones", asy
name: "grafana-example",
parent_evidence_id: parent.evidence_id,
timeline: "recorded-start-time",
entities: expect.arrayContaining([
expect.objectContaining({
type: "/api/folders",
id: "fg1jo91e57e2ob",
fields: expect.objectContaining({ title: "probe folder" }),
}),
]),
});
expect(JSON.stringify(sealed.value.normalized_result)).toContain(
"probe folder",
);
expect(parseEvidence(sealed.value)).toEqual(sealed.value);
});
@@ -375,16 +375,47 @@ describe("browser scenario retained boundaries", () => {
rows: [{ transaction_id: "request-2" }],
},
});
expect(result).toMatchObject({
items: {
rows: [
{
events: expect.not.arrayContaining([
expect.objectContaining({
event: expect.objectContaining({ kind: "network-content" }),
}),
]),
omitted_events: expect.arrayContaining([
expect.objectContaining({ kind: "network-content" }),
]),
},
],
},
});
expect(JSON.stringify(result)).not.toContain('"content":');
const selected = await view(capture, {
...request,
event_kinds: ["network-content"],
});
expect(JSON.stringify(selected)).toContain('"content":');
expect(selected).toMatchObject({
items: {
rows: [
{
transaction_id: "request-2",
events: [
expect.objectContaining({
event: expect.objectContaining({
kind: "network-content",
transaction_id: "request-2",
phase: "response",
body: expect.objectContaining({
state: "captured",
content: Buffer.from('{"id":2,"login":"probe"}').toString(
"base64",
),
}),
}),
}),
],
omitted_events: expect.arrayContaining([
{ item_index: expect.any(Number), kind: "request" },
]),
@@ -393,7 +424,9 @@ describe("browser scenario retained boundaries", () => {
},
});
});
});
describe("browser scenario network observation", () => {
it("distinguishes unrequested and legacy-unknown network observation from an observed empty inventory", () => {
const base = scenarioCaptureFixture([], {
request_body: false,
@@ -326,7 +326,9 @@ it("keeps bundle-selection errors useful when operation labels exceed the reply
if (read.ok) throw new Error("Expected a record-selection error");
const encoded = JSON.stringify(read);
expect(Buffer.byteLength(encoded, "utf8")).toBeLessThan(8192);
for (const record of records) expect(encoded).toContain(record.evidence_id);
expect(encoded).toContain("operation omitted");
for (const record of records)
expect(read.error.userMessage).toContain(
`${record.evidence_id} (operation omitted)`,
);
expect(encoded).not.toContain(operation);
});
@@ -1,4 +1,6 @@
import { describe, expect, it } from "vitest";
import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../../tests/fixtures/lifecycleAcknowledgement.js";
import { AnalysisProviderRegistry } from "./AnalysisProviderRegistry.js";
import {
@@ -651,14 +653,15 @@ describe("analysis provider registry: profile and cleanup failures", () => {
selected = true;
return result;
});
await pending;
controller.abort(reason);
let closed = false;
const closing = registry.closeOwnedResources().then((result) => {
closed = true;
return result;
});
try {
let closing: ReturnType<typeof registry.closeOwnedResources> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(pending, selecting, "profile probe");
controller.abort(reason);
closing = registry.closeOwnedResources().then((result) => {
closed = true;
return result;
});
await new Promise<void>((resolve) => setImmediate(resolve));
expect(selected).toBe(false);
expect(closed).toBe(false);
@@ -674,10 +677,18 @@ describe("analysis provider registry: profile and cleanup failures", () => {
expect(await closing).toEqual(ok(null));
expect(registry.hasPendingOwnedResources()).toBe(false);
expect(alpha.created).toEqual([]);
} finally {
finish?.();
await Promise.all([selecting, closing]);
}
}, [
() => {
controller.abort(reason);
finish?.();
},
async () => {
await Promise.allSettled([selecting, closing]);
},
async () => {
expect(await registry.closeOwnedResources()).toEqual(ok(null));
},
]);
});
});
@@ -970,41 +981,62 @@ describe("automatic profile cleanup ownership", () => {
};
}),
);
const result = await registry.select(DATABASE_TARGET, "auto", {
const selecting = registry.select(DATABASE_TARGET, "auto", {
signal: controller.signal,
});
if (result.ok) throw new Error("Mixed failed profiles admitted");
expect(result.error.cleanupIncomplete).toBe(true);
expect(result.error.cleanupResources).toEqual([
"alpha-file",
"beta-file",
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
admitted,
selecting,
"profile children admission",
);
const result = await selecting;
if (result.ok) throw new Error("Mixed failed profiles admitted");
expect(result.error.cleanupIncomplete).toBe(true);
expect(result.error.cleanupResources).toEqual([
"alpha-file",
"beta-file",
]);
expect(result.error.diagnostics?.profile_errors).toEqual(
expect.arrayContaining([
expect.objectContaining({ provider_id: "alpha" }),
expect.objectContaining({ provider_id: "beta" }),
]),
);
const close1 = await registry.closeOwnedResources();
expect(close1.ok).toBe(false);
released.add(first);
const close2 = await registry.closeOwnedResources();
expect(close2.ok).toBe(false);
expect(result.error.cleanupIncomplete).toBe(true);
expect(result.error.cleanupResources).toEqual([
first === "alpha" ? "beta-file" : "alpha-file",
]);
expect(close1.ok ? [] : close1.error.cleanupResources).toEqual([
first === "alpha" ? "beta-file" : "alpha-file",
]);
released.add(first === "alpha" ? "beta" : "alpha");
expect(await registry.closeOwnedResources()).toEqual(ok(null));
expect(result.error.cleanupIncomplete).toBe(false);
if (close1.ok || close2.ok)
throw new Error("Cleanup unexpectedly succeeded");
expect(close1.error.cleanupIncomplete).toBe(false);
expect(close2.error.cleanupIncomplete).toBe(false);
expect(registry.hasPendingOwnedResources()).toBe(false);
}, [
() => {
released.add("alpha");
released.add("beta");
controller.abort(reason);
admitChildren?.();
},
async () => {
await Promise.allSettled([selecting]);
},
async () => {
expect(await registry.closeOwnedResources()).toEqual(ok(null));
},
]);
expect(result.error.diagnostics?.profile_errors).toEqual(
expect.arrayContaining([
expect.objectContaining({ provider_id: "alpha" }),
expect.objectContaining({ provider_id: "beta" }),
]),
);
const close1 = await registry.closeOwnedResources();
expect(close1.ok).toBe(false);
released.add(first);
const close2 = await registry.closeOwnedResources();
expect(close2.ok).toBe(false);
expect(result.error.cleanupIncomplete).toBe(true);
expect(result.error.cleanupResources).toEqual([
first === "alpha" ? "beta-file" : "alpha-file",
]);
expect(close1.ok ? [] : close1.error.cleanupResources).toEqual([
first === "alpha" ? "beta-file" : "alpha-file",
]);
released.add(first === "alpha" ? "beta" : "alpha");
expect(await registry.closeOwnedResources()).toEqual(ok(null));
expect(result.error.cleanupIncomplete).toBe(false);
if (close1.ok || close2.ok)
throw new Error("Cleanup unexpectedly succeeded");
expect(close1.error.cleanupIncomplete).toBe(false);
expect(close2.error.cleanupIncomplete).toBe(false);
expect(registry.hasPendingOwnedResources()).toBe(false);
},
);
});
@@ -1034,10 +1066,12 @@ it("registers before production and gates fresh selections until shutdown has jo
},
]);
const selecting = registry.select(DATABASE_TARGET, "alpha");
await entered;
const closing = registry.closeOwnedResources();
const next = registry.select(DATABASE_TARGET, "alpha");
try {
let closing: ReturnType<typeof registry.closeOwnedResources> | undefined;
let next: ReturnType<typeof registry.select> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered, selecting, "profile finalization");
closing = registry.closeOwnedResources();
next = registry.select(DATABASE_TARGET, "alpha");
await new Promise<void>((resolve) => setImmediate(resolve));
expect(calls).toBe(1);
expect(registry.hasPendingOwnedResources()).toBe(true);
@@ -1050,11 +1084,15 @@ it("registers before production and gates fresh selections until shutdown has jo
expect((await next).ok).toBe(true);
expect(calls).toBe(2);
expect(registry.hasPendingOwnedResources()).toBe(false);
} finally {
finish?.();
await Promise.all([selecting, closing, next]);
await registry.closeOwnedResources();
}
}, [
() => finish?.(),
async () => {
await Promise.allSettled([selecting, closing, next]);
},
async () => {
expect(await registry.closeOwnedResources()).toEqual(ok(null));
},
]);
});
it("joins cancelled readiness finalization and shutdown before admitting another observation", async () => {
@@ -1094,15 +1132,17 @@ it("joins cancelled readiness finalization and shutdown before admitting another
settled = true;
return cause;
});
await entered;
controller.abort(reason);
let closed = false;
const closing = registry.closeOwnedResources().then((value) => {
closed = true;
return value;
});
const next = registry.candidates();
try {
let closing: ReturnType<typeof registry.closeOwnedResources> | undefined;
let next: ReturnType<typeof registry.candidates> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered, observing, "readiness observation");
controller.abort(reason);
closing = registry.closeOwnedResources().then((value) => {
closed = true;
return value;
});
next = registry.candidates();
await new Promise<void>((resolve) => setImmediate(resolve));
expect(settled).toBe(false);
expect(closed).toBe(false);
@@ -1125,11 +1165,18 @@ it("joins cancelled readiness finalization and shutdown before admitting another
expect(await registry.candidates()).toHaveLength(1);
expect(calls).toBe(2);
expect(registry.hasPendingOwnedResources()).toBe(false);
} finally {
finish();
await Promise.allSettled([observing, closing, next]);
await registry.closeOwnedResources();
}
}, [
() => {
controller.abort(reason);
finish();
},
async () => {
await Promise.allSettled([observing, closing, next]);
},
async () => {
expect(await registry.closeOwnedResources()).toEqual(ok(null));
},
]);
});
it.each(["alpha", "beta"])(
@@ -1208,8 +1255,12 @@ it("joins every candidate and retains physical failure with exact mixed cleanup
settled = true;
return cause;
});
await entered;
try {
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
entered,
observing,
"mixed readiness observation",
);
await new Promise<void>((resolve) => setImmediate(resolve));
expect(settled).toBe(false);
finish();
@@ -1245,11 +1296,15 @@ it("joins every candidate and retains physical failure with exact mixed cleanup
details: { diagnostics: { reason: "physical observation failed" } },
});
expect(registry.hasPendingOwnedResources()).toBe(false);
} finally {
finish();
await observing;
await registry.closeOwnedResources();
}
}, [
finish,
async () => {
await observing;
},
async () => {
expect(await registry.closeOwnedResources()).toEqual(ok(null));
},
]);
});
it("registers selection before async readiness can race shutdown", async () => {
@@ -1,8 +1,10 @@
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
import { awaitAcknowledgement } from "../../../tests/fixtures/lifecycleAcknowledgement.js";
import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs";
import { elf } from "../../domain/binaryTarget.fixture.js";
import { describe, expect, it } from "vitest";
import { describe, expect, it, onTestFinished } from "vitest";
import {
createAnalysisExecution,
@@ -42,76 +44,68 @@ import { SessionProviderRouter } from "./SessionProviderRouter.js";
*/
it("binds omitted provider subjects to the selected target", async () => {
const { session } = openSession();
try {
const opened = await session.open(process.execPath);
expect(opened.ok).toBe(true);
const execution = await session.execute("address_name", {});
expect(execution.ok).toBe(true);
if (!execution.ok || !opened.ok) return;
expect(execution.value.subject).toEqual({
path: opened.value.path,
sha256: opened.value.sha256,
format: opened.value.format,
...(opened.value.architecture === undefined
? {}
: { architecture: opened.value.architecture }),
});
} finally {
await session.close();
}
const opened = await session.open(process.execPath);
expect(opened.ok).toBe(true);
const execution = await session.execute("address_name", {});
expect(execution.ok).toBe(true);
if (!execution.ok || !opened.ok) return;
expect(execution.value.subject).toEqual({
path: opened.value.path,
sha256: opened.value.sha256,
format: opened.value.format,
...(opened.value.architecture === undefined
? {}
: { architecture: opened.value.architecture }),
});
});
it("preserves an explicitly unbound provider observation", async () => {
const { session } = openSession({ explicitNullSubject: true });
try {
expect((await session.open(process.execPath)).ok).toBe(true);
const execution = await session.execute("address_name", {});
expect(execution.ok).toBe(true);
if (execution.ok) expect(execution.value.subject).toBeNull();
} finally {
await session.close();
}
expect((await session.open(process.execPath)).ok).toBe(true);
const execution = await session.execute("address_name", {});
expect(execution.ok).toBe(true);
if (execution.ok) expect(execution.value.subject).toBeNull();
});
it("retains live source Evidence without replaying a prior observation", async () => {
const { session, deep } = openSession({ live: true });
try {
expect((await session.open(process.execPath)).ok).toBe(true);
expect(
(
await session.execute("address_name", {
document: "0",
address: "0x9999",
})
).ok,
).toBe(true);
const child = deep.created[0];
if (child === undefined) throw new Error("expected provider child");
let reads = 0;
child.execute = () =>
Promise.resolve(
ok(createAnalysisExecution(`live ${++reads}`, deepIdentity)),
);
const parameters = { document: "0", address: "0x1000" };
const first = await session.execute("address_name", parameters);
const second = await session.execute("address_name", parameters);
expect(first.ok && first.value.result).toBe("live 1");
expect(second.ok && second.value.result).toBe("live 2");
if (!first.ok || !second.ok) return;
expect(first.value.evidenceId).toBeDefined();
expect(second.value.evidenceId).toBeDefined();
expect(first.value.evidenceId).not.toBe(second.value.evidenceId);
expect(session.exportEvidenceBundle().records).toEqual(
expect.arrayContaining([
expect.objectContaining({ normalized_result: "live 1" }),
expect.objectContaining({ normalized_result: "live 2" }),
]),
expect((await session.open(process.execPath)).ok).toBe(true);
expect(
(
await session.execute("address_name", {
document: "0",
address: "0x9999",
})
).ok,
).toBe(true);
const child = deep.created[0];
if (child === undefined) throw new Error("expected provider child");
let reads = 0;
child.execute = () =>
Promise.resolve(
ok(createAnalysisExecution(`live ${++reads}`, deepIdentity)),
);
const snapshot = session.exportAnalysisSnapshot();
if (snapshot.ok) expect(snapshot.value.entries).toHaveLength(0);
} finally {
await session.close();
}
const parameters = { document: "0", address: "0x1000" };
const first = await session.execute("address_name", parameters);
const second = await session.execute("address_name", parameters);
expect(first.ok && first.value.result).toBe("live 1");
expect(second.ok && second.value.result).toBe("live 2");
if (!first.ok || !second.ok) return;
expect(first.value.evidenceId).toBeDefined();
expect(second.value.evidenceId).toBeDefined();
expect(first.value.evidenceId).not.toBe(second.value.evidenceId);
expect(session.exportEvidenceBundle().records).toEqual(
expect.arrayContaining([
expect.objectContaining({ normalized_result: "live 1" }),
expect.objectContaining({ normalized_result: "live 2" }),
]),
);
const snapshot = session.exportAnalysisSnapshot();
expect(snapshot.ok, snapshot.ok ? undefined : snapshot.error.message).toBe(
true,
);
if (!snapshot.ok) return;
expect(snapshot.value.entries).toEqual([]);
});
it("keeps unbound observations out of persisted target snapshots", async () => {
@@ -119,93 +113,84 @@ it("keeps unbound observations out of persisted target snapshots", async () => {
const directory = await createTestTempDirectory("rea-unbound-snapshot-");
const path = join(directory, "snapshot.json");
const parameters = { document: "0", address: "0x1000" };
try {
expect((await session.open(process.execPath)).ok).toBe(true);
const bound = await session.execute("address_name", parameters);
expect(bound.ok).toBe(true);
const child = deep.created[0];
if (child === undefined || !bound.ok)
throw new Error("Expected bound read");
child.execute = () =>
Promise.resolve(
ok(
createAnalysisExecution("unbound observation", deepIdentity, {
subject: null,
}),
),
);
const execution = await session.execute("address_name", {
...parameters,
address: "0x2000",
});
expect(execution.ok).toBe(true);
if (!execution.ok) return;
expect(execution.value.subject).toBeNull();
expect(session.exportEvidenceBundle().records).toEqual(
expect.arrayContaining([
expect.objectContaining({
expect((await session.open(process.execPath)).ok).toBe(true);
const bound = await session.execute("address_name", parameters);
expect(bound.ok).toBe(true);
const child = deep.created[0];
if (child === undefined || !bound.ok) throw new Error("Expected bound read");
child.execute = () =>
Promise.resolve(
ok(
createAnalysisExecution("unbound observation", deepIdentity, {
subject: null,
normalized_result: "unbound observation",
}),
]),
),
);
const snapshot = session.exportAnalysisSnapshot();
expect(snapshot.ok, snapshot.ok ? undefined : snapshot.error.message).toBe(
true,
const execution = await session.execute("address_name", {
...parameters,
address: "0x2000",
});
expect(execution.ok).toBe(true);
if (!execution.ok) return;
expect(execution.value.subject).toBeNull();
expect(session.exportEvidenceBundle().records).toEqual(
expect.arrayContaining([
expect.objectContaining({
subject: null,
normalized_result: "unbound observation",
}),
]),
);
const snapshot = session.exportAnalysisSnapshot();
expect(snapshot.ok, snapshot.ok ? undefined : snapshot.error.message).toBe(
true,
);
if (!snapshot.ok) return;
expect(snapshot.value.entries).toHaveLength(1);
expect(snapshot.value.entries[0]?.parameters).toEqual(parameters);
const unboundReplay = await session.execute("address_name", {
...parameters,
address: "0x2000",
});
expect(unboundReplay.ok).toBe(true);
if (unboundReplay.ok) {
expect(unboundReplay.value.subject).toBeNull();
expect(unboundReplay.value.evidenceId).toBe(execution.value.evidenceId);
}
expect(
(await session.writeAnalysisSnapshot(snapshot.value, path, false)).ok,
).toBe(true);
expect((await session.close()).ok).toBe(true);
expect(
(
await session.open(process.execPath, {
loadSnapshot: () => session.readAnalysisSnapshot(path),
})
).ok,
).toBe(true);
const replay = await session.execute("address_name", parameters);
expect(replay.ok).toBe(true);
if (replay.ok) {
expect(replay.value.evidenceId).toBe(bound.value.evidenceId);
expect(replay.value.limitations).toContain(
"Loaded from a local REA analysis snapshot; this call did not re-run the provider.",
);
if (!snapshot.ok) return;
expect(snapshot.value.entries).toHaveLength(1);
expect(snapshot.value.entries[0]?.parameters).toEqual(parameters);
const unboundReplay = await session.execute("address_name", {
...parameters,
address: "0x2000",
});
expect(unboundReplay.ok).toBe(true);
if (unboundReplay.ok) {
expect(unboundReplay.value.subject).toBeNull();
expect(unboundReplay.value.evidenceId).toBe(execution.value.evidenceId);
}
expect(
(await session.writeAnalysisSnapshot(snapshot.value, path, false)).ok,
).toBe(true);
expect((await session.close()).ok).toBe(true);
expect(
(
await session.open(process.execPath, {
loadSnapshot: () => session.readAnalysisSnapshot(path),
})
).ok,
).toBe(true);
const replay = await session.execute("address_name", parameters);
expect(replay.ok).toBe(true);
if (replay.ok) {
expect(replay.value.evidenceId).toBe(bound.value.evidenceId);
expect(replay.value.limitations).toContain(
"Loaded from a local REA analysis snapshot; this call did not re-run the provider.",
);
}
} finally {
await session.close();
}
});
describe("BinarySession open liveness", () => {
it("retires a locally blocked owner even when its native health is idle", async () => {
const { session, deep } = openSession();
try {
expect((await session.open(process.execPath)).ok).toBe(true);
expect((await session.execute("address_name", {})).ok).toBe(true);
const retained = deep.created[0];
if (retained === undefined) throw new Error("Expected an acquired child");
retained.reuseReadiness = () => "blocked";
expect(retained.operationHealthSnapshot?.()?.state).toBe("idle");
expect((await session.open(process.execPath)).ok).toBe(true);
expect(retained.closeCalled).toBe(true);
expect((await session.execute("address_name", {})).ok).toBe(true);
expect(deep.created).toHaveLength(2);
} finally {
await session.close();
}
expect((await session.open(process.execPath)).ok).toBe(true);
expect((await session.execute("address_name", {})).ok).toBe(true);
const retained = deep.created[0];
if (retained === undefined) throw new Error("Expected an acquired child");
retained.reuseReadiness = () => "blocked";
expect(retained.operationHealthSnapshot?.()?.state).toBe("idle");
expect((await session.open(process.execPath)).ok).toBe(true);
expect(retained.closeCalled).toBe(true);
expect((await session.execute("address_name", {})).ok).toBe(true);
expect(deep.created).toHaveLength(2);
});
it("reuses the retained client while the child stays live", async () => {
const { session, deep } = openSession();
@@ -368,6 +353,7 @@ it("clears retired runtime availability after cancelled-switch restoration", asy
});
const { session, deep } = openSession({
failOperationInitially: true,
releaseBeforeCleanup: releaseClose,
beforeFirstClose: async () => {
closeStarted();
await closeGate;
@@ -394,11 +380,17 @@ it("clears retired runtime availability after cancelled-switch restoration", asy
);
await writeFile(alternate, elf(2, 1, 62));
const switching = session.open(alternate, { signal: controller.signal });
await closing;
controller.abort();
releaseClose();
const cancelled = await switching;
const cancelled = await withVerifierCleanup(async () => {
await awaitAcknowledgement(closing, switching, "provider close");
controller.abort();
releaseClose();
return await switching;
}, [
releaseClose,
async () => {
await switching;
},
]);
expect(cancelled).toMatchObject({
ok: false,
error: { _tag: "AnalysisCancelledError" },
@@ -435,7 +427,6 @@ it("clears retired runtime availability after cancelled-switch restoration", asy
reason: "available",
remediation: null,
});
await session.close();
});
const sessionRunId = (
@@ -498,6 +489,7 @@ const openSession = (
operationHealth?: boolean;
failOperationInitially?: boolean;
beforeFirstClose?: () => Promise<void>;
releaseBeforeCleanup?: () => void;
explicitNullSubject?: boolean;
live?: boolean;
} = {},
@@ -527,6 +519,11 @@ const openSession = (
]),
new InvestigationRecords(),
);
onTestFinished(async () => {
options.releaseBeforeCleanup?.();
const closed = await session.close();
if (!closed.ok) throw closed.error;
});
return { session, deep: { created }, onProbe };
};
@@ -1,4 +1,6 @@
import { expect, it } from "vitest";
import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../../tests/fixtures/lifecycleAcknowledgement.js";
import { DeferredAnalysisClient } from "../../providers/DeferredAnalysisClient.js";
import {
createAnalysisExecution,
@@ -164,8 +166,12 @@ it("keeps a shared creation alive for an interested peer", async () => {
reason = { request: "one peer leaves" };
const first = client.execute("health", {}, { signal: controller.signal });
const second = client.execute("health", {});
try {
await entered.promise;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
entered.promise,
first,
"shared client creation",
);
controller.abort(reason);
const cancelled = await first;
if (cancelled.ok) throw new Error("Expected peer cancellation");
@@ -181,11 +187,17 @@ it("keeps a shared creation alive for an interested peer", async () => {
expect(creates).toBe(1);
expect(await client.close()).toEqual(ok(null));
expect(closes).toBe(1);
} finally {
loading.resolve(child());
await Promise.allSettled([first, second]);
await client.close();
}
}, [
() => controller.abort(),
() => loading.resolve(child()),
async () => {
await Promise.allSettled([first, second]);
},
async () => {
const closed = await client.close();
if (!closed.ok) throw closed.error;
},
]);
});
it("joins a late acquired client's final cleanup before cancellation settles", async () => {
@@ -207,8 +219,8 @@ it("joins a late acquired client's final cleanup before cancellation settles", a
settled = true;
return result;
});
try {
await entered.promise;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered.promise, pending, "client creation");
controller.abort(reason);
loading.resolve(
child(async () => {
@@ -218,7 +230,7 @@ it("joins a late acquired client's final cleanup before cancellation settles", a
return ok(null);
}),
);
await closeEntered.promise;
await awaitAcknowledgement(closeEntered.promise, pending, "client cleanup");
expect(settled).toBe(false);
expect(client.operationHealthSnapshot()?.state).toBe("idle");
expect(client.reuseReadiness()).toBe("blocked");
@@ -229,12 +241,18 @@ it("joins a late acquired client's final cleanup before cancellation settles", a
expect(result.error.cleanupIncomplete).toBe(false);
expect(await client.close()).toEqual(ok(null));
expect(closes).toBe(1);
} finally {
loading.resolve(child());
closing.resolve();
await pending;
await client.close();
}
}, [
() => controller.abort(),
() => loading.resolve(child()),
() => closing.resolve(),
async () => {
await pending;
},
async () => {
const closed = await client.close();
if (!closed.ok) throw closed.error;
},
]);
});
it("keeps a producer-authored physical failure primary even when it is the caller's abort reason", async () => {
@@ -256,8 +274,8 @@ it("keeps a producer-authored physical failure primary even when it is the calle
{},
{ signal: controller.signal },
);
try {
await entered.promise;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered.promise, pending, "client creation");
controller.abort(physical);
finishing.resolve();
const result = await pending;
@@ -272,11 +290,17 @@ it("keeps a producer-authored physical failure primary even when it is the calle
expect(result.error.cause).toBe(physical);
expect(physical.cause).toBeUndefined();
expect(projectAnalysisError(result.error).code).toBe("execution_failure");
} finally {
finishing.resolve();
await pending;
await client.close();
}
}, [
() => controller.abort(),
() => finishing.resolve(),
async () => {
await pending;
},
async () => {
const closed = await client.close();
if (!closed.ok) throw closed.error;
},
]);
});
it("retains a failed late client's exact close owner and restores cancellation after confirmed retry", async () => {
@@ -1,4 +1,6 @@
import { expect, it } from "vitest";
import { withVerifierCleanup } from "../../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../../tests/fixtures/lifecycleAcknowledgement.js";
import { parseBinaryTarget } from "../BinaryTargetResolver.js";
import type {
@@ -7,7 +9,6 @@ import type {
} from "../AnalysisProvider.js";
import { createAnalysisExecution } from "../AnalysisProvider.js";
import { LazyAnalysisProvider } from "./LazyAnalysisProvider.js";
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
import { ok as resultOk } from "../../domain/result.js";
const identity: ProviderIdentity = {
@@ -66,14 +67,15 @@ it("joins a cancelled unabortable module load and refuses late client acquisitio
const entered = new Promise<void>((resolve) => {
enter = resolve;
});
const loading = new Promise<AnalysisProvider>((resolve) => {
finishLoading = resolve;
});
const provider = new LazyAnalysisProvider({
identity,
capabilities: [],
load: () => {
enter();
return new Promise((resolve) => {
finishLoading = resolve;
});
return loading;
},
});
const controller = new AbortController();
@@ -86,9 +88,9 @@ it("joins a cancelled unabortable module load and refuses late client acquisitio
settled = true;
return value;
});
await entered;
controller.abort(reason);
try {
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered, pending, "provider module load");
controller.abort(reason);
await new Promise<void>((resolve) => setImmediate(resolve));
expect(settled).toBe(false);
finishLoading(
@@ -105,11 +107,17 @@ it("joins a cancelled unabortable module load and refuses late client acquisitio
expect(result.error.cause).toBe(reason);
expect(creates).toBe(0);
expect(await client.close()).toEqual(resultOk(null));
} finally {
finishLoading(providerWithClient());
await pending;
await client.close();
}
}, [
() => controller.abort(),
() => finishLoading(providerWithClient()),
async () => {
await pending;
},
async () => {
const closed = await client.close();
if (!closed.ok) throw closed.error;
},
]);
});
it("reports that a lazy client was closed before its first operation", async () => {
@@ -121,7 +121,7 @@ it("preserves the caller's exact abort reason before invocation", async () => {
expect(calls).toBe(0);
});
it("cancels before invocation and after an acquired observation", async () => {
it("retains the acquired observation when cancellation arrives during inspection", async () => {
const controller = new AbortController();
const service = new BinaryLayoutService({
inspectAndClose(input, options) {
@@ -154,21 +154,6 @@ it("cancels before invocation and after an acquired observation", async () => {
normalized_result: binaryLayoutFixture(),
});
}
const untouched = new BinaryLayoutService({
inspectAndClose(input, options) {
return this.inspect(input, options);
},
identity: BINARY_LAYOUT_TEST_PROVIDER,
inspect: () => {
throw new Error("must not run");
},
});
expect(
await untouched.inspect(
{ path: "/source.elf" },
{ signal: controller.signal },
),
).toMatchObject({ ok: false, error: { _tag: "AnalysisCancelledError" } });
});
it.each(["different-path", "invalid-digest", "outside-range"])(
@@ -49,20 +49,6 @@ it("rejects a relative path and extra arguments before reading", async () => {
expect(calls).toBe(0);
});
it("records the selected PE and an absent certificate table", async () => {
const result = await new PeCertificatesService({
identity,
inspect: () => Promise.resolve(ok(report)),
}).inspect({ path: report.artifact.path });
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
expect(result.value).toMatchObject({
confidence: "observed",
subject: { format: "pe", architecture: "x86" },
normalized_result: report,
});
});
it("discards an observation if the caller cancels while the provider completes", async () => {
const controller = new AbortController();
const service = new PeCertificatesService({
@@ -62,20 +62,6 @@ it("rejects a relative path and extra arguments before reading", async () => {
expect(calls).toBe(0);
});
it("records the selected PE and an absent import table", async () => {
const result = await new PeImportsService({
identity,
inspect: () => Promise.resolve(ok(report)),
}).inspect({ path: report.artifact.path });
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
expect(result.value).toMatchObject({
confidence: "observed",
subject: { format: "pe", architecture: "x86_64" },
normalized_result: report,
});
});
it.each(["imports", "delay_imports", "examined_dlls"] as const)(
"rejects contradictory complete %s coverage",
async (field) => {
@@ -128,7 +128,7 @@ it("preserves the caller's exact abort reason before invocation", async () => {
expect(calls).toBe(0);
});
it("cancels before invocation and after an acquired observation", async () => {
it("retains the acquired observation when cancellation arrives during inspection", async () => {
const controller = new AbortController();
const service = new RecordedCrashService({
inspectAndClose(input, options) {
@@ -161,21 +161,6 @@ it("cancels before invocation and after an acquired observation", async () => {
normalized_result: recordedCrashFixture(),
});
}
const untouched = new RecordedCrashService({
inspectAndClose(input, options) {
return this.inspect(input, options);
},
identity: RECORDED_CRASH_TEST_PROVIDER,
inspect: () => {
throw new Error("must not run");
},
});
expect(
await untouched.inspect(
{ path: "/source.elf" },
{ signal: controller.signal },
),
).toMatchObject({ ok: false, error: { _tag: "AnalysisCancelledError" } });
});
it.each([
@@ -378,10 +378,15 @@ describe("buildEnvironment cancellation and input checks", () => {
const built = await buildEnvironment(input);
if (built.ok) throw new Error("Expected a cancellation");
const projected = projectAnalysisError(built.error);
expect(projected.code).toBe("cancelled");
expect(JSON.stringify(projected)).toContain(
`"output_directory":${JSON.stringify(input.directory)}`,
);
expect(projected).toMatchObject({
code: "cancelled",
details: {
diagnostics: {
output_directory: input.directory,
bundle_written: true,
},
},
});
expect(await listFiles(input.directory!)).toHaveLength(7);
});
@@ -89,6 +89,7 @@ describe("loadEnvironmentState", () => {
const notEvidence = loadEnvironmentState({}, source);
if (notEvidence.ok || !(notEvidence.error instanceof AnalysisInputError))
throw new Error("Expected an input refusal");
expect(notEvidence.error.issues.length).toBeGreaterThan(0);
expect(
notEvidence.error.issues.every((issue) => issue.path[0] === "state"),
).toBe(true);
@@ -61,21 +61,6 @@ it("rejects a relative path and extra arguments before reading", async () => {
expect(calls).toBe(0);
});
it("records the selected BEAM form", async () => {
const result = await new BeamService({
close: async () => {},
identity,
inspect: () => Promise.resolve(ok(report)),
}).inspect({ path: report.artifact.path });
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
expect(result.value).toMatchObject({
confidence: "observed",
subject: { format: "file" },
normalized_result: report,
});
});
it("discards an observation if the caller cancels while the provider completes", async () => {
const controller = new AbortController();
const reason = { source: "caller-request", operation: "cancel" };
@@ -77,9 +77,13 @@ it("queries an empty event name as an exact retained event identity", () => {
}),
);
expect(trace.seed_node_ids.length).toBeGreaterThan(0);
expect(
trace.nodes
.filter(({ node_id }) => trace.seed_node_ids.includes(node_id))
.every(({ properties }) => properties.event_name === ""),
).toBe(true);
const seeds = trace.nodes.filter(({ node_id }) =>
trace.seed_node_ids.includes(node_id),
);
expect(seeds.map(({ node_id }) => node_id).sort()).toEqual(
[...trace.seed_node_ids].sort(),
);
expect(seeds.every(({ properties }) => properties.event_name === "")).toBe(
true,
);
});
@@ -63,25 +63,6 @@ it.each([
},
);
it("preserves the selected worker budget in provider input and Evidence", async () => {
const report = { ...fixture(), worker_timeout_ms: 1234 };
const service = new SqliteDatabaseService({
inspect: async (input) => {
expect(input.worker_timeout_ms).toBe(1234);
return ok(execution(report));
},
});
const result = await service.inspect({
path: "/selected.db",
worker_timeout_ms: 1234,
});
if (!result.ok) throw result.error;
expect(result.value.parameters).toMatchObject({ worker_timeout_ms: 1234 });
expect(result.value.normalized_result).toMatchObject({
worker_timeout_ms: 1234,
});
});
it.each([
{ selected: undefined, reported: undefined },
{ selected: undefined, reported: 1234 },
@@ -109,18 +90,6 @@ it.each([
},
);
it("retains the actual default without inventing an explicit caller selection", async () => {
const service = new SqliteDatabaseService({
inspect: async () => ok(execution(fixture())),
});
const result = await service.inspect({ path: "/selected.db" });
if (!result.ok) throw result.error;
expect(result.value.normalized_result).toMatchObject({
worker_timeout_ms: 30_000,
});
expect(result.value.parameters).not.toHaveProperty("worker_timeout_ms");
});
it("accepts sqlite_schema rows without requiring a fabricated declaration", async () => {
const report = {
...fixture(),
+3 -89
View File
@@ -1,4 +1,4 @@
import { readFile, writeFile } from "node:fs/promises";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
@@ -14,9 +14,6 @@ import {
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
import { scanCanonicalArtifactInventory } from "../../tests/fixtures/artifactInventory.js";
const INVENTORY_LIMITATION =
"Intel HEX/S-record inventory decodes data records into address-contiguous regions. It does not establish CPU, byte order, memory map, or that a declared start address is executable; gaps are unwritten, not zero.";
const writeImage = async (name: string, text: string): Promise<string> => {
const directory = await createTestTempDirectory("rea-hex-records-");
const path = join(directory, name);
@@ -24,60 +21,6 @@ const writeImage = async (name: string, text: string): Promise<string> => {
return path;
};
it("inventories Intel HEX regions with root and region facts", async () => {
const snapshot = await scanCanonicalArtifactInventory(
await writeImage("firmware.hex", INTEL_HEX_I32_GAP),
);
expect(snapshot.manifest.root_format).toBe("intel-hex");
expect(snapshot.coverage).toMatchObject({
scope: "container-entries",
status: "complete",
});
expect(snapshot.limitations).toContain(INVENTORY_LIMITATION);
const root = snapshot.occurrences.find((row) => row.logical_path === ".");
expect(root).toMatchObject({
artifact_kind: "container",
format_metadata: {
kind: "hex-records",
variant: "I32HEX",
start_address: 0x0800_0101,
region_count: 2,
},
});
expect(
snapshot.occurrences
.filter((row) => row.logical_path !== ".")
.map((row) => [row.logical_path, row.declared_size, row.format_metadata]),
).toEqual([
[
"0x08000000-0x0800001f.bin",
32,
{
kind: "hex-record-region",
start_address: 0x0800_0000,
end_address: 0x0800_001f,
length: 32,
record_count: 2,
first_line: 2,
last_line: 3,
},
],
[
"0x08000100-0x08000103.bin",
4,
{
kind: "hex-record-region",
start_address: 0x0800_0100,
end_address: 0x0800_0103,
length: 4,
record_count: 1,
first_line: 4,
last_line: 4,
},
],
]);
});
it("reports unknown coverage and termination facts as limitations", async () => {
const truncated = await scanCanonicalArtifactInventory(
await writeImage("truncated.s19", SREC_S19.replace("S9030000FC\n", "")),
@@ -103,19 +46,7 @@ it("reports unknown coverage and termination facts as limitations", async () =>
);
});
it("fails closed on malformed or over-limit records", async () => {
await expect(
scanCanonicalArtifactInventory(
await writeImage(
"checksum.hex",
INTEL_HEX_I32_GAP.replace("BEEFC3", "BEEFC4"),
),
),
).rejects.toMatchObject({
reason: "format",
message:
"Intel HEX is malformed: line 4: record type 00 checksum 0xc4 does not match computed 0xc3",
});
it("fails closed on over-limit records", async () => {
const runs = [intelHexRecord(0, 0, [1])];
for (let index = 1; index <= 65_536; index += 1) {
const address = index * 2;
@@ -137,7 +68,7 @@ it("leaves an invalid first record to plain file classification", async () => {
expect(snapshot.manifest.root_format).toBe("file");
});
it("extracts byte-exact regions that re-inventory as raw files", async () => {
it("re-inventories extracted regions as raw files", async () => {
const directory = await createTestTempDirectory("rea-hex-extract-");
const path = join(directory, "firmware.hex");
await writeFile(path, INTEL_HEX_I32_GAP);
@@ -151,23 +82,6 @@ it("extracts byte-exact regions that re-inventory as raw files", async () => {
integrityPolicy: "fail",
resourceScope,
});
expect(extracted.containment_verified).toBe(true);
expect(
Buffer.compare(
await readFile(
join(extracted.output_root, "0x08000000-0x0800001f.bin"),
),
Buffer.from(Array.from({ length: 32 }, (_, index) => index)),
),
).toBe(0);
expect(
Buffer.compare(
await readFile(
join(extracted.output_root, "0x08000100-0x08000103.bin"),
),
Buffer.from("deadbeef", "hex"),
),
).toBe(0);
const reinventoried = await scanCanonicalArtifactInventory(
extracted.output_root,
);
+2 -69
View File
@@ -1,11 +1,9 @@
import { readFile, writeFile } from "node:fs/promises";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { Uf2ArtifactReader } from "./Uf2ArtifactReader.js";
import { ArtifactResourceScope } from "./ArtifactResourceScope.js";
import { extractArtifact } from "./extraction/ArtifactExtraction.js";
import {
RP2040_FAMILY,
RP2XXX_ABSOLUTE_FAMILY,
@@ -135,78 +133,13 @@ it("leaves coverage unknown when numbered blocks are absent", async () => {
]);
});
it("fails closed on a conflicting rewrite and leaves short files unrecognized", async () => {
const rewrite = uf2Block({
address: 0x1000_0010,
payload: [0xff],
blockNo: 0,
numBlocks: 1,
familyId: RP2040_FAMILY,
});
await expect(
scanCanonicalArtifactInventory(
await writeImage(
"conflict.uf2",
uf2File([...uf2Image(RP2040_FAMILY, 0x1000_0000, APP), rewrite]),
),
),
).rejects.toMatchObject({
reason: "format",
message:
"UF2 is malformed: family 0xe48bff56 (RP2040) address 0x10000010 is written with different values by blocks 0-2 (0x75) and block 3 (0xff)",
});
it("leaves short files unrecognized", async () => {
const truncated = await scanCanonicalArtifactInventory(
await writeImage("short.uf2", ABSOLUTE.subarray(0, 511)),
);
expect(truncated.manifest.root_format).toBe("file");
});
it("extracts byte-exact regions into family directories", async () => {
const directory = await createTestTempDirectory("rea-uf2-extract-");
const path = join(directory, "firmware.uf2");
await writeFile(
path,
uf2File([ABSOLUTE, ...uf2Image(RP2040_FAMILY, 0x1000_0000, APP)]),
);
const resourceScope = new ArtifactResourceScope();
try {
const extracted = await extractArtifact({
inputPath: path,
inputFormat: "uf2",
outputRoot: join(directory, "out"),
environment: process.env,
integrityPolicy: "fail",
resourceScope,
});
expect(extracted.containment_verified).toBe(true);
expect(extracted.coverage.status).toBe("unknown");
expect(
Buffer.compare(
await readFile(
join(
extracted.output_root,
"family-0xe48bff56/0x10000000-0x10000257.bin",
),
),
APP,
),
).toBe(0);
expect(
Buffer.compare(
await readFile(
join(
extracted.output_root,
"family-0xe48bff57/0x10ffff00-0x10ffffff.bin",
),
),
Buffer.alloc(256, 0xef),
),
).toBe(0);
} finally {
await resourceScope.close();
}
});
it("rejects a forged region entry and a changed carrier", async () => {
const path = await writeImage(
"identity.uf2",
-21
View File
@@ -593,27 +593,6 @@ describe("ZipArtifactReader decompression budget", () => {
}
});
it("streams a valid small member through open", async () => {
const directory = await createTestTempDirectory("rea-zip-budget-valid-");
const path = join(directory, "valid.zip");
const writer = new ZipWriter(new Uint8ArrayWriter());
await writer.add("member.bin", new TextReader("valid member bytes"));
await writeFile(path, await writer.close());
const reader = new ZipArtifactReader(path, "zip");
try {
const entries = [];
for await (const entry of reader.entries()) entries.push(entry);
const entry = entries[0];
if (entry === undefined) throw new Error("Expected a ZIP member");
const chunks: Buffer[] = [];
for await (const chunk of await reader.open(entry))
chunks.push(Buffer.from(chunk));
expect(Buffer.concat(chunks).toString()).toBe("valid member bytes");
} finally {
await reader.close();
}
});
it("rejects under-declared member output before delivering its complete actual bytes", async () => {
const directory = await createTestTempDirectory("rea-zip-underdeclared-");
const path = join(directory, "underdeclared.zip");
-2
View File
@@ -220,8 +220,6 @@ describe("safe CDP response metadata", () => {
"agent-header-secret",
])
expect(serialized).not.toContain(secret);
expect(serialized).toContain("link-secret");
expect(serialized).toContain("private.example.test/x");
});
it("reports well-known agent resources as untrusted observations", () => {
+13 -69
View File
@@ -73,32 +73,30 @@ describe("request step schema", () => {
);
});
it("accepts a same-origin request with query, headers and a JSON body", () => {
it("accepts a declared secret as a request header value", () => {
const parsed = scenario(
[
requestStep({
query: [{ name: "org", value: { source: "literal", value: "1" } }],
headers: [
{
name: "X-CSRFToken",
value: { source: "cookie", name: "csrftoken" },
},
{
name: "Authorization",
value: { source: "secret", secret_id: "token" },
prefix: "Bearer ",
},
],
json: { title: "Scratch folder 1" },
expected_status: [200, 201],
}),
],
[{ secret_id: "token", environment_variable: "APP_TOKEN" }],
);
expect(parsed.actions[0]).toMatchObject({
action: "request",
method: "POST",
path: "/api/folders",
headers: [
{
name: "Authorization",
value: { source: "secret", secret_id: "token" },
prefix: "Bearer ",
},
],
});
});
@@ -220,66 +218,12 @@ describe("request step execution", () => {
vi.unstubAllGlobals();
});
it("sends the declared request through the page's fetch with its cookies", async () => {
const calls = stubFetch(201);
const parsed = scenario([
requestStep({
query: [{ name: "org", value: { source: "literal", value: "1" } }],
json: { title: "Scratch folder 1" },
}),
]);
await perform(fakePage({}), parsed.actions[0]!, resolved(parsed));
expect(calls).toEqual([
{
url: `${ORIGIN}/api/folders?org=1`,
init: expect.objectContaining({
method: "POST",
credentials: "same-origin",
headers: [["content-type", "application/json"]],
body: '{"title":"Scratch folder 1"}',
}),
},
]);
});
it("reads header values from a cookie and from stored JSON, and redacts them afterwards", async () => {
it("sends requests with same-origin credentials", async () => {
const calls = stubFetch(200);
const parsed = scenario([
requestStep({
headers: [
{
name: "X-CSRFToken",
value: { source: "cookie", name: "csrftoken" },
},
{
name: "Authorization",
value: {
source: "local_storage",
key: "auth",
json_pointer: "/token",
},
prefix: "Bearer ",
},
],
}),
]);
const secrets = resolved(parsed);
await perform(
fakePage({
cookies: [{ name: "csrftoken", value: "csrf-value-123" }],
storage: { auth: JSON.stringify({ token: "jwt-value-456" }) },
}),
parsed.actions[0]!,
secrets,
);
expect(calls[0]!.init.headers).toEqual([
["X-CSRFToken", "csrf-value-123"],
["Authorization", "Bearer jwt-value-456"],
]);
const redacted = secrets.redact("csrf-value-123 and jwt-value-456");
expect(redacted).not.toContain("csrf-value-123");
expect(redacted).not.toContain("jwt-value-456");
expect(redacted).toContain("[REDACTED:page:create.X-CSRFToken]");
const parsed = scenario([requestStep({})]);
await perform(fakePage({}), parsed.actions[0]!, resolved(parsed));
expect(calls).toHaveLength(1);
expect(calls[0]!.init.credentials).toBe("same-origin");
});
it("refuses to run off the start origin, and fails on an unexpected status or a missing cookie", async () => {
+34 -12
View File
@@ -47,15 +47,27 @@ it.each([
});
it("retains U+FFFD in well-formed metadata as an ordinary XML character", () => {
const decoded = JSON.stringify(
decodeSazMetadata(
Buffer.from(
'<Session><SessionFlag N="ui-comments" V="caf�"/>�</Session>',
),
),
const decoded = decodeSazMetadata(
Buffer.from('<Session><SessionFlag N="ui-comments" V="caf�"/>�</Session>'),
);
expect(decoded).toContain('{"name":"V","value":"caf�"}');
expect(decoded).toContain('"value":"�"');
expect(decoded).toMatchObject({
name: "#document",
children: [
{
name: "Session",
children: [
{
name: "SessionFlag",
attributes: [
{ name: "N", value: "ui-comments" },
{ name: "V", value: "caf�" },
],
},
{ name: "#text", value: "�" },
],
},
],
});
});
it.each([
@@ -74,10 +86,20 @@ it.each([
it("preserves valid comments, CDATA, declarations, and predefined entities", () => {
const xml =
'<?xml version="1.0"?><Session><!-- note & literal --><Value>A &amp; B</Value><C><![CDATA[C & D]]></C></Session>';
const decoded = JSON.stringify(decodeSazMetadata(Buffer.from(xml)));
expect(decoded).toContain("note & literal");
expect(decoded).toContain("A & B");
expect(decoded).toContain("C & D");
const decoded = decodeSazMetadata(Buffer.from(xml));
expect(decoded).toMatchObject({
children: [
{ name: "xml" },
{
name: "Session",
children: [
{ name: "#comment", value: " note & literal " },
{ name: "Value", children: [{ name: "#text", value: "A & B" }] },
{ name: "C", children: [{ name: "#cdata-section", value: "C & D" }] },
],
},
],
});
});
it("rejects DTDs without resolving entities", () => {
+4 -2
View File
@@ -108,12 +108,14 @@ describe("CLI JSON path normalization", () => {
"FILE:electron",
"https://example.test/app",
])("keeps URL %j distinguishable from a local path", (value) => {
const input = {
const expected = {
executable_path: value,
application_path: resolve("/synthetic/app"),
};
const input = { ...expected };
const normalized = resolveCliJsonPaths(input, paths);
expect(normalized).toEqual(input);
expect(normalized).toEqual(expected);
expect(input).toEqual(expected);
const parsed = electronActiveObservationInputSchema.safeParse(normalized);
expect(parsed.success).toBe(false);
if (parsed.success) throw new Error("Expected URL path rejection");
+84 -19
View File
@@ -1,14 +1,18 @@
import { expect, it } from "vitest";
import { z } from "zod";
import { Ajv2020 } from "ajv/dist/2020.js";
import {
binaryLayoutSummaryViewResultSchema,
javascriptApplicationSummaryViewResultSchema,
projectAnalysisView,
analysisViewRequestSchema,
} from "../domain/analysisView/analysisView.js";
import {
analysisViewCaptureEvidence,
analysisViewJavaScriptEvidence,
analysisViewLayoutEvidence,
environmentStateCaptureFixture,
} from "../../tests/fixtures/analysisView.js";
import { toolContract } from "./toolContracts.js";
@@ -29,25 +33,6 @@ const projectSummary = (parent: {
);
it("advertises only that operation's summary from analysis detail outputs", () => {
for (const name of [
"inspect_binary_layout",
"analyze_javascript_application",
] as const) {
const schema = JSON.stringify(
z.toJSONSchema(toolContract(name).outputSchema),
);
expect(schema).not.toContain("api-surface");
expect(schema).not.toContain("environment-manifest");
expect(schema).not.toContain("environment-state");
expect(schema).not.toContain("inspect_web_network_capture");
}
const viewSchema = JSON.stringify(
z.toJSONSchema(toolContract("inspect_analysis_view").outputSchema),
);
expect(viewSchema).toContain("api-surface");
expect(viewSchema).toContain("environment-manifest");
expect(viewSchema).toContain("environment-state");
const layout = projectSummary(analysisViewLayoutEvidence());
const javascript = projectSummary(analysisViewJavaScriptEvidence());
if (!layout.ok || !javascript.ok) throw new Error("Expected summaries");
@@ -61,4 +46,84 @@ it("advertises only that operation's summary from analysis detail outputs", () =
expect(
binaryLayoutSummaryViewResultSchema.safeParse(javascript.value).success,
).toBe(false);
const ajv = new Ajv2020({ strict: false, validateFormats: false });
const validators = Object.fromEntries(
(
[
"inspect_binary_layout",
"analyze_javascript_application",
"inspect_analysis_view",
] as const
).map((name) => [
name,
ajv.compile(z.toJSONSchema(toolContract(name).outputSchema)),
]),
);
const layoutReceipt = {
...analysisViewLayoutEvidence(),
normalized_result: layout.value,
};
const javascriptReceipt = {
...analysisViewJavaScriptEvidence(),
normalized_result: javascript.value,
};
expect(validators.inspect_binary_layout!(layoutReceipt)).toBe(true);
expect(validators.analyze_javascript_application!(javascriptReceipt)).toBe(
true,
);
const parent = analysisViewCaptureEvidence(environmentStateCaptureFixture());
const captureParent = {
evidenceId: parent.evidence_id,
operation: parent.operation,
normalizedResult: parent.normalized_result,
parameters: parent.parameters,
subject: parent.subject,
limitations: parent.limitations,
};
const captureSummary = projectAnalysisView(captureParent, {
kind: "summary",
});
if (!captureSummary.ok) throw captureSummary.error;
const captureReceipt = { ...parent, normalized_result: captureSummary.value };
expect(validators.inspect_analysis_view!(captureReceipt)).toBe(true);
expect(
validators.inspect_binary_layout!({
...layoutReceipt,
normalized_result: captureSummary.value,
}),
).toBe(false);
expect(
validators.analyze_javascript_application!({
...javascriptReceipt,
normalized_result: captureSummary.value,
}),
).toBe(false);
for (const kind of [
"api-surface",
"environment-manifest",
"environment-state",
] as const) {
const projected = projectAnalysisView(
captureParent,
analysisViewRequestSchema.parse({ kind }),
);
if (!projected.ok) throw projected.error;
const receipt = { ...parent, normalized_result: projected.value };
expect(validators.inspect_analysis_view!(receipt), kind).toBe(true);
expect(
validators.inspect_binary_layout!({
...layoutReceipt,
normalized_result: projected.value,
}),
kind,
).toBe(false);
expect(
validators.analyze_javascript_application!({
...javascriptReceipt,
normalized_result: projected.value,
}),
kind,
).toBe(false);
}
});
+28 -18
View File
@@ -2,6 +2,8 @@ import { chmod, mkdir, realpath, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it as test, vi } from "vitest";
import { withVerifierCleanup } from "../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../tests/fixtures/lifecycleAcknowledgement.js";
import { createSystemDoctorHost } from "./doctorRuntime.js";
import { DOCTOR_PROVIDER_IDS } from "./application/DoctorProviderIds.js";
@@ -273,8 +275,8 @@ test.each([true, false])(
.finally(() => {
settled = true;
});
try {
await entered;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered, pending, "display selection");
expect(selected.mock.calls).toHaveLength(1);
if (cancel) {
controller.abort(reason);
@@ -295,13 +297,17 @@ test.each([true, false])(
expect.objectContaining({ name: "hopper-demo-runtime", ok: true }),
);
}
} finally {
release();
await pending;
await system.close?.();
inspected.mockRestore();
selected.mockRestore();
}
}, [
release,
async () => {
await pending;
},
async () => {
await system.close?.();
},
() => inspected.mockRestore(),
() => selected.mockRestore(),
]);
},
5_000,
);
@@ -340,8 +346,8 @@ test("joins display finalization before preserving a late cancellation", async (
.finally(() => {
settled = true;
});
try {
await entered;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(entered, pending, "display finalization");
controller.abort(reason);
await Promise.resolve();
expect(settled).toBe(false);
@@ -351,13 +357,17 @@ test("joins display finalization before preserving a late cancellation", async (
expect(failure).toBeInstanceOf(AnalysisCancelledError);
expect((failure as Error).cause).toBe(reason);
expect(inspected).not.toHaveBeenCalled();
} finally {
release();
await pending;
await system.close?.();
inspected.mockRestore();
selected.mockRestore();
}
}, [
release,
async () => {
await pending;
},
async () => {
await system.close?.();
},
() => inspected.mockRestore(),
() => selected.mockRestore(),
]);
});
test("rejects a pre-aborted caller before the Hopper display check", async () => {
+8 -2
View File
@@ -782,9 +782,15 @@ it("projects JavaScript summary and preserves selected module observations", ()
expect(item.value.item).toMatchObject({
path: "renderer.js",
kind: "javascript-asset",
observations: expect.arrayContaining([
expect.objectContaining({
properties: expect.objectContaining({
source: "export const secret = 1;\n",
text: "should not leak",
}),
}),
]),
});
expect(JSON.stringify(item.value.item)).toContain("export const secret = 1;");
expect(JSON.stringify(item.value.item)).toContain("should not leak");
const page = projectAnalysisView(parent, {
kind: "page",
collection: "modules",
@@ -351,6 +351,9 @@ it("derives value-free body shapes, observed query names and record pointers", (
"status",
]);
const silent = manifest([], environmentManifestCaptureFixture(), false);
expect(silent.tools.map((tool) => tool.name)).toEqual(
projected.tools.map((tool) => tool.name),
);
expect(
silent.tools.every((tool) => tool.query_parameter_names.length === 0),
).toBe(true);
+1
View File
@@ -180,6 +180,7 @@ describe("call path reconstruction", () => {
expect(
result.paths.map((path) => path.nodes.map(({ address }) => address)),
).toEqual([["0x1000", "0x2000", "0x4000"]]);
expect(result.paths[0]?.edges).toHaveLength(2);
expect(
result.paths[0]?.edges.every((edge) => edge.evidence_links.length > 0),
).toBe(true);
+1 -9
View File
@@ -155,15 +155,7 @@ describe("Electron fuse wire decoding", () => {
});
});
it("does not treat a missing sentinel or a short wire as disabled fuses", () => {
const missing = describeElectronFuseFindings([slice([])], "mach-o");
expect(missing.runtime_implications).toEqual([]);
expect(missing.limitations.join(" ")).toContain(
"unknown, not that every fuse is disabled",
);
expect(missing.limitations.join(" ")).toContain(
"fuse state for that slice is unknown, not disabled",
);
it("does not treat a short wire as disabled fuses", () => {
const truncated = describeElectronFuseFindings(
[
slice([
+10 -2
View File
@@ -56,6 +56,10 @@ describe("Evidence completion ledger", () => {
]);
expect(left).toEqual(right);
expect(left.records).toEqual([
record("scenario.a", "unknown", [evidenceId("3")]),
record("scenario.z", "pass", [evidenceId("1"), evidenceId("2")]),
]);
expect(left.ledger_id).toMatch(/^ecl_[a-f0-9]{64}$/u);
});
@@ -67,9 +71,13 @@ describe("Evidence completion ledger", () => {
expect(
createEvidenceCompletionLedger([
record("scenario.many-evidence-items", "pass", evidenceIds),
record(
"scenario.many-evidence-items",
"pass",
[...evidenceIds].reverse(),
),
]).records[0]?.evidence_ids,
).toHaveLength(evidenceIds.length);
).toEqual(evidenceIds);
});
it("rejects missing, malformed, and duplicate Evidence IDs", () => {
+7 -3
View File
@@ -113,11 +113,15 @@ it("preserves cleanup failure authority and resources while excluding diagnostic
const projected = projectAnalysisError(
redactExplicitFailure(error, ["private", "REDACTED"]),
);
expect(projected.code).toBe("cleanup_incomplete");
expect(projected).toMatchObject({
code: "cleanup_incomplete",
details: {
resources: expect.arrayContaining(["pid:123"]),
diagnostics: { exit_code: 2 },
},
});
expect(JSON.stringify(projected)).not.toContain("private");
expect(JSON.stringify(projected)).not.toContain("REDACTED");
expect(JSON.stringify(projected)).toContain("pid:123");
expect(JSON.stringify(projected)).toContain('"exit_code":2');
});
it("redacts cleanup primaries and mirrors the source error's live status", () => {
+1 -75
View File
@@ -108,82 +108,8 @@ describe("ESP image recognition", () => {
});
describe("ESP image decoding", () => {
it("reports header, chip, segment, integrity and descriptor facts", () => {
const decoded = image(APP);
expect(decoded.metadata).toEqual({
kind: "esp-image",
header: {
segment_count: 4,
spi_mode: 2,
spi_speed: 0,
spi_size: 2,
entry_addr: 0x4038_0000,
wp_pin: 0xee,
spi_pin_drv: "000000",
chip_id: 5,
min_chip_rev: 0,
min_chip_rev_full: 0,
max_chip_rev_full: 199,
reserved: "00000000",
hash_appended: true,
},
chip: { target: "esp32c3", isa: "riscv", source: "ESP-IDF v6.1" },
// Segments end at 456; the checksum ends the next 16-byte block.
checksum: {
offset: 463,
stored: espChecksum(ESP32C3_APP_SEGMENTS),
computed: espChecksum(ESP32C3_APP_SEGMENTS),
status: "valid",
},
appended_sha256: {
offset: 464,
stored: sha256(APP.subarray(0, 464)),
computed: sha256(APP.subarray(0, 464)),
status: "valid",
},
image_end: 496,
signature_sector_offset: null,
undescribed_trailing: [],
descriptor: {
kind: "app",
offset: 32,
observed_length: 256,
secure_version: 0,
version: "1.2.3",
project_name: "rea_esp_fixture",
time: "12:34:56",
date: "Oct 27 2026",
idf_ver: "v6.1",
app_elf_sha256: "ab".repeat(32),
min_efuse_blk_rev_full: 0,
max_efuse_blk_rev_full: 199,
mmu_page_size: 16,
spi_flash_mode: 2,
},
flash_dump: null,
});
expect(
decoded.segments.map(({ dataOffset, availableLength, metadata }) => [
metadata.index,
metadata.load_addr,
metadata.data_len,
metadata.header_offset,
dataOffset,
availableLength,
metadata.classification,
]),
).toEqual([
[0, 0x3c00_0020, 288, 24, 32, 288, "mapped"],
[1, 0x3fc8_8000, 16, 320, 328, 16, "loaded"],
[2, 0x4038_0000, 32, 344, 352, 32, "loaded"],
[3, 0x4200_0020, 64, 384, 392, 64, "mapped"],
]);
expect(decoded.coverage).toBe("complete");
expect(decoded.limitations).toEqual([]);
expect(decoded.signatureBlocks).toEqual([]);
});
it("decodes identically from any chunking", () => {
expect(image(APP).metadata.flash_dump).toBeNull();
const signed = signedEspImage(APP, ["rsa", null, "ecdsa-p384"]);
const expected = image(signed, signed.length);
for (const chunkBytes of [1, 3, 16, 4096])
-15
View File
@@ -399,21 +399,6 @@ describe("hex record recognition", () => {
});
});
it.each([3, 5])(
"rejects a nonzero reserved offset in Intel HEX start record %i",
(type) => {
const text = [
intelHexRecord(0, 0x20, [0xa5]),
intelHexRecord(type, 0x1234, [0, 0, 0, 1]),
intelHexRecord(1, 0),
].join("\n");
expect(decode(text)).toMatchObject({
ok: false,
error: { kind: "format" },
});
},
);
it("keeps data offsets, start payloads and the ignored EOF offset distinct", () => {
const parsed = image(
[
+10 -2
View File
@@ -596,7 +596,11 @@ describe("function collection collation ties", () => {
const calls = result.dimensions.find(
({ dimension }) => dimension === "calls",
);
expect(calls).toMatchObject({ status: "changed" });
expect(calls).toMatchObject({
status: "changed",
left_digest: expect.any(String),
right_digest: expect.any(String),
});
expect(calls?.left_digest).not.toBe(calls?.right_digest);
});
@@ -1143,7 +1147,11 @@ describe("function comparison identity and malformed CFG", () => {
const pseudocode = comparison.dimensions.find(
({ dimension }) => dimension === "pseudocode",
);
expect(pseudocode).toMatchObject({ status: "changed" });
expect(pseudocode).toMatchObject({
status: "changed",
left_digest: expect.any(String),
right_digest: expect.any(String),
});
expect(pseudocode?.left_digest).not.toBe(pseudocode?.right_digest);
});
});
@@ -175,9 +175,9 @@ it.each([
require('node:fs').readFileSync('also-wrong');
`);
expect(ir.configurationOperations).toEqual([]);
expect(
ir.bindings.find(({ name }) => name === "fs")?.provenance.status,
).not.toBe("module");
const binding = ir.bindings.find(({ name }) => name === "fs");
expect(binding).toBeDefined();
expect(binding?.provenance.status).not.toBe("module");
});
it("keeps local loader reassignment separate from the ambient loader", () => {
@@ -336,25 +336,25 @@ describe("JavaScript semantic analysis: computed method keys", () => {
});
});
describe("JavaScript semantic analysis: calls 2", () => {
it("does not treat a shadowed Promise binding as the intrinsic", () => {
const ir = analyzeJavaScriptSemantics(`
it("does not treat a shadowed Promise binding as the intrinsic", () => {
const ir = analyzeJavaScriptSemantics(`
function run(Promise) {
return Promise.resolve(1).then(work);
}
`);
expect(ir.promiseOperations).toEqual([
expect.objectContaining({
kind: "chain",
method: "then",
ownership: "returned",
sourcePromiseIds: [],
sourceResolution: "unresolved",
}),
]);
});
expect(ir.promiseOperations).toEqual([
expect.objectContaining({
kind: "chain",
method: "then",
ownership: "returned",
sourcePromiseIds: [],
sourceResolution: "unresolved",
}),
]);
});
describe("JavaScript semantic analysis: callable fingerprints", () => {
it("fingerprints formatting and local-name changes identically", () => {
const left = analyzeJavaScriptSemantics(`
function calculate(value) {
@@ -371,9 +371,16 @@ describe("JavaScript semantic analysis: calls 2", () => {
expect(left.functionFingerprints).toHaveLength(1);
expect(right.functionFingerprints).toHaveLength(1);
expect(changed.functionFingerprints).toHaveLength(1);
expect(left.functionFingerprints[0]?.components).toEqual(
right.functionFingerprints[0]?.components,
);
expect(
changed.functionFingerprints[0]?.components.literalSetSha256,
).toEqual(expect.any(String));
expect(left.functionFingerprints[0]?.components.literalSetSha256).toEqual(
expect.any(String),
);
expect(
changed.functionFingerprints[0]?.components.literalSetSha256,
).not.toBe(left.functionFingerprints[0]?.components.literalSetSha256);
@@ -432,11 +439,13 @@ describe("JavaScript semantic analysis: calls 2", () => {
),
);
expect(
fingerprint(ordered, "ordered")?.components.relationShapeSha256,
).not.toBe(
fingerprint(reversed, "ordered")?.components.relationShapeSha256,
);
const orderedDigest = fingerprint(ordered, "ordered")?.components
.relationShapeSha256;
const reversedDigest = fingerprint(reversed, "ordered")?.components
.relationShapeSha256;
expect(orderedDigest).toEqual(expect.any(String));
expect(reversedDigest).toEqual(expect.any(String));
expect(orderedDigest).not.toBe(reversedDigest);
expect(fingerprint(ordered, "timerOwner")?.components.effects).toContain(
"timer",
);
@@ -451,9 +460,10 @@ describe("JavaScript semantic analysis: calls 2", () => {
fingerprint(ordered, "networkOwner")?.components.effects,
).not.toContain("timer");
});
});
it("retains ambiguous local callees and explicit dynamic frontiers", () => {
const ir = analyzeJavaScriptSemantics(`
it("retains ambiguous local callees and explicit dynamic frontiers", () => {
const ir = analyzeJavaScriptSemantics(`
function left(value) { return value; }
function right(value) { return value; }
const selected = chooseLeft ? left : right;
@@ -462,37 +472,36 @@ describe("JavaScript semantic analysis: calls 2", () => {
external(input);
`);
const selected = ir.callSites.find(
({ resolution }) => resolution === "ambiguous",
);
expect(selected?.calleeCallableIds).toEqual(
[
onlyCallable(ir, "left").callableId,
onlyCallable(ir, "right").callableId,
].sort(),
);
expect(
ir.argumentFlows.filter(
({ callSiteId }) => callSiteId === selected?.callSiteId,
),
).toHaveLength(2);
expect(ir.frontiers).toEqual(
expect.arrayContaining([
expect.objectContaining({
kind: "dynamic-call",
reason: expect.stringMatching(/ambiguous/iu),
}),
expect.objectContaining({
kind: "dynamic-property",
reason: expect.stringMatching(/computed member/iu),
}),
expect.objectContaining({
kind: "dynamic-call",
reason: "Unresolved call target external.",
}),
]),
);
});
const selected = ir.callSites.find(
({ resolution }) => resolution === "ambiguous",
);
expect(selected?.calleeCallableIds).toEqual(
[
onlyCallable(ir, "left").callableId,
onlyCallable(ir, "right").callableId,
].sort(),
);
expect(
ir.argumentFlows.filter(
({ callSiteId }) => callSiteId === selected?.callSiteId,
),
).toHaveLength(2);
expect(ir.frontiers).toEqual(
expect.arrayContaining([
expect.objectContaining({
kind: "dynamic-call",
reason: expect.stringMatching(/ambiguous/iu),
}),
expect.objectContaining({
kind: "dynamic-property",
reason: expect.stringMatching(/computed member/iu),
}),
expect.objectContaining({
kind: "dynamic-call",
reason: "Unresolved call target external.",
}),
]),
);
});
describe("JavaScript semantic rest parameter flow", () => {
@@ -473,9 +473,9 @@ describe("JavaScript semantic analysis: mapped arguments writes", () => {
.returnSites[0]?.value;
if (value?.status !== "object")
throw new Error("Expected an object return value");
expect(
value.properties.find(({ name }) => name === "value")?.value.status,
).not.toBe("literal");
const property = value.properties.find(({ name }) => name === "value");
expect(property).toBeDefined();
expect(property?.value.status).not.toBe("literal");
});
});
@@ -59,6 +59,8 @@ it("keeps a nested function's directive out of its owner fingerprint", () => {
const second = analyze('"ordinary"');
expect(first.size).toBe(2);
expect(second.get("outer")).toEqual(first.get("outer"));
expect(first.get("inner")?.literalSetSha256).toEqual(expect.any(String));
expect(second.get("inner")?.literalSetSha256).toEqual(expect.any(String));
expect(second.get("inner")?.literalSetSha256).not.toBe(
first.get("inner")?.literalSetSha256,
);
@@ -53,9 +53,9 @@ it.each([
])(
"indexes the legacy block-function write on its outer owner: %s",
(source) => {
expect(
onlyCallable(analyze(source), "read").returnSites[0]?.value.status,
).not.toBe("literal");
const returnSite = onlyCallable(analyze(source), "read").returnSites[0];
expect(returnSite).toBeDefined();
expect(returnSite?.value.status).not.toBe("literal");
},
);
@@ -246,5 +246,6 @@ it("owns a class declaration's self reference separately from its outer variable
it("does not initialize a class self name during computed-key evaluation", () => {
const ir = analyze(`class Service { [Service()]() {} }`, "fixture.mjs");
expect(ir.callSites[0]).toBeDefined();
expect(ir.callSites[0]?.resolution).not.toBe("exact");
});
@@ -210,6 +210,7 @@ describe("JavaScript semantic analysis: loop lexical names", () => {
({ name, role }) => name === "value" && role === "read",
);
expect(reads).toHaveLength(2);
expect(reads[0]?.bindingId).toEqual(expect.any(String));
expect(reads[0]?.bindingId).not.toBe(reads[1]?.bindingId);
expect(reads[1]?.bindingId).toBe(topLevelBinding(ir, "value").bindingId);
});
@@ -224,6 +225,7 @@ describe("JavaScript semantic analysis: loop lexical names", () => {
({ name, role }) => name === "value" && role === "read",
);
expect(reads).toHaveLength(2);
expect(reads[0]?.bindingId).toEqual(expect.any(String));
expect(reads[0]?.bindingId).not.toBe(
topLevelBinding(ir, "value").bindingId,
);
@@ -242,11 +244,14 @@ describe("JavaScript semantic analysis: loop lexical names", () => {
const binding = onlyBinding(ir, "value");
const callable = ir.callables.find(({ name }) => name === "run");
expect(binding.scopeId).toBe(callable?.bodyScopeId);
expect(
ir.references
.filter(({ name, role }) => name === "value" && role === "read")
.every(({ bindingId }) => bindingId === binding.bindingId),
).toBe(true);
const reads = ir.references.filter(
({ name, role }) => name === "value" && role === "read",
);
expect(reads).toHaveLength(2);
expect(reads.map(({ bindingId }) => bindingId)).toEqual([
binding.bindingId,
binding.bindingId,
]);
});
});
@@ -268,6 +273,9 @@ describe("JavaScript semantic analysis: class expression lexical names", () => {
({ name, role }) => name === "Service" && role === "read",
);
expect(references).toHaveLength(2);
expect(references[1]?.bindingId).toBe(
topLevelBinding(ir, "Service").bindingId,
);
expect(references[0]?.bindingId).not.toBe(references[1]?.bindingId);
expect(references[0]?.resolution).toBe("resolved");
const classScope = ir.scopes.find(({ kind }) => kind === "class");
@@ -54,6 +54,7 @@ it("orders exported callable initializers within their enclosing declaration", (
resolution: "exact",
calleeCallableIds: [original.callableId],
});
expect(ir.callSites[1]).toBeDefined();
expect(ir.callSites[1]?.resolution).not.toBe("exact");
});
@@ -202,12 +202,14 @@ it.each([
: (child: { loader: unknown }) => {
if (!preserveHost) child.loader = {};
};
let thrown: unknown;
try {
runInNewContext(source, { module, external });
} catch (error) {
expect(error).toMatchObject({ name: "TypeError" });
expect(preserveHost).toBe(false);
thrown = error;
}
if (preserveHost) expect(thrown).toBeUndefined();
else expect(thrown).toMatchObject({ name: "TypeError" });
expect(calls).toEqual(preserveHost ? ["./uncertain.cjs"] : []);
}
},
@@ -368,13 +368,31 @@ it("shares completed factory control flow across repeated loads within one analy
},
});
expect(results).toHaveLength(64);
expect(
results.every((result) =>
symbolicVariants(result).every(
(variant, index) => variant === symbolicVariants(results[0]!)[index],
const variants = symbolicVariants(results[0]!);
expect(variants).toHaveLength(2);
expect(variants).toEqual(
expect.arrayContaining(
[true, false].map((positive) =>
expect.objectContaining({
kind: "namespace",
conditions: expect.arrayContaining([
{
node: expect.objectContaining({
type: "Identifier",
name: "flag",
}),
positive,
},
]),
}),
),
),
).toBe(true);
);
for (const result of results) {
const actual = symbolicVariants(result);
expect(actual).toHaveLength(variants.length);
actual.forEach((variant, index) => expect(variant).toBe(variants[index]));
}
expect(
evaluated.mock.calls.filter(([node]) =>
t.isIdentifier(node, { name: "flag" }),
+2 -2
View File
@@ -201,7 +201,7 @@ describe("NativeAOT detail and summary byte accounting", () => {
budget,
);
const complete = detail(REPORT_BUDGET);
expect(complete?.derived_memory).not.toBeNull();
expect(complete?.derived_memory).toEqual(expect.any(Object));
let low = 0;
let high = Buffer.byteLength(JSON.stringify(complete));
while (low < high) {
@@ -211,7 +211,7 @@ describe("NativeAOT detail and summary byte accounting", () => {
}
const atBoundary = detail(low);
const belowBoundary = detail(low - 1);
expect(atBoundary?.derived_memory).not.toBeNull();
expect(atBoundary?.derived_memory).toEqual(expect.any(Object));
expect(Buffer.byteLength(JSON.stringify(atBoundary))).toBeLessThanOrEqual(
low,
);
+3
View File
@@ -228,6 +228,9 @@ describe("historical source graph", () => {
it("omits absent local diagnostics and rejects explicitly undefined non-JSON metadata", () => {
const input = graphInput();
const graph = createHistoricalSourceGraph(input);
expect(graph.entries.map((entry) => entry.path)).toEqual(
input.entries.map((entry) => entry.path),
);
expect(
graph.entries.every(
(entry) => !Object.hasOwn(entry, "local_diagnostics"),
-1
View File
@@ -467,7 +467,6 @@ describe("web bundle analyzer", () => {
expect.objectContaining({ value: "Vue" }),
]),
);
expect(JSON.stringify(result)).toContain("authorization=secret");
expect(result.completeness.status).toBe("complete");
});
+1 -1
View File
@@ -38,7 +38,7 @@ describe("captured script path projection", () => {
const paths = planned.map(({ record }) =>
record.content.state === "exported" ? record.content.relative_path : "",
);
expect(paths[0]).toMatch(/^origins\/[a-f0-9]{64}\/app\/main.js$/u);
expect(paths[0]).toMatch(/^origins\/[a-f0-9]{64}\/app\/main\.js$/u);
expect(paths[1]?.replace("lib/value.mjs", "main.js")).toBe(paths[0]);
expect(paths[2]).not.toBe(paths[0]);
});
-56
View File
@@ -20,62 +20,6 @@ const metadataLayout = (bytes: Buffer) => {
};
describe("managed member pointer ownership", () => {
it.each([
{ table: "fields" as const, prefix: "0x040000" },
{ table: "methods" as const, prefix: "0x060000" },
])("maps reordered pointer slots to physical $table", ({ table, prefix }) => {
const bytes = buildManagedMemberPointerFixture();
const result = inspectManagedMembersBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(result.coverage).toEqual({ state: "complete", issues: [] });
expect(result.types.map((type) => type.full_name)).toEqual([
"<Module>",
"Fixture.A",
"Fixture.B",
]);
expect(
result[table].map(({ token, declaring_type, declaring_type_token }) => ({
token,
declaring_type,
declaring_type_token,
})),
).toEqual([
{
token: `${prefix}01`,
declaring_type: "Fixture.B",
declaring_type_token: "0x02000003",
},
{
token: `${prefix}02`,
declaring_type: "Fixture.A",
declaring_type_token: "0x02000002",
},
]);
});
it("resolves a local custom-attribute constructor through MethodPtr", () => {
const bytes = buildManagedMemberPointerFixture();
const target = managedPeFixtureTarget(bytes);
const artifact = inspectManagedArtifactBytes(bytes, target);
const members = inspectManagedMembersBytes(bytes, target);
expect(artifact.metadata.stream_names).toContain("#-");
expect(artifact.coverage).toEqual({ state: "complete", issues: [] });
expect(artifact.attributes).toEqual([
expect.objectContaining({
parent_token: "0x20000001",
constructor_token: "0x06000001",
type_name: "Fixture.B",
}),
]);
expect(artifact.attributes[0]?.type_name).toBe(
members.methods[0]?.declaring_type,
);
});
it.each([
{
label: "identity pointer tables",
+15 -9
View File
@@ -5,6 +5,8 @@ import { syncBuiltinESMExports } from "node:module";
import * as fileSystem from "node:fs/promises";
import { dirname, join } from "node:path";
import { afterEach, expect, it, vi } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { thinFuseImage } from "../../tests/fixtures/electron/fuseImage.js";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
@@ -375,8 +377,8 @@ it("awaits an admitted preflight's final close before projecting cancellation",
settled = true;
return result;
});
try {
await closeEntered;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(closeEntered, running, "preflight close");
await new Promise<void>((resolve) => setImmediate(resolve));
expect(settled).toBe(false);
releaseClose();
@@ -396,13 +398,17 @@ it("awaits an admitted preflight's final close before projecting cancellation",
await provider.close();
expect(result.error.cleanupIncomplete).toBe(false);
expect(retry.error.cleanupIncomplete).toBe(false);
} finally {
releaseClose();
denied = false;
await running;
await provider.close();
await fixture.release();
}
}, [
() => {
releaseClose();
denied = false;
},
async () => {
await running;
},
() => provider.close(),
() => fixture.release(),
]);
});
it.each(["admission", "close"] as const)(
-83
View File
@@ -33,54 +33,6 @@ const parse = (bytes: Buffer, input = defaults) =>
input,
);
it("reads atoms, exports, imports, lambdas and the Code header", () => {
const report = parse(erlangBeamFixture("demo"));
expect(report.atom_encoding).toBe("utf8");
expect(report.module).toBe("demo");
expect(report.atoms?.map((atom) => atom.name)).toEqual([
"demo",
"module_info",
"start",
"erlang",
"get_module_info",
]);
expect(report.exports).toEqual([
{ function_index: 3, function: "start", arity: 0, label: 2 },
{ function_index: 2, function: "module_info", arity: 0, label: 1 },
{ function_index: 2, function: "module_info", arity: 1, label: 3 },
]);
expect(report.imports).toEqual([
{
module_index: 4,
module: "erlang",
function_index: 5,
function: "get_module_info",
arity: 1,
},
]);
expect(report.lambdas?.[0]).toMatchObject({
function: "start",
arity: 1,
label: 4,
old_unique: 42,
});
expect(report.code).toMatchObject({
instruction_set: 0,
opcode_max: 169,
labels: 4,
functions: 2,
extra_header_bytes: 0,
instructions: { bytes: 4 },
});
expect(report.chunks.map((chunk) => chunk.name)).toEqual([
"AtU8",
"Code",
"ExpT",
"ImpT",
"FunT",
]);
});
it("selects the last table and Code payload for every repeated chunk name", () => {
const original = erlangBeamFixture("selected");
const decoy = erlangBeamFixture("decoy");
@@ -236,41 +188,6 @@ it.each([Buffer.from([0]), Buffer.from([0, 0, 0, 1])])(
},
);
// Expected values come from beam_lib:chunks/2 on the same file under OTP 29.
it("reads a compiler-produced OTP 29 BEAM with the compact atom table", () => {
const report = parse(erlangBeamOtp29Fixture());
expect(report.module).toBe("rea_beam_fixture");
expect(report.atoms?.map((atom) => atom.name)).toEqual([
"rea_beam_fixture",
"hello",
"lists",
"reverse",
"module_info",
"erlang",
"get_module_info",
]);
expect(report.exports).toEqual([
{ function_index: 5, function: "module_info", arity: 1, label: 6 },
{ function_index: 5, function: "module_info", arity: 0, label: 4 },
{ function_index: 2, function: "hello", arity: 1, label: 2 },
]);
expect(
report.imports?.map((row) => [row.module, row.function, row.arity]),
).toEqual([
["lists", "reverse", 1],
["erlang", "get_module_info", 1],
["erlang", "get_module_info", 2],
]);
expect(report.code).toMatchObject({
instruction_set: 0,
opcode_max: 181,
labels: 7,
functions: 3,
extra_header_bytes: 0,
instructions: { bytes: 49 },
});
});
it("accepts extended compact atom lengths and rejects truncated or overflowing values", () => {
const compactAtom = (length: Buffer, name: Buffer): Buffer =>
erlangBeamChunksFixture([
+18 -7
View File
@@ -1,3 +1,5 @@
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { expect, it } from "vitest";
import {
decodeEvmBytecodeCarrier,
@@ -22,14 +24,23 @@ it.each(["0x1", "60 00", "0xzz", "\uFEFF6000", "\u00a06000"])(
);
},
);
it("rejects a long interior separator run in linear time", () => {
const text = `60${" ".repeat(200_000)}00`;
const started = performance.now();
expect(() => decodeEvmBytecodeCarrier(Buffer.from(text), "hex")).toThrow(
EvmCarrierFailure,
it("rejects a long interior separator run without pathological backtracking", async () => {
const moduleUrl = new URL("./EvmBytecodeCarrier.ts", import.meta.url).href;
const source = `
import assert from "node:assert/strict";
import { decodeEvmBytecodeCarrier, EvmCarrierFailure } from ${JSON.stringify(moduleUrl)};
const text = "60" + " ".repeat(1_000_000) + "00";
assert.throws(() => decodeEvmBytecodeCarrier(Buffer.from(text), "hex"), EvmCarrierFailure);
`;
await promisify(execFile)(
process.execPath,
["--input-type=module", "-e", source],
{
timeout: 15_000,
killSignal: "SIGKILL",
},
);
expect(performance.now() - started).toBeLessThan(1_000);
});
}, 20_000);
it("distinguishes raw bytes, empty code and malformed UTF-8", () => {
const bytes = Uint8Array.from([0xff, 0x00]);
const decoded = decodeEvmBytecodeCarrier(bytes, "raw");
@@ -88,11 +88,6 @@ describe("flattened devicetree", () => {
] as const)("rejects %s", (_name, mutate) =>
expect(() => parse(mutate(devicetreeFixture()))).toThrow(),
);
it("distinguishes unsupported versions", () => {
expect(() => parse(devicetreeFixture(undefined, 18))).toThrow(
expect.objectContaining({ kind: "unsupported" }),
);
});
it("bounds depth, nodes and properties", () => {
let root: FixtureNode = { name: "leaf" };
for (let i = 0; i < 10; i++) root = { name: `n${i}`, children: [root] };
@@ -190,7 +185,6 @@ it("leaves conflicting phandle declarations unresolved", () => {
]);
});
it.each([
["empty", Buffer.alloc(0)],
["short", Buffer.from([0, 0])],
["multiple cells", cells(1, 2)],
])(
+2
View File
@@ -109,6 +109,8 @@ describe("GameCube DOL Ghidra profile", () => {
);
if (!left.ok) throw left.error;
if (!right.ok) throw right.error;
expect(left.value.profile?.digest).toEqual(expect.any(String));
expect(right.value.profile?.digest).toEqual(expect.any(String));
expect(left.value.profile?.digest).not.toBe(right.value.profile?.digest);
});
+2 -4
View File
@@ -110,9 +110,7 @@ describe("native load-image result projection", () => {
);
expect(parsed.ok).toBe(true);
if (!parsed.ok) throw parsed.error;
expect(JSON.stringify(parsed.value.value)).toBe(
JSON.stringify(observations),
);
expect(parsed.value.value).toEqual(observations);
expect(parsed.value.rawResult).toBeUndefined();
});
@@ -129,7 +127,7 @@ describe("native load-image result projection", () => {
);
expect(parsed.ok).toBe(true);
if (!parsed.ok) throw parsed.error;
expect(JSON.stringify(parsed.value.value)).toContain('"entry_context":[]');
expect(parsed.value.value).toMatchObject({ entry_context: [] });
});
});
@@ -419,6 +419,8 @@ describe("Ghidra language override profile", () => {
});
expect(parameters).not.toHaveProperty("language_selection");
expect(ghidraProfileLanguageOverride(parameters)).toBeUndefined();
expect(automatic.value.profile?.digest).toEqual(expect.any(String));
expect(configured.value.profile?.digest).toEqual(expect.any(String));
expect(automatic.value.profile?.digest).not.toBe(
configured.value.profile?.digest,
);
+2
View File
@@ -126,6 +126,8 @@ describe("bounded Ghidra MIPS admission", () => {
},
gnu_attributes: null,
});
expect(left.value.profile?.digest).toEqual(expect.any(String));
expect(right.value.profile?.digest).toEqual(expect.any(String));
expect(left.value.profile?.digest).not.toBe(right.value.profile?.digest);
const windows = new GhidraProvider(
config,
+2
View File
@@ -124,6 +124,8 @@ describe("bounded Ghidra PowerPC admission", () => {
);
if (!left.ok) throw left.error;
if (!right.ok) throw right.error;
expect(left.value.profile?.digest).toEqual(expect.any(String));
expect(right.value.profile?.digest).toEqual(expect.any(String));
expect(left.value.profile?.digest).not.toBe(right.value.profile?.digest);
});
+11 -7
View File
@@ -482,6 +482,7 @@ describe("Ghidra raw-binary profile", () => {
expect(resolved.ok && shifted.ok).toBe(true);
if (!resolved.ok || !shifted.ok || resolved.value.profile === null)
throw new Error("expected raw-binary profiles");
expect(shifted.value.profile?.digest).toEqual(expect.any(String));
expect(shifted.value.profile?.digest).not.toBe(
resolved.value.profile.digest,
);
@@ -4864,14 +4865,14 @@ it("reports every Windows existing-project capability unavailable before open",
REA_GHIDRA_PROJECT_PATH: join(process.cwd(), "selected.gpr"),
});
try {
const capabilities = ghidra.capabilities();
expect(capabilities.length).toBeGreaterThan(0);
expect(
ghidra
.capabilities()
.every(
(capability) =>
!capability.available &&
capability.availabilityCode === "unsupported_host",
),
capabilities.every(
(capability) =>
!capability.available &&
capability.availabilityCode === "unsupported_host",
),
).toBe(true);
const readiness = await ghidra.inspectReadinessSnapshot();
expect(readiness.availability).toMatchObject({
@@ -4879,6 +4880,9 @@ it("reports every Windows existing-project capability unavailable before open",
code: "unsupported_host",
reason: EXISTING_PROJECT_WINDOWS_UNAVAILABLE,
});
expect(readiness.capabilities.map(({ operation }) => operation)).toEqual(
capabilities.map(({ operation }) => operation),
);
expect(
readiness.capabilities.every(
(capability) =>
+2
View File
@@ -166,6 +166,8 @@ const registerRiscvLanguageCommitmentCases = () => {
elf_class: 32,
rvc_declared: false,
});
expect(wide.value.profile?.digest).toEqual(expect.any(String));
expect(narrow.value.profile?.digest).toEqual(expect.any(String));
expect(wide.value.profile?.digest).not.toBe(narrow.value.profile?.digest);
});
+7 -1
View File
@@ -52,7 +52,13 @@ it("reports truncated cyclic causes in serializable diagnostics", () => {
cause,
).diagnostics;
expect(JSON.stringify(diagnostics)).toContain('"cause_truncated":true');
const serialized = JSON.parse(JSON.stringify(diagnostics)) as {
failure_cause: unknown;
};
let nested = serialized.failure_cause;
while (typeof nested === "object" && nested !== null && "cause" in nested)
nested = nested.cause;
expect(nested).toMatchObject({ cause_truncated: true });
});
it("keeps bounded compiler and nested launcher outcomes in diagnostics", () => {
+41 -59
View File
@@ -292,30 +292,26 @@ describe("Ghidra extension profile and producer validation", () => {
it("requires exact inventory, artifact digest and consistent producer status", async () => {
const { extension } = await artifact();
const valid = result(extension.sha256);
const inventoryMismatch =
"Ghidra extension result inventory differs from its committed profile.";
expect(validateGhidraExtensionResults([extension], [valid])).toBeNull();
expect(validateGhidraExtensionResults([extension], [])).toBe(
inventoryMismatch,
);
expect(validateGhidraExtensionResults([], [valid])).toBe(inventoryMismatch);
expect(validateGhidraExtensionResults([extension], [valid, valid])).toBe(
inventoryMismatch,
expect(validateGhidraExtensionResults([extension], [])).toContain(
"inventory",
);
expect(validateGhidraExtensionResults([], [valid])).toContain("inventory");
expect(
validateGhidraExtensionResults(
[extension],
[{ ...valid, sha256: "b".repeat(64) }],
),
).toBe("Ghidra extension identity mismatch: nativeaot");
validateGhidraExtensionResults([extension], [valid, valid]),
).toContain("inventory");
const identityMismatch = validateGhidraExtensionResults(
[extension],
[{ ...valid, sha256: "b".repeat(64) }],
);
expect(identityMismatch).toContain("identity");
expect(identityMismatch).toContain(extension.id);
expect(
validateGhidraExtensionResults(
[extension],
[{ ...valid, status: "complete" }],
),
).toBe(
"NativeAOT extension returned a malformed or inconsistent producer report.",
);
).toContain("producer report");
});
it("requires matching explicit configuration for imported executable profiles", async () => {
const { extension, path } = await artifact();
@@ -329,39 +325,28 @@ describe("Ghidra extension profile and producer validation", () => {
).toBeNull();
expect(
validateGhidraExtensionProfile([extension], config(), target, "linux"),
).toBe(
"Configured Ghidra extension inventory differs from the resolved analysis profile.",
).toContain("inventory");
const unsupportedHost = validateGhidraExtensionProfile(
[extension],
config(path),
target,
"darwin",
);
expect(
validateGhidraExtensionProfile(
[extension],
config(path),
target,
"darwin",
),
).toBe(
"NativeAOT recovery is verified on Linux only; omit REA_GHIDRA_NATIVEAOT_JAR for ordinary native analysis.",
);
expect(
validateGhidraExtensionProfile(
[{ ...extension, entry_class: "arbitrary.Code" }],
expect(unsupportedHost).toContain("Linux");
expect(unsupportedHost).toContain("REA_GHIDRA_NATIVEAOT_JAR");
for (const changed of [
{ ...extension, entry_class: "arbitrary.Code" },
{ ...extension, configured_path: "/another.jar" },
]) {
const invalid = validateGhidraExtensionProfile(
[changed],
config(path),
target,
"linux",
),
).toBe(
"Ghidra extension profile lacks matching explicit local configuration: nativeaot",
);
expect(
validateGhidraExtensionProfile(
[{ ...extension, configured_path: "/another.jar" }],
config(path),
target,
"linux",
),
).toBe(
"Ghidra extension profile lacks matching explicit local configuration: nativeaot",
);
);
expect(invalid).toContain("configuration");
expect(invalid).toContain(extension.id);
}
});
it("does not classify PE/CLI or ReadyToRun as NativeAOT", async () => {
const resolved = await resolveGhidraExtensions(
@@ -465,8 +450,8 @@ describe("Ghidra extension recovery coverage", () => {
},
},
};
expect(validateGhidraExtensionResults([extension], [incomplete])).toBe(
"NativeAOT recovery coverage contradicts its reported completion status.",
expect(validateGhidraExtensionResults([extension], [incomplete])).toContain(
"coverage",
);
expect(
validateGhidraExtensionResults(
@@ -485,9 +470,7 @@ describe("Ghidra extension recovery coverage", () => {
[extension],
[{ ...positive, result: { ...positive.result, discovery: null } }],
),
).toBe(
"NativeAOT extension returned a malformed or inconsistent producer report.",
);
).toContain("producer report");
});
it("preserves real loader failures without requiring a successful analyzer report", async () => {
const { extension } = await artifact();
@@ -512,14 +495,13 @@ describe("Ghidra extension recovery coverage", () => {
const incomplete = result(extension.sha256);
incomplete.result.status = "complete";
incomplete.result.reason = null;
expect(
validateGhidraExtensionResults(
[extension],
[{ ...incomplete, status: "complete", reason: null }],
),
).toBe(
"NativeAOT recovery omitted its supported format, method-table inventory or derived-memory identity.",
const rejection = validateGhidraExtensionResults(
[extension],
[{ ...incomplete, status: "complete", reason: null }],
);
expect(rejection).toContain("format");
expect(rejection).toContain("method-table");
expect(rejection).toContain("derived-memory");
});
it.each(["not_applicable", "unsupported", "failed"] as const)(
"rejects contradictory %s reports without discarding discovery context",
@@ -544,7 +526,7 @@ describe("Ghidra extension recovery coverage", () => {
[extension],
[{ ...report, result: { ...report.result, method_tables: 1 } }],
),
).toBe("NativeAOT non-recovery status carries recovered metadata.");
).toContain("metadata");
for (const reason of [null, "", " "] as const)
expect(
validateGhidraExtensionResults(
@@ -557,7 +539,7 @@ describe("Ghidra extension recovery coverage", () => {
},
],
),
).toBe("NativeAOT non-recovery status omits its required reason.");
).toContain("reason");
},
);
});
+17 -10
View File
@@ -87,10 +87,12 @@ describe("Hopper name inventory labels", () => {
it("leaves malformed provider inventories untouched for the consumer to reject", () => {
const value = [{ address: 1, value: "__objc_class_HuntRoot_class" }];
const original = structuredClone(value);
expect(projectHopperNameInventory(value)).toEqual({
result: value,
result: original,
changed: false,
});
expect(value).toEqual(original);
});
it("preserves source rows and the exact raw reply through the provider boundary", async () => {
@@ -110,6 +112,7 @@ describe("Hopper name inventory labels", () => {
objc_class_label: "hopper_legacy_class_object",
},
];
const originalSource = structuredClone(source);
const callTool = vi
.spyOn(HopperClient.prototype, "callTool")
.mockResolvedValue(ok(source));
@@ -126,14 +129,13 @@ describe("Hopper name inventory labels", () => {
format: "analysis-database",
});
const result = await client.execute("list_names", {});
expect(callTool).toHaveBeenCalledOnce();
expect(result.ok).toBe(true);
if (result.ok) {
try {
const result = await client.execute("list_names", {});
expect(callTool).toHaveBeenCalledOnce();
if (!result.ok) throw result.error;
expect(result.value.result).toEqual([
{
...source[0],
...originalSource[0],
objc_class_label: "hopper_legacy_class_object",
},
{
@@ -142,9 +144,14 @@ describe("Hopper name inventory labels", () => {
provider_metadata: { ordinal: 2 },
},
]);
expect(result.value.rawResult).toEqual(source);
expect(result.value.rawResult).toEqual(originalSource);
expect(source).toEqual(originalSource);
} finally {
try {
await client.close();
} finally {
callTool.mockRestore();
}
}
await client.close();
callTool.mockRestore();
});
});
+3 -1
View File
@@ -230,6 +230,7 @@ describe("Hopper relationship coverage", () => {
: operation === "procedure_callees"
? ["0x2000"]
: [];
const originalSource = structuredClone(source);
const callTool = vi
.spyOn(HopperClient.prototype, "callTool")
.mockImplementation((method) =>
@@ -277,7 +278,8 @@ describe("Hopper relationship coverage", () => {
procedure: "0x1000",
});
if (!observed.ok) throw observed.error;
expect(observed.value.result).toEqual(source);
expect(observed.value.result).toEqual(originalSource);
expect(source).toEqual(originalSource);
if (operation === "procedure_callers") {
expect(observed.value.rawResult).toMatchObject({
native_result: {
+3 -2
View File
@@ -61,6 +61,7 @@ it.each(["search_strings", "search_procedures"] as const)(
selected,
{ ...selected, value: "matched alternative" },
];
const originalSource = structuredClone(source);
const callTool = vi
.spyOn(HopperClient.prototype, "callTool")
.mockResolvedValue(ok(source));
@@ -83,12 +84,12 @@ it.each(["search_strings", "search_procedures"] as const)(
case_sensitive: true,
});
expect(result.ok).toBe(true);
if (result.ok) expect(result.value.result).toEqual([selected]);
if (result.ok) expect(result.value.result).toEqual([originalSource[1]]);
expect(callTool).toHaveBeenCalledOnce();
expect(callTool.mock.calls[0]?.[0]).toBe(
operation === "search_strings" ? "list_strings" : "list_procedures",
);
expect(source[1]).toEqual(selected);
expect(source).toEqual(originalSource);
} finally {
await client.close();
callTool.mockRestore();
+4 -4
View File
@@ -32,9 +32,9 @@ it.each([true, false])(
workspaceRoot: rawRoot,
}),
);
const before = await readdir(root);
const before = (await readdir(root)).sort();
const inspection = await inspectIdaRegistration(path, resourceScope);
const after = await readdir(root);
const after = (await readdir(root)).sort();
expect(inspection.available).toBe(exists);
expect(
inspection.checks.find(({ name }) => name === "private_workspace")
@@ -62,7 +62,7 @@ it.skipIf(process.platform === "win32")(
workspaceRoot: selected,
}),
);
const before = await readdir(root);
const before = (await readdir(root)).sort();
const inspection = await inspectIdaRegistration(path, resourceScope);
expect(inspection.available).toBe(false);
const check = inspection.checks.find(
@@ -71,7 +71,7 @@ it.skipIf(process.platform === "win32")(
expect(check).toMatchObject({ ok: false, code: "invalid_configuration" });
expect(check?.detail).toContain(selected);
expect(check?.detail).toContain("ENOTDIR");
expect(await readdir(root)).toEqual(before);
expect((await readdir(root)).sort()).toEqual(before);
expect(await readdir(workspace)).toEqual([]);
},
);
+14 -13
View File
@@ -112,7 +112,7 @@ it.each(["missing", "file", "directory", "omitted"] as const)(
throw new Error("Readiness must not connect to IDA");
});
const provider = new IdaProvider(config.value, {}, connect);
const before = await readdir(root);
const before = (await readdir(root)).sort();
try {
const snapshot = await provider.inspectReadinessSnapshot();
if (kind === "missing" || kind === "file") {
@@ -135,7 +135,7 @@ it.each(["missing", "file", "directory", "omitted"] as const)(
)?.reason,
).toContain("direct call edges");
} else expect(snapshot.availability.status).toBe("available");
expect(await readdir(root)).toEqual(before);
expect((await readdir(root)).sort()).toEqual(before);
expect(connect).not.toHaveBeenCalled();
} finally {
const closed = await provider.closeOwnedResources();
@@ -200,20 +200,20 @@ describe("IDA provider target support", () => {
const config = parseConfig({});
if (!config.ok) throw new Error("expected valid configuration");
const provider = new IdaProvider(config.value, {});
expect(
provider.inspectTargetSupport({
path: "/tmp/chre_pal.so",
sha256: "a".repeat(64),
kind: "executable",
format: "elf",
architecture: "hexagon",
availableArchitectures: ["hexagon"],
}),
).toMatchObject({
const support = provider.inspectTargetSupport({
path: "/tmp/chre_pal.so",
sha256: "a".repeat(64),
kind: "executable",
format: "elf",
architecture: "hexagon",
availableArchitectures: ["hexagon"],
});
expect(support).toMatchObject({
status: "unsupported",
code: "architecture_unsupported",
reason: expect.stringMatching(/hexagon.*Ghidra/su),
});
expect(support.reason).toContain("hexagon");
expect(support.reason).toContain("Ghidra");
});
it("admits only the families IDA admits today, so new families stay refused", () => {
const config = parseConfig({});
@@ -336,6 +336,7 @@ it.each(["http", "stdio"] as const)(
);
const changed = await provider.resolveAnalysisProfile(target);
if (!changed.ok) throw changed.error;
expect(changed.value.profile?.digest).toEqual(expect.any(String));
expect(changed.value.profile?.digest).not.toBe(profiles[0]);
} finally {
await provider.closeOwnedResources();
+5 -2
View File
@@ -1938,9 +1938,11 @@ describe("headless IDA cleanup verification", () => {
});
it("releases a privately owned worker after malformed open output using verified session inventory", async () => {
const { producer, client, root } = await fixture("headless");
let ownedId: string | undefined;
producer.beforeCall = async (name, args) => {
if (name === "idb_open") {
producer.database = z.string().parse(args.preferred_session_id);
ownedId = z.string().parse(args.preferred_session_id);
producer.database = ownedId;
producer.inputPath = z.string().parse(args.input_path);
}
};
@@ -1953,9 +1955,10 @@ describe("headless IDA cleanup verification", () => {
: undefined;
expect((await client.execute("health", {})).ok).toBe(false);
expect((await client.close()).ok).toBe(true);
expect(ownedId).toEqual(expect.any(String));
expect(
producer.calls.find(({ name }) => name === "idb_close")?.args.database,
).toMatch(/^rea-/u);
).toBe(ownedId);
expect(await readdir(root)).toEqual(["target.elf"]);
});
});
@@ -4,6 +4,8 @@ import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, expect, it, vi } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
const fileAccess = vi.hoisted(() => ({
path: undefined as string | undefined,
@@ -248,31 +250,43 @@ it("drains pending Electron discovery before shutdown cleanup completes", async
const listing = provider.listTargets(
listElectronTargetsInputSchema.parse({ cdp_endpoint: fake.endpoint }),
);
await started;
let closed = false;
const closing = provider.close().then(() => {
closed = true;
});
const closedBeforeDiscovery = await Promise.race([
closing.then(() => true),
new Promise<boolean>((resolve) => setImmediate(() => resolve(false))),
]);
expect(closedBeforeDiscovery).toBe(false);
expect(closed).toBe(false);
releaseDiscovery();
let closing: Promise<void> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(started, listing, "Electron discovery");
closing = provider.close().then(() => {
closed = true;
});
const closedBeforeDiscovery = await Promise.race([
closing.then(() => true),
new Promise<boolean>((resolve) => setImmediate(() => resolve(false))),
]);
expect(closedBeforeDiscovery).toBe(false);
expect(closed).toBe(false);
releaseDiscovery();
const [listed] = await Promise.all([listing, closing]);
expect(closed).toBe(true);
expect(listed.ok).toBe(true);
expect(fileAccess.calls).toContain("stat");
const rejected = await provider.listTargets(
listElectronTargetsInputSchema.parse({ cdp_endpoint: fake.endpoint }),
);
expect(rejected.ok).toBe(false);
if (rejected.ok)
throw new Error("Expected closed provider admission failure");
expect(rejected.error.cleanupIncomplete).toBe(false);
expect(rejected.error.cleanupResources).toEqual([]);
const [listed] = await Promise.all([listing, closing]);
expect(closed).toBe(true);
expect(listed.ok).toBe(true);
expect(fileAccess.calls).toContain("stat");
const rejected = await provider.listTargets(
listElectronTargetsInputSchema.parse({ cdp_endpoint: fake.endpoint }),
);
expect(rejected.ok).toBe(false);
if (rejected.ok)
throw new Error("Expected closed provider admission failure");
expect(rejected.error.cleanupIncomplete).toBe(false);
expect(rejected.error.cleanupResources).toEqual([]);
}, [
releaseDiscovery,
async () => {
await listing;
},
async () => {
await closing;
},
() => provider.close(),
]);
});
it("keeps ordinary and malformed file-location authorization unchanged", async () => {
+1 -1
View File
@@ -78,8 +78,8 @@ it.each([
expect(diagnostic).toMatchObject({
code: "target_unavailable",
message: expect.stringContaining(message),
remediation: { action: expect.stringContaining(action) },
});
expect(JSON.stringify(diagnostic)).toContain(action);
expect(inspector.commands).toEqual([]);
} finally {
await inspector.close();
@@ -6,6 +6,11 @@ import { pathToFileURL } from "node:url";
import { expect, it, onTestFinished, vi } from "vitest";
import { WebSocketServer } from "ws";
import {
withVerifierCleanup,
closeVerifierResources,
} from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { CdpConnection } from "../browser/CdpConnection.js";
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
@@ -74,6 +79,8 @@ const inspectorCloseFixture = async () => {
const firstClose = new Promise<void>((_resolve, reject) => {
rejectFirstClose = reject;
});
// Cleanup may reject the gate before a transport consumes it.
void firstClose.catch(() => undefined);
const originalClose = CdpConnection.prototype.close;
const closeSpy = vi
.spyOn(CdpConnection.prototype, "close")
@@ -89,14 +96,20 @@ const inspectorCloseFixture = async () => {
});
onTestFinished(async () => {
closeSpy.mockRestore();
if (closeCalls > 0) rejectFirstClose?.(new Error("fixture finalization"));
await new V8InspectorProvider(undefined, admission).close();
for (const socket of sockets.clients) socket.terminate();
await new Promise<void>((resolve) => sockets.close(() => resolve()));
if (server.listening)
await new Promise<void>((resolve) => server.close(() => resolve()));
await rm(root, { recursive: true, force: true });
await closeVerifierResources([
() => rejectFirstClose?.(new Error("fixture finalization")),
() => closeSpy.mockRestore(),
() => new V8InspectorProvider(undefined, admission).close(),
() => {
for (const socket of sockets.clients) socket.terminate();
},
() => new Promise<void>((resolve) => sockets.close(() => resolve())),
async () => {
if (server.listening)
await new Promise<void>((resolve) => server.close(() => resolve()));
},
() => rm(root, { recursive: true, force: true }),
]);
});
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
const address = server.address();
@@ -141,17 +154,33 @@ it("retains cancelled final-close ownership and retries the exact transport befo
const observing = observeJavaScriptRuntime(provider(), input, {
signal: controller.signal,
});
await closeStarted;
controller.abort(cancellationReason);
let firstObservationSettled = false;
void observing.finally(() => {
firstObservationSettled = true;
});
await Promise.resolve();
expect(firstObservationSettled).toBe(false);
rejectFirstClose?.(new Error("controlled final socket close failure"));
const result = await observing;
const result = await withVerifierCleanup(async () => {
await awaitAcknowledgement(
closeStarted,
observing,
"inspector final close",
);
controller.abort(cancellationReason);
let firstObservationSettled = false;
void observing.then(
() => {
firstObservationSettled = true;
},
() => {
firstObservationSettled = true;
},
);
await Promise.resolve();
expect(firstObservationSettled).toBe(false);
rejectFirstClose(new Error("controlled final socket close failure"));
return await observing;
}, [
() => controller.abort(new Error("fixture finalization")),
() => rejectFirstClose(new Error("fixture finalization")),
async () => {
await observing;
},
]);
expect(result.ok).toBe(false);
if (result.ok) throw new Error("Expected cleanup-incomplete cancellation");
const error = result.error;
@@ -202,15 +231,26 @@ it("retains cancelled final-close ownership and retries the exact transport befo
const activeObservation = observeJavaScriptRuntime(provider(), activeInput, {
signal: activeController.signal,
});
try {
await waitForConnections(4);
const queuedController = new AbortController();
let shutdown: Promise<void> | undefined;
let queuedObservation:
| ReturnType<typeof observeJavaScriptRuntime>
| undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
waitForConnections(4),
activeObservation,
"active inspector connection",
);
let shutdownSettled = false;
const shutdown = provider()
shutdown = provider()
.close()
.then(() => {
shutdownSettled = true;
});
const queuedObservation = observeJavaScriptRuntime(provider(), input);
queuedObservation = observeJavaScriptRuntime(provider(), input, {
signal: queuedController.signal,
});
await new Promise<void>((resolve) => setImmediate(resolve));
expect(shutdownSettled).toBe(false);
expect(fixture.acceptedConnections).toBe(4);
@@ -221,12 +261,26 @@ it("retains cancelled final-close ownership and retries the exact transport befo
expect(activeResult.ok).toBe(false);
await shutdown;
expect(shutdownSettled).toBe(true);
await waitForConnections(5);
await awaitAcknowledgement(
waitForConnections(5),
queuedObservation,
"queued inspector connection",
);
const queuedResult = await queuedObservation;
expect(queuedResult.ok).toBe(true);
expect(fixture.acceptedConnections).toBe(5);
expect(closeSpy).toHaveBeenCalledTimes(8);
} finally {
activeController.abort(new Error("fixture finalization"));
}
}, [
() => activeController.abort(new Error("fixture finalization")),
() => queuedController.abort(new Error("fixture finalization")),
async () => {
await activeObservation;
},
async () => {
await shutdown;
},
async () => {
await queuedObservation;
},
]);
});
+9 -4
View File
@@ -61,7 +61,7 @@ const tracer: NativeCallTracer = {
};
describe("private otool operand lifecycle", () => {
it("uses a private alias only for otool and removes it after all commands settle", async () => {
it("uses a private alias for native commands and removes it after all commands settle", async () => {
const path = await mkdtemp(join(tmpdir(), "rea-macho-operand-test-"));
const selected = await parenthesizedTarget();
const target = selected.target.path;
@@ -90,10 +90,13 @@ describe("private otool operand lifecycle", () => {
try {
const result = await client.execute("inspect_macho", {});
if (!result.ok) throw result.error;
expect(JSON.stringify(result.value.result)).toContain(
join(path, "artifact.macho"),
);
const alias = join(path, "artifact.macho");
const report = inspectMachoSchema.parse(result.value.result);
expect(
report.provenance
.find((entry) => entry.tool === "lipo")
?.command.at(-1),
).toBe(alias);
expect(calls.some(({ operand }) => operand === alias)).toBe(true);
expect(calls.some(({ operand }) => operand === target)).toBe(false);
await expect(stat(path)).rejects.toMatchObject({ code: "ENOENT" });
@@ -166,7 +169,9 @@ describe("private otool operand lifecycle", () => {
await rm(selected.directory, { recursive: true, force: true });
}
});
});
describe("private operand command cleanup", () => {
it("retains the alias until command cleanup is confirmed", async () => {
const path = await mkdtemp(join(tmpdir(), "rea-macho-operand-test-"));
let unresolved = true;
+36 -15
View File
@@ -12,6 +12,8 @@ import {
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, expect, it, vi, onTestFinished } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
import type { BinaryTarget } from "../domain/binaryTargetTypes.js";
@@ -246,22 +248,41 @@ it("waits for an in-flight signature binding before provider close", async () =>
);
const client = provider.createClient(target);
const operation = client.execute("inspect_signature", {});
await entered;
let closeSettled = false;
const closing = client.close().finally(() => {
closeSettled = true;
});
await Promise.resolve();
expect(closeSettled).toBe(false);
releaseRootFactory();
await expect(operation).resolves.toMatchObject({
ok: false,
error: { _tag: "AnalysisArtifactChangedError" },
});
await expect(closing).resolves.toMatchObject({ ok: true, value: null });
expect(closeSettled).toBe(true);
await expect(stat(roots[0]!)).rejects.toMatchObject({ code: "ENOENT" });
let closing: ReturnType<typeof client.close> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
entered,
operation,
"signature root acquisition",
);
closing = client.close().finally(() => {
closeSettled = true;
});
await Promise.resolve();
expect(closeSettled).toBe(false);
releaseRootFactory();
await expect(operation).resolves.toMatchObject({
ok: false,
error: { _tag: "AnalysisArtifactChangedError" },
});
await expect(closing).resolves.toMatchObject({ ok: true, value: null });
expect(closeSettled).toBe(true);
await expect(stat(roots[0]!)).rejects.toMatchObject({ code: "ENOENT" });
}, [
releaseRootFactory,
async () => {
await operation;
},
async () => {
const result = await closing;
if (result !== undefined && !result.ok) throw result.error;
},
async () => {
const result = await client.close();
if (!result.ok) throw result.error;
},
]);
});
it("releases retained descriptors before retrying their containing root", async () => {
-15
View File
@@ -3,7 +3,6 @@ import { readFileSync } from "node:fs";
import { expect, it } from "vitest";
import { inspectElfNotesInputSchema } from "../../domain/native/elfNotes.js";
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
import { BinaryFormatFailure } from "../metadata/BinaryContainer.js";
import { decodeElfSectionNameTable } from "../metadata/ElfSectionNameTable.js";
import { readElfDirectoryFromBuffer } from "../metadata/NativeSectionDirectory.js";
import {
@@ -243,20 +242,6 @@ it("keeps note facts but marks section names unavailable for unknown compression
);
});
it("tags valid extended section numbering as unsupported", () => {
let failure: unknown;
try {
parse(elfExtendedSectionCountFixture());
} catch (cause) {
failure = cause;
}
expect(failure).toBeInstanceOf(BinaryFormatFailure);
expect(failure).toMatchObject({
kind: "unsupported",
message: "ELF extended numbering is unsupported.",
});
});
it.each([1, 2])(
"admits algorithm %i section names when max_file_bytes includes expansion",
(compressionType) => {
+15 -8
View File
@@ -7,6 +7,8 @@ import { withArtifactRegularFile } from "../artifacts/ArtifactFileOperation.js";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { OwnedCommandFailure } from "./OwnedCommand.js";
import { EventEmitter } from "node:events";
import { PassThrough } from "node:stream";
@@ -837,7 +839,9 @@ it("observes cancellation after pending descriptor cleanup before admitting repl
{ message: "busy", details: {} },
);
let calls = 0;
try {
const caller = new AbortController();
let pending: ReturnType<typeof resources.run> | undefined;
await withVerifierCleanup(async () => {
const first = await resources.run(async () =>
ok(await resources.read("fixture", 1)),
);
@@ -845,13 +849,12 @@ it("observes cancellation after pending descriptor cleanup before admitting repl
ok: false,
error: { cleanupIncomplete: true },
});
const caller = new AbortController();
const reason = { request: "cancel while old descriptor closes" };
const pending = resources.run(async () => {
pending = resources.run(async () => {
calls++;
return ok("Must not run");
}, caller.signal);
await started;
await awaitAcknowledgement(started, pending, "descriptor cleanup");
caller.abort(reason);
releaseCleanup();
const result = await pending;
@@ -859,10 +862,14 @@ it("observes cancellation after pending descriptor cleanup before admitting repl
expect(result.error.cause).toBe(reason);
expect(calls).toBe(0);
if (!first.ok) expect(first.error.cleanupIncomplete).toBe(false);
} finally {
releaseCleanup();
await resources.close();
}
}, [
() => caller.abort(),
releaseCleanup,
async () => {
await pending;
},
() => resources.close(),
]);
});
it.each([false, true])(
+53 -30
View File
@@ -1,6 +1,8 @@
import { access } from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { expect, it } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
import { cleanupOwnedProcessGroup } from "./ProcessOwnership.js";
@@ -48,22 +50,34 @@ it("waits for an admitted late handshake before closing its process and root", a
announceInitialize();
await held;
});
await initializing;
await waitFor(() => runtime.supervisor !== undefined);
const rootPath = runtime.rootPath;
expect(rootPath).toBeDefined();
const closing = runtime.close().then(() => {
closeSettled = true;
});
await new Promise<void>((resolve) => setImmediate(resolve));
expect(closeSettled).toBe(false);
expect(runtime.supervisor).toBeDefined();
releaseInitialize();
await expect(starting).rejects.toThrow("closing");
await closing;
expect(runtime.supervisor).toBeUndefined();
expect(runtime.rootPath).toBeUndefined();
await expect(access(rootPath as string)).rejects.toThrow();
let closing: Promise<void> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(initializing, starting, "worker initialization");
await waitFor(() => runtime.supervisor !== undefined);
const rootPath = runtime.rootPath;
expect(rootPath).toBeDefined();
closing = runtime.close().then(() => {
closeSettled = true;
});
await new Promise<void>((resolve) => setImmediate(resolve));
expect(closeSettled).toBe(false);
expect(runtime.supervisor).toBeDefined();
releaseInitialize();
await expect(starting).rejects.toThrow("closing");
await closing;
expect(runtime.supervisor).toBeUndefined();
expect(runtime.rootPath).toBeUndefined();
await expect(access(rootPath as string)).rejects.toThrow();
}, [
releaseInitialize,
async () => {
await starting.catch(() => undefined);
},
async () => {
await closing;
},
() => runtime.close(),
]);
});
it("resolves a bound cleanup error after shared pending-owner retry succeeds", async () => {
@@ -87,22 +101,31 @@ it("resolves a bound cleanup error after shared pending-owner retry succeeds", a
},
}),
);
await runtime.start(startOptions());
const rootPath = runtime.rootPath;
const error = new ProviderCleanupError("owned-worker-test", [], {
reason: "controlled cleanup retry",
});
await expect(runtime.stop()).resolves.toMatchObject({ status: "incomplete" });
runtime.bindCleanupFailure(error);
expect(error.cleanupIncomplete).toBe(true);
expect(error.cleanupResources).toContain(rootPath);
await withVerifierCleanup(async () => {
await runtime.start(startOptions());
const rootPath = runtime.rootPath;
const error = new ProviderCleanupError("owned-worker-test", [], {
reason: "controlled cleanup retry",
});
await expect(runtime.stop()).resolves.toMatchObject({
status: "incomplete",
});
runtime.bindCleanupFailure(error);
expect(error.cleanupIncomplete).toBe(true);
expect(error.cleanupResources).toContain(rootPath);
await OwnedWorkerRuntime.retryPending();
await OwnedWorkerRuntime.retryPending();
expect(error.cleanupIncomplete).toBe(false);
expect(error.cleanupResources).toEqual([]);
expect(runtime.rootPath).toBeUndefined();
await expect(access(rootPath as string)).rejects.toThrow();
expect(error.cleanupIncomplete).toBe(false);
expect(error.cleanupResources).toEqual([]);
expect(runtime.rootPath).toBeUndefined();
await expect(access(rootPath as string)).rejects.toThrow();
}, [
() => {
failCleanup = false;
},
() => runtime.close(),
]);
});
it("observes request-stream errors and resets them for a fresh child", async () => {
@@ -1,4 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import {
cleanupOwnedProcessGroup,
type ProcessOwnershipHost,
@@ -287,14 +289,22 @@ describe("multi-group ownership observation cancellation", () => {
return outcome;
});
let settledBeforeRelease = false;
try {
await secondListStarted;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
secondListStarted,
observedOutcome,
"liveness recheck",
);
controller.abort(reason);
await new Promise<void>((resolve) => setImmediate(resolve));
settledBeforeRelease = settled;
} finally {
releaseSecondList?.();
}
}, [
() => controller.abort(reason),
() => releaseSecondList?.(),
async () => {
await observedOutcome;
},
]);
const outcome = await observedOutcome;
expect(settledBeforeRelease).toBe(true);
+29 -13
View File
@@ -1,6 +1,8 @@
import { EventEmitter } from "node:events";
import { PassThrough } from "node:stream";
import { expect, it, vi } from "vitest";
import { withVerifierCleanup } from "../../scripts/lib/verifier-cleanup.mjs";
import { awaitAcknowledgement } from "../../tests/fixtures/lifecycleAcknowledgement.js";
import {
ProviderProcessSupervisor,
type ProcessDiagnosticBudget,
@@ -29,19 +31,33 @@ it("retains final stderr between verified exit and output close during stop", as
},
});
const stopped = owner.stop();
expect(owner.stop()).toBe(stopped);
await started;
// Let cleanup observe exit before delivering the separately acknowledged output.
await new Promise<void>((resolve) => setImmediate(resolve));
producer.stderr.end("final launcher diagnostic");
producer.stdout.end();
producer.emit("close", 2, null);
await expect(stopped).resolves.toEqual({ status: "verified-cleanup" });
expect(owner.snapshot()).toMatchObject({
stderr: { text: "final launcher diagnostic" },
exitCode: 2,
});
expect(await owner.waitForOutputClose(0)).toBe(true);
await withVerifierCleanup(async () => {
expect(owner.stop()).toBe(stopped);
await awaitAcknowledgement(started, stopped, "process cleanup");
// Let cleanup observe exit before delivering the separately acknowledged output.
await new Promise<void>((resolve) => setImmediate(resolve));
producer.stderr.end("final launcher diagnostic");
producer.stdout.end();
producer.emit("close", 2, null);
await expect(stopped).resolves.toEqual({ status: "verified-cleanup" });
expect(owner.snapshot()).toMatchObject({
stderr: { text: "final launcher diagnostic" },
exitCode: 2,
});
expect(await owner.waitForOutputClose(0)).toBe(true);
}, [
() => {
producer.stderr.end();
producer.stdout.end();
if (!owner.observedOutputClose()) producer.emit("close", 2, null);
},
async () => {
await stopped;
},
() => owner.dispose(),
() => producer.stderr.destroy(),
() => producer.stdout.destroy(),
]);
});
it("bounds late output retention and drain without changing verified cleanup", async () => {
-87
View File
@@ -100,24 +100,6 @@ it.each([
nodeOptions: '"--max-old-space-size=256',
heap: effective(80, true),
},
{
name: "leaves a percentage flag unestablished instead of the 128 MiB request",
execArgv: ["--max-old-space-size-percentage=50"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "lets a percentage flag win over a later absolute flag",
execArgv: ["--max-old-space-size-percentage=50", "--max-old-space-size=64"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "lets a percentage flag win over an earlier absolute flag",
execArgv: ["--max-old-space-size=64", "--max-old-space-size-percentage=50"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "lets a NODE_OPTIONS percentage win over an execArgv absolute flag",
execArgv: ["--max-old-space-size=64"],
@@ -164,36 +146,12 @@ it.each([
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "leaves an env-file launch unestablished when NODE_OPTIONS was absent",
execArgv: ["--env-file=rea.env"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "leaves an env-file-if-exists launch unestablished when NODE_OPTIONS was absent",
execArgv: ["--env-file-if-exists=.env"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "does not let an absolute flag hide NODE_OPTIONS supplied by an env file",
execArgv: ["--env-file=rea.env", "--max-old-space-size=64"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "recognizes an env-file flag split from its path",
execArgv: ["--env-file", "rea.env"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "keeps an inherited NODE_OPTIONS value when an env file cannot override it",
execArgv: ["--env-file=rea.env"],
nodeOptions: "--max-old-space-size=64",
heap: effective(64, true),
},
{
name: "lets a later absolute flag win when an inherited NODE_OPTIONS blocks the env file",
execArgv: ["--max-old-space-size=32", "--env-file-if-exists=rea.env"],
@@ -206,33 +164,6 @@ it.each([
nodeOptions: "",
heap: effective(128, false),
},
{
name: "leaves a config-file percentage source unestablished",
execArgv: ["--experimental-config-file=node.config.json"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "leaves a default config-file launch unestablished",
execArgv: ["--experimental-default-config-file"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "recognizes a config-file flag split from its path",
execArgv: ["--experimental-config-file", "node.config.json"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "does not let an absolute flag hide a config-file heap option",
execArgv: [
"--experimental-config-file=node.config.json",
"--max-old-space-size=64",
],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "does not let a recovered NODE_OPTIONS hide a config-file heap option",
execArgv: ["--experimental-default-config-file"],
@@ -245,30 +176,12 @@ it.each([
nodeOptions: "--experimental-config-file=node.config.json",
heap: { kind: "unknown" } as const,
},
{
name: "leaves a positive --max-heap-size unestablished instead of the 128 MiB request",
execArgv: ["--max-heap-size=64"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "recognizes an underscore --max-heap-size",
execArgv: ["--max_heap_size=+064"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "leaves a NODE_OPTIONS --max-heap-size unestablished",
execArgv: [],
nodeOptions: "--max-heap-size=64",
heap: { kind: "unknown" } as const,
},
{
name: "does not let an old-space flag hide a positive --max-heap-size",
execArgv: ["--max-heap-size=64", "--max-old-space-size=256"],
nodeOptions: undefined,
heap: { kind: "unknown" } as const,
},
{
name: "does not let a later --max-heap-size hide an old-space flag",
execArgv: ["--max-old-space-size=64"],
@@ -173,7 +173,7 @@ it.skipIf(process.platform === "win32")(
expect(
snapshot.files.find(({ path }) => path === "root_0:readable.txt")
?.sha256,
).not.toBeNull();
).toEqual(expect.any(String));
expect(
snapshot.files.find(({ path }) => path === "root_0:unreadable.txt"),
).toMatchObject({ type: "file", size: 17, sha256: null });
@@ -529,15 +529,16 @@ it("cancels the bounded-wait timer once deliveries settle after an exit", async
it("keeps a non-empty reason when a signal delivery throws without a message", async () => {
const fake = fakeTerminal();
let signalStarted = false;
const throwing = {
...fake,
signalTarget: async (): Promise<never> => {
signalStarted = true;
throw new Error();
},
};
const pending = start(throwing, { timeout_ms: 100, finalization_ms: 5_000 });
await waitFor(() => throwing.signals.length >= 0);
await new Promise((resolve) => setTimeout(resolve, 150));
await waitFor(() => signalStarted);
throwing.deliverExit({ exitCode: 0, signal: 0 });
const exit = observed(await pending);
-62
View File
@@ -1095,68 +1095,6 @@ it("rejects code keys and members whose consts are unhashable", () => {
expect(hashable.coverage.status).toBe("complete");
});
it("uses the code equality fields for each CPython layout", () => {
const codeCollections = (
layout: "py38" | "py311",
different: boolean,
): [Buffer, Buffer] => {
const lineA = marshalCode(layout, {
name: "f",
filename: "fixture.py",
firstlineno: 1,
linetable: Buffer.from([2, 1]),
});
const lineB = marshalCode(layout, {
name: "f",
filename: "fixture.py",
firstlineno: 1,
linetable: Buffer.from([2, different ? 2 : 1]),
});
const frozenSet = Buffer.concat([
Buffer.from([0x3e]),
u32(2),
lineA,
lineB,
]);
const dict = Buffer.concat([
Buffer.from([0x7b]),
lineA,
Buffer.from([0x4e]),
lineB,
Buffer.from([0x54, 0x30]),
]);
return [frozenSet, dict];
};
for (const [magic, layout] of [
[3413, "py38"],
[3439, "py38"],
] as const) {
const [frozenSet, dict] = codeCollections(layout, true);
const report = parse(
Buffer.concat([
pythonPycHeader(magic),
marshalCode(layout, { consts: [frozenSet, dict] }),
]),
);
expect(root(report).consts).toEqual([
{ kind: "frozenset", length: 1 },
{ kind: "dict", length: 1 },
]);
}
const [frozenSet, dict] = codeCollections("py311", true);
const py312 = parse(
Buffer.concat([
pythonPycHeader(3531),
marshalCode("py311", { consts: [frozenSet, dict] }),
]),
);
expect(root(py312).consts).toEqual([
{ kind: "frozenset", length: 2 },
{ kind: "dict", length: 2 },
]);
});
it("keeps the first dictionary key when a later key compares equal", () => {
const key = (filename: string): Buffer =>
marshalCode("py311", {
+14 -44
View File
@@ -1,9 +1,8 @@
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import type { Client } from "@modelcontextprotocol/client";
import { Ajv2020 } from "ajv/dist/2020.js";
import type { AnySchema } from "ajv";
import { afterEach, expect, it } from "vitest";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { expect, it } from "vitest";
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { z } from "zod";
@@ -19,9 +18,10 @@ import { SessionProviderRouter } from "../application/binary/SessionProviderRout
import { toolContract } from "../contracts/toolContracts.js";
import { createEvidence } from "../domain/evidence.js";
import { jsonObjectSchema, type JsonValue } from "../domain/jsonValue.js";
import { createServer } from "./createServer.js";
import { createArtifactBinarySession } from "../composition/binary.js";
import { build } from "plist";
import { createApplicationMcpHarness } from "../../tests/fixtures/applicationMcpHarness.js";
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
const identity: ProviderIdentity = {
id: "bundle-resource-mcp-fixture",
@@ -29,14 +29,8 @@ const identity: ProviderIdentity = {
version: "1",
};
const resources: Array<{ close(): Promise<unknown> }> = [];
afterEach(async () => {
await Promise.all(resources.splice(0).map((resource) => resource.close()));
});
it("routes bundle resource MCP calls with null executable identity and retained Evidence", async () => {
const parent = await mkdtemp(join(tmpdir(), "rea-bundle-resource-mcp-"));
const parent = await createTestTempDirectory("rea-bundle-resource-mcp-");
const bundle = join(parent, "Fixture.app");
await mkdir(bundle);
const plistPath = join(bundle, "Info.plist");
@@ -82,22 +76,11 @@ it("routes bundle resource MCP calls with null executable identity and retained
new InvestigationRecords(),
{ createClient: () => ({ client: resourceClient, capabilities: [] }) },
);
const server = createServer({ kind: "session", session });
const client = new Client({ name: "bundle-resource-test", version: "1" });
const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();
resources.push({
close: async () => {
await client.close();
await server.close();
await session.close();
await rm(parent, { recursive: true, force: true });
},
const { client } = await createApplicationMcpHarness({
session,
clientIdentity: { name: "bundle-resource-test", version: "1" },
});
await server.connect(serverTransport);
await client.connect(clientTransport);
await expectResourceSchemaBranches(client);
const unbound = await client.callTool({
@@ -192,8 +175,8 @@ it("routes bundle resource MCP calls with null executable identity and retained
});
it("exposes the production resource session through the MCP SDK for a keyless bundle", async () => {
const parent = await mkdtemp(
join(tmpdir(), "rea-bundle-resource-production-mcp-"),
const parent = await createTestTempDirectory(
"rea-bundle-resource-production-mcp-",
);
const bundle = join(parent, "Fixture.app");
const resourceDirectory = join(bundle, "Resources");
@@ -210,23 +193,10 @@ it("exposes the production resource session through the MCP SDK for a keyless bu
);
const session = await createArtifactBinarySession({}, process.platform);
const server = createServer({ kind: "session", session });
const client = new Client({
name: "production-bundle-resource-test",
version: "1",
const { client } = await createApplicationMcpHarness({
session,
clientIdentity: { name: "production-bundle-resource-test", version: "1" },
});
const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();
resources.push({
close: async () => {
await client.close();
await server.close();
await session.close();
await rm(parent, { recursive: true, force: true });
},
});
await server.connect(serverTransport);
await client.connect(clientTransport);
const opened = await client.callTool({
name: "open_bundle_resources",
@@ -118,9 +118,18 @@ describe("replay value fidelity over MCP", () => {
name: "get_users_by_id",
arguments: { id: "3" },
});
expect(JSON.stringify(ownKeys.structuredContent)).toContain(
'"__proto__":{"retained":true}',
);
const content = ownKeys.structuredContent;
if (
typeof content !== "object" ||
content === null ||
!("body" in content)
)
throw new Error("Expected a structured replay body");
const body = content.body;
expect(body).toEqual(JSON.parse(proto));
if (typeof body !== "object" || body === null)
throw new Error("Expected an object replay body");
expect(Object.hasOwn(body, "__proto__")).toBe(true);
} finally {
await transport.terminateSession();
await client.close();
@@ -431,9 +431,28 @@ describe("state data plane intake", () => {
]);
expect(batch.json).toMatchObject([
{ id: 1, result: {} },
{ id: 2, result: { parts: [{ kind: "data" }] } },
{
id: 2,
result: {
parts: [
{
kind: "data",
data: {
rea_state: {
entities: expect.arrayContaining([
expect.objectContaining({
type: "/api/users",
id: "2",
fields: expect.objectContaining({ login: "renamed" }),
}),
]),
},
},
},
],
},
},
]);
expect(JSON.stringify(batch.json)).toContain('"login":"renamed"');
const notified = await plane.post({ jsonrpc: "2.0", method: "data/reset" });
expect(notified.status).toBe(202);
expect(loginOf(plane.store)).toBe("probe");
@@ -542,10 +561,29 @@ describe("state data plane timing and order", () => {
const replies = (await batch).json;
expect(replies).toMatchObject([
{ id: 1, result: {} },
{ id: 2, result: { parts: [{ kind: "data" }] } },
{
id: 2,
result: {
parts: [
{
kind: "data",
data: {
rea_state: {
entities: expect.arrayContaining([
expect.objectContaining({
type: "/api/users",
id: "2",
fields: expect.objectContaining({ login: "renamed" }),
}),
]),
},
},
},
],
},
},
]);
// The get arrived with the add, before the reset, so it reports the loaded state.
expect(JSON.stringify(replies)).toContain('"login":"renamed"');
await expect(reset).resolves.toMatchObject({ result: {} });
expect(loginOf(plane.store)).toBe("probe");
});
+81 -37
View File
@@ -13,6 +13,8 @@ import {
expectForeignHostRefused,
rawHttpRequest,
} from "../../../tests/support/http/rawRequest.js";
import { awaitAcknowledgement } from "../../../tests/fixtures/lifecycleAcknowledgement.js";
import { closeVerifierResources } from "../../../scripts/lib/verifier-cleanup.mjs";
import {
hostHeaderAllowlist,
McpSessionSlot,
@@ -1104,21 +1106,25 @@ it.each(["replacement", "DELETE"])(
const first = await connect(served.url);
let next: Awaited<ReturnType<typeof connect>> | undefined;
let replacement: ReturnType<typeof connect> | undefined;
let deletion:
| ReturnType<typeof first.transport.terminateSession>
| undefined;
const request = first.client
.callTool({ name: "slow", arguments: {} })
.catch(() => undefined);
try {
void first.client
.callTool({ name: "slow", arguments: {} })
.catch(() => undefined);
await started;
const deletion =
await awaitAcknowledgement(started, request, "tool start");
deletion =
ending === "DELETE" ? first.transport.terminateSession() : undefined;
if (deletion !== undefined) await aborted;
if (deletion !== undefined)
await awaitAcknowledgement(aborted, deletion, "tool cancellation");
let connected = false;
replacement = connect(served.url).then((value) => {
connected = true;
next = value;
return value;
});
await aborted;
await awaitAcknowledgement(aborted, replacement, "tool cancellation");
await new Promise<void>((resolve) => setImmediate(resolve));
expect(connections).toBe(1);
expect(connected).toBe(false);
@@ -1132,9 +1138,15 @@ it.each(["replacement", "DELETE"])(
]);
} finally {
release();
await replacement?.catch(() => undefined);
await first.client.close();
await next?.client.close();
await closeVerifierResources([
() => first.client.close(),
async () => {
await Promise.allSettled([request, deletion, replacement]);
},
async () => {
await next?.client.close();
},
]);
}
},
);
@@ -1162,7 +1174,11 @@ it.each(["ready", "failure"] as const)(
});
const settled = starting.catch(() => undefined);
try {
await factoryStarted.promise;
await awaitAcknowledgement(
factoryStarted.promise,
starting,
"factory start",
);
expect((await fetch(new URL("/pending", url))).status).toBe(503);
expect(handled).not.toHaveBeenCalled();
factoryGate.release();
@@ -1222,14 +1238,19 @@ it.each(["open stream", "late response"])(
const response = fetch(new URL("/stream", served.url)).catch(
() => undefined,
);
await entered.promise;
if (timing === "open stream") await response;
let closed = false;
const closing = served.prepareShutdown().then(() => {
closed = true;
});
let closing: Promise<void> | undefined;
try {
await cancelling.promise;
await awaitAcknowledgement(entered.promise, response, "route start");
if (timing === "open stream") await response;
let closed = false;
closing = served.prepareShutdown().then(() => {
closed = true;
});
await awaitAcknowledgement(
cancelling.promise,
closing,
"body cancellation",
);
await new Promise<void>((resolve) => setImmediate(resolve));
expect(closed).toBe(false);
expect(shutdownHooks()).toBe(0);
@@ -1238,8 +1259,14 @@ it.each(["open stream", "late response"])(
expect(shutdownHooks()).toBe(1);
} finally {
cleanup.release();
await closing;
await (await response)?.body?.cancel().catch(() => undefined);
await closeVerifierResources([
async () => {
await (closing ?? served.close());
},
async () => {
await (await response)?.body?.cancel().catch(() => undefined);
},
]);
}
},
);
@@ -1586,13 +1613,14 @@ it.each(["prepareShutdown", "close"] as const)(
const response = fetch(new URL("/pending", served.url)).catch(
() => undefined,
);
await started;
let closed = false;
const closing = served[method]().then(() => {
closed = true;
});
let closing: Promise<void> | undefined;
try {
await cleanupStarted;
await awaitAcknowledgement(started, response, "route start");
let closed = false;
closing = served[method]().then(() => {
closed = true;
});
await awaitAcknowledgement(cleanupStarted, closing, "route finalizer");
await new Promise<void>((resolve) => setImmediate(resolve));
expect(closed).toBe(false);
expect(shutdownHooks()).toBe(0);
@@ -1601,8 +1629,14 @@ it.each(["prepareShutdown", "close"] as const)(
expect(shutdownHooks()).toBe(1);
} finally {
release();
await closing;
await response;
await closeVerifierResources([
async () => {
await (closing ?? served.close());
},
async () => {
await response;
},
]);
}
},
);
@@ -1909,7 +1943,7 @@ it.each(["replacement", "DELETE", "shutdown"])(
);
try {
// With no stream prefetch, this proves the SDK is waiting on the old body.
await readStarted;
await awaitAcknowledgement(readStarted, pending, "request body read");
if (ending === "shutdown") await slot.close();
else {
if (ending === "DELETE") {
@@ -2034,12 +2068,14 @@ it.each(["after timeout", "during drain"])(
},
});
const first = await connect(served.url);
const request = first.client
.callTool({ name: "slow", arguments: {} })
.catch(() => undefined);
let replacement: Promise<Response | undefined> | undefined;
let pendingClose: Promise<void> | undefined;
try {
void first.client
.callTool({ name: "slow", arguments: {} })
.catch(() => undefined);
await started;
const replacement = fetch(served.url, {
await awaitAcknowledgement(started, request, "tool start");
replacement = fetch(served.url, {
method: "POST",
headers: {
"content-type": "application/json",
@@ -2051,8 +2087,8 @@ it.each(["after timeout", "during drain"])(
if (shutdownTiming === "during drain") return undefined;
throw cause;
});
await drainStarted;
const pendingClose =
await awaitAcknowledgement(drainStarted, replacement, "session drain");
pendingClose =
shutdownTiming === "during drain" ? served.close() : undefined;
const refused = await replacement;
if (shutdownTiming === "after timeout") expect(refused?.status).toBe(503);
@@ -2068,7 +2104,15 @@ it.each(["after timeout", "during drain"])(
expect(shutdowns).toBe(1);
} finally {
release();
await first.client.close();
await closeVerifierResources([
() => first.client.close(),
async () => {
await Promise.allSettled([request, replacement]);
},
async () => {
await pendingClose;
},
]);
}
},
);
Loaded 100 of 342 files, more files were not shown because too many files have changed in this diff. Show more