Files
reverse-engineering-anything/tests/acceptance/nativeDispatchSnapshot.test.ts
T
morluto 9b3a633298 fix(evidence): retain caller paths across snapshot replay
Preserve the verbatim direct CLI selection in caller_input_path, separate from canonical subject identity and provider/query parameters. Fresh publication uses the existing immutable metadata import path so provider auto-recording cannot discard request provenance. Replay returns the current selection without rewriting stored Evidence or partitioning the cache by alias. Historical records without the optional field still authenticate.

Use the domain semantic projection for ledger agreement, remove the duplicate projection and subject-only replacement names, and preserve request metadata at same-invocation summary delivery. Later retained-view questions do not inherit that context. Existing one-shot parameters.input_path remains for compatibility.

Validation: 149 focused tests, 28 public CLI/MCP cases, all canonical advertised schemas through the pinned SDK, and nine documentation tests passed across the affected checks. Build, typecheck/lint, and formatting passed. Two Windows-only replay cases were skipped on macOS. Native macOS and managed fixtures were exercised; no Hopper/Ghidra/IDA runtime was started.
2026-10-25 02:20:13 +08:00

389 lines
16 KiB
TypeScript

import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { toolContract } from "../../src/contracts/toolContracts.js";
import { parseEvidence } from "../../src/domain/evidence.js";
import { readAnalysisSnapshot } from "../../src/application/binary/AnalysisSnapshotFiles.js";
import { workflowAnalysisProfile } from "../../src/application/WorkflowAnalysisProfile.js";
import { APPLE_DISPATCH_METADATA_ANALYSIS_PROFILE } from "../../src/native/NativeMacOSProviderMetadata.js";
import {
CPU,
machoImage,
} from "../../src/artifacts/apple/MachoImage.fixture.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
import { cliTest } from "../support/cli/cliFixture.js";
import { nativeObjcClassImportMachO } from "../fixtures/nativeObjcClassImportMachO.js";
import { objcRecordBudgetMacho } from "../fixtures/objcRecordBudgetMacho.js";
import { parseMcpToolError } from "../fixtures/mcpToolError.js";
cliTest.skipIf(process.platform !== "darwin")(
"restores the exact profileless workflow commitment after a rejected snapshot switch",
async () => {
const root = await createTestTempDirectory("rea-native-snapshot-restore-");
const first = join(root, "first.macho");
const second = join(root, "second.macho");
const saved = join(root, "before.json");
const conflicting = join(root, "conflicting.json");
const after = join(root, "after.json");
const bytes = machoImage({ cpu: CPU.arm64, commands: [] });
await Promise.all([writeFile(first, bytes), writeFile(second, bytes)]);
const { call } = await connectLocalToolsMcp({
PATH: process.env.PATH ?? "",
REA_ANALYSIS_PROVIDER: "auto",
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
GHIDRA_INSTALL_DIR: "/rea-unconfigured-deep-provider/ghidra",
JAVA_HOME: "/rea-unconfigured-deep-provider/java",
REA_IDA_MCP_CONFIG: "/rea-unconfigured-deep-provider/ida-mcp.json",
REA_KUNA_COMMAND: "/rea-unconfigured-deep-provider/kuna",
REA_KUNA_CACHE_DIR: "/rea-unconfigured-deep-provider/kuna-cache",
});
const good = async (name: string, arguments_: Record<string, unknown>) => {
const result = await call(name, arguments_);
expect(result.isError, JSON.stringify(result)).not.toBe(true);
return result;
};
const unknown = {
question: "Does this function reach the network?",
domain: "network",
supporting_evidence_ids: [],
contradicting_evidence_ids: [],
required_authority: "shipped-artifact",
required_confidence: "observed",
required_environment: null,
recommended_probes: [
{ operation: "analyze_function", rationale: "Inspect its callers." },
],
relationships: [],
};
await good("open_binary", { path: second });
await good("inspect_native_dispatch_metadata", { max_records: 100 });
await good("record_unknown", { ...unknown, severity: "high" });
await good("close_binary", { snapshot_path: conflicting, overwrite: true });
await good("open_binary", { path: first });
const original = parseEvidence(
(await good("inspect_native_dispatch_metadata", { max_records: 100 }))
.structuredContent,
);
expect(original.analysis_profile).toEqual(
workflowAnalysisProfile(
APPLE_DISPATCH_METADATA_ANALYSIS_PROFILE,
"inspect_native_dispatch_metadata",
),
);
await good("record_unknown", { ...unknown, severity: "medium" });
await good("close_binary", { snapshot_path: saved, overwrite: true });
const savedSnapshot = await readAnalysisSnapshot(saved);
if (!savedSnapshot.ok) throw savedSnapshot.error;
expect(savedSnapshot.value.binding.analysis_profile).toEqual(
APPLE_DISPATCH_METADATA_ANALYSIS_PROFILE,
);
expect(
savedSnapshot.value.workflow_entries[0]?.execution.analysis_profile,
).toEqual(original.analysis_profile);
await good("open_binary", { path: first, snapshot_path: saved });
const rejected = await call("open_binary", {
path: second,
snapshot_path: conflicting,
});
expect(parseMcpToolError(rejected)).toMatchObject({
error: {
code: "evidence_integrity_mismatch",
details: { reason: "Conflicting unknown revision" },
},
});
expect((await good("binary_session", {})).structuredContent).toMatchObject({
result: { open: true, path: first },
});
const observed = parseEvidence(
(await good("inspect_native_dispatch_metadata", { max_records: 1 }))
.structuredContent,
);
expect(observed.analysis_profile).toEqual(original.analysis_profile);
await good("open_binary", { path: first, snapshot_path: saved });
await good("close_binary", { snapshot_path: after, overwrite: true });
const snapshot = await readAnalysisSnapshot(after);
if (!snapshot.ok) throw snapshot.error;
expect(snapshot.value.binding.analysis_profile).toEqual(
savedSnapshot.value.binding.analysis_profile,
);
expect(
snapshot.value.workflow_entries[0]?.execution.analysis_profile,
).toEqual(
savedSnapshot.value.workflow_entries[0]?.execution.analysis_profile,
);
expect(await readFile(first)).toEqual(Buffer.from(bytes));
expect(await readFile(second)).toEqual(Buffer.from(bytes));
},
30000,
);
cliTest.skipIf(process.platform !== "darwin")(
"preserves ambiguous encoded method implementations through the public CLI and MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-objc-method-candidates-");
const target = join(root, "methods.macho");
const bytes = objcRecordBudgetMacho("class", [0x700, 0x720]);
await writeFile(target, bytes);
const environment = {
PATH: process.env.PATH ?? "",
REA_ANALYSIS_PROVIDER: "auto",
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
GHIDRA_INSTALL_DIR: "/rea-unconfigured-deep-provider/ghidra",
JAVA_HOME: "/rea-unconfigured-deep-provider/java",
REA_IDA_MCP_CONFIG: "/rea-unconfigured-deep-provider/ida-mcp.json",
REA_KUNA_COMMAND: "/rea-unconfigured-deep-provider/kuna",
REA_KUNA_CACHE_DIR: "/rea-unconfigured-deep-provider/kuna-cache",
REA_LOG_LEVEL: "silent",
};
const result = await cli.run({
arguments: ["inspect-native-dispatch-metadata", target, "--json"],
environment,
});
expect(
result.exitCode,
`${result.stderr}\n${JSON.stringify(result.json)}`,
).toBe(0);
const cliEvidence = toolContract(
"inspect_native_dispatch_metadata",
).outputSchema.parse(result.json);
const { client, call } = await connectLocalToolsMcp(environment);
const opened = await call("open_binary", { path: target });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const inspected = await call("inspect_native_dispatch_metadata", {});
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
const mcpEvidence = parseEvidence(inspected.structuredContent);
for (const evidence of [cliEvidence, mcpEvidence]) {
expect(evidence.normalized_result).toMatchObject({
result: {
objc_classes: [
{
name: "FixtureClass",
methods: [{ selector: "classMethod", address: null }],
},
],
objc_dispatch_implementations: [
{ selector: "classMethod", implementation_address: "0x100000700" },
{ selector: "classMethod", implementation_address: "0x100000720" },
],
},
});
}
expect(await readFile(target)).toEqual(bytes);
await client.ping();
},
30000,
);
cliTest.skipIf(process.platform !== "darwin")(
"retains and reopens profileless native dispatch workflow snapshots through CLI and stdio MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-native-dispatch-snapshot-");
const target = join(root, "minimal.macho");
const cliSnapshot = join(root, "cli-snapshot.json");
const mcpSnapshot = join(root, "mcp-snapshot.json");
const bytes = machoImage({ cpu: CPU.arm64, commands: [] });
await writeFile(target, bytes);
const isolatedProviderEnvironment = {
PATH: process.env.PATH ?? "",
REA_ANALYSIS_PROVIDER: "auto",
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
GHIDRA_INSTALL_DIR: "/rea-unconfigured-deep-provider/ghidra",
JAVA_HOME: "/rea-unconfigured-deep-provider/java",
REA_IDA_MCP_CONFIG: "/rea-unconfigured-deep-provider/ida-mcp.json",
REA_KUNA_COMMAND: "/rea-unconfigured-deep-provider/kuna",
REA_KUNA_CACHE_DIR: "/rea-unconfigured-deep-provider/kuna-cache",
};
const cliArguments = [
"inspect-native-dispatch-metadata",
target,
"--snapshot",
cliSnapshot,
"--json",
];
const firstCli = await cli.run({
arguments: cliArguments,
environment: isolatedProviderEnvironment,
});
expect(firstCli.exitCode, firstCli.stderr).toBe(0);
const cliEvidence = toolContract(
"inspect_native_dispatch_metadata",
).outputSchema.parse(firstCli.json);
expect(cliEvidence.caller_input_path).toBe(target);
const cliSaved = await readAnalysisSnapshot(cliSnapshot);
if (!cliSaved.ok) throw cliSaved.error;
expect(cliSaved.value.binding.analysis_profile).toEqual(
APPLE_DISPATCH_METADATA_ANALYSIS_PROFILE,
);
expect(cliSaved.value.entries).toEqual([]);
expect(cliSaved.value.workflow_entries).toHaveLength(1);
expect(
cliSaved.value.evidence_bundle.records.find(
(record) => record.evidence_id === cliEvidence.evidence_id,
)?.caller_input_path,
).toBe(target);
const savedQueryId = cliSaved.value.workflow_entries[0]?.query_id;
const replayCli = await cli.run({
arguments: [
"inspect-native-dispatch-metadata",
"minimal.macho",
"--snapshot",
cliSnapshot,
"--json",
],
cwd: root,
environment: isolatedProviderEnvironment,
});
expect(replayCli.exitCode, replayCli.stderr).toBe(0);
const replayEvidence = toolContract(
"inspect_native_dispatch_metadata",
).outputSchema.parse(replayCli.json);
expect(replayEvidence).toEqual({
...cliEvidence,
caller_input_path: "minimal.macho",
});
const replaySaved = await readAnalysisSnapshot(cliSnapshot);
if (!replaySaved.ok) throw replaySaved.error;
expect(replaySaved.value.workflow_entries[0]?.query_id).toBe(savedQueryId);
expect(replaySaved.value.workflow_entries[0]?.parameters).toEqual(
cliSaved.value.workflow_entries[0]?.parameters,
);
expect(
replaySaved.value.evidence_bundle.records.find(
(record) => record.evidence_id === cliEvidence.evidence_id,
)?.caller_input_path,
).toBe(target);
const changedLimitCli = await cli.run({
arguments: [...cliArguments, "--max-records", "1"],
environment: isolatedProviderEnvironment,
});
expect(changedLimitCli.exitCode, changedLimitCli.stderr).toBe(0);
const changedLimitEvidence = parseEvidence(changedLimitCli.json);
expect(changedLimitEvidence.parameters.max_records).toBe(1);
expect(changedLimitEvidence.evidence_id).not.toBe(cliEvidence.evidence_id);
const changedLimitSnapshot = await readAnalysisSnapshot(cliSnapshot);
if (!changedLimitSnapshot.ok) throw changedLimitSnapshot.error;
expect(changedLimitSnapshot.value.workflow_entries).toHaveLength(2);
const { client, call } = await connectLocalToolsMcp(
isolatedProviderEnvironment,
);
const opened = await call("open_binary", { path: target });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const firstMcp = await call("inspect_native_dispatch_metadata", {
max_records: 100,
});
expect(firstMcp.isError, JSON.stringify(firstMcp)).not.toBe(true);
const mcpEvidence = parseEvidence(firstMcp.structuredContent);
expect(mcpEvidence).not.toHaveProperty("caller_input_path");
const closed = await call("close_binary", {
snapshot_path: mcpSnapshot,
overwrite: true,
});
expect(closed.isError, JSON.stringify(closed)).not.toBe(true);
const mcpSaved = await readAnalysisSnapshot(mcpSnapshot);
if (!mcpSaved.ok) throw mcpSaved.error;
expect(mcpSaved.value.binding.analysis_profile).toEqual(
APPLE_DISPATCH_METADATA_ANALYSIS_PROFILE,
);
expect(mcpSaved.value.entries).toEqual([]);
expect(mcpSaved.value.workflow_entries).toHaveLength(1);
expect(mcpSaved.value.evidence_bundle.records[0]).not.toHaveProperty(
"caller_input_path",
);
const reopened = await call("open_binary", {
path: target,
snapshot_path: mcpSnapshot,
});
expect(reopened.isError, JSON.stringify(reopened)).not.toBe(true);
const reobserved = await call("inspect_native_dispatch_metadata", {
max_records: 100,
});
expect(reobserved.isError, JSON.stringify(reobserved)).not.toBe(true);
expect(parseEvidence(reobserved.structuredContent)).toEqual(mcpEvidence);
expect(await readFile(target)).toEqual(Buffer.from(bytes));
await client.ping();
},
30000,
);
cliTest.skipIf(process.platform !== "darwin")(
"reports exact Objective-C class imports through the compiled native MCP adapter",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-objc-import-mcp-");
const target = join(root, "class-import.macho");
const fixture = nativeObjcClassImportMachO();
await writeFile(target, fixture.bytes);
const environment = {
PATH: process.env.PATH ?? "",
REA_ANALYSIS_PROVIDER: "auto",
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-deep-provider/hopper",
GHIDRA_INSTALL_DIR: "/rea-unconfigured-deep-provider/ghidra",
JAVA_HOME: "/rea-unconfigured-deep-provider/java",
REA_IDA_MCP_CONFIG: "/rea-unconfigured-deep-provider/ida-mcp.json",
REA_KUNA_COMMAND: "/rea-unconfigured-deep-provider/kuna",
REA_KUNA_CACHE_DIR: "/rea-unconfigured-deep-provider/kuna-cache",
REA_LOG_LEVEL: "silent",
};
const cliResult = await cli.run({
arguments: ["inspect-native-dispatch-metadata", target, "--json"],
environment,
});
expect(cliResult.exitCode, cliResult.stderr).toBe(0);
const cliEvidence = toolContract(
"inspect_native_dispatch_metadata",
).outputSchema.parse(cliResult.json);
expect(cliEvidence.normalized_result).toMatchObject({
result: {
target_selection: {
container: "thin",
offset: 0,
cpu_type: CPU.arm64.type,
cpu_subtype: CPU.arm64.subtype,
},
},
});
const { client, call } = await connectLocalToolsMcp(environment);
const opened = await call("open_binary", { path: target });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const inspected = await call("inspect_native_dispatch_metadata", {
max_records: 100,
objc_class_import_addresses: [fixture.address, fixture.unboundAddress],
objc_class_import_addresses_complete: true,
});
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
const evidence = parseEvidence(inspected.structuredContent);
expect(evidence.normalized_result).toMatchObject({
target_sha256: expect.stringMatching(/^[a-f0-9]{64}$/u),
target_architecture: "arm64",
provider: { id: "native-macos" },
result: {
target_selection: {
container: "thin",
offset: 0,
cpu_type: CPU.arm64.type,
cpu_subtype: CPU.arm64.subtype,
},
objc_class_import_bindings: [
{
requested_address: fixture.address,
symbol: "_OBJC_CLASS_$_Document",
library: "/System/Library/Frameworks/AppKit.framework/AppKit",
},
],
objc_class_import_binding_coverage: {
status: "complete",
examined: 2,
decoded: 1,
},
},
});
await client.ping();
},
);