Files
reverse-engineering-anything/scripts/lib/ghidra-value-scope-snapshot-e2e.mjs
T
morluto c5ed046d6e fix(ghidra)!: qualify memory effect coverage (#8651)
* fix(ghidra): qualify memory effect coverage

* fix(native): retain value trace source limitations

* fix(native): distinguish value trace policy commitments

* fix(ghidra): bind value-flow scope to snapshot profile

* test(ghidra): verify legacy value-flow snapshot recovery
2026-11-08 15:05:51 +00:00

124 lines
4.2 KiB
JavaScript

import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { parseBinaryTarget } from "../../dist/application/BinaryTargetResolver.js";
import { createAnalysisProfile } from "../../dist/domain/analysisProfile.js";
import {
createAnalysisSnapshotEntry,
parseAnalysisSnapshot,
serializeAnalysisSnapshot,
snapshotBinding,
snapshotTarget,
} from "../../dist/domain/analysisSnapshot.js";
import { createEvidence } from "../../dist/domain/evidence.js";
import { createEvidenceBundle } from "../../dist/domain/evidenceBundle.js";
import {
requireMcpLifecycleResult,
requireMcpOutput,
requireMcpToolError,
} from "./mcp-verifier-results.mjs";
/** A pre-scope dossier must not be replayed into a newly qualified trace. */
export async function verifyLegacyValueScopeSnapshot(
client,
{ target, procedure, observed, env },
) {
const { value_flow_effect_scope: scope, ...legacyParameters } =
observed.analysis_profile.parameters;
const legacyProfile = createAnalysisProfile(
observed.provider,
legacyParameters,
);
const parsed = await parseBinaryTarget(target.path, {
cwd: process.cwd(),
hostArchitecture: process.arch,
});
if (!parsed.ok) throw parsed.error;
const nativeTarget = parsed.value;
const result = structuredClone(observed.normalized_result);
const flow = result.native_value_flow;
assert.equal(flow.available, true);
const describesScope = (value) =>
value.includes("LOAD and STORE") && value.includes("address varnodes");
assert.ok(flow.limitations.some(describesScope));
flow.limitations = flow.limitations.filter((value) => !describesScope(value));
const evidence = createEvidence(nativeTarget, observed.provider, {
operation: observed.operation,
parameters: observed.parameters,
result,
rawResult: result,
analysisProfile: legacyProfile,
limitations: observed.limitations,
locations: observed.locations,
});
const binding = snapshotBinding(legacyProfile);
const identity = snapshotTarget(nativeTarget);
const snapshot = parseAnalysisSnapshot({
target: identity,
binding,
entries: [
createAnalysisSnapshotEntry({
target: identity,
binding,
operation: observed.operation,
parameters: observed.parameters,
execution: {
result,
rawResult: result,
provider: observed.provider,
analysisProfile: legacyProfile,
limitations: observed.limitations,
locations: observed.locations,
subject: nativeTarget,
},
}),
],
workflow_entries: [],
evidence_bundle: createEvidenceBundle([evidence]),
});
const path = join(env.TMPDIR, `legacy-value-scope-${randomUUID()}.json`);
await writeFile(path, serializeAnalysisSnapshot(snapshot), { flag: "wx" });
try {
const bytes = await readFile(path);
const session = requireMcpLifecycleResult(
await client.callTool({ name: "binary_session", arguments: {} }),
"binary_session",
);
const reply = await client.callTool({
name: "open_binary",
arguments: {
path: target.path,
provider_id: "ghidra",
snapshot_path: path,
},
});
assert.equal(reply.isError, true);
assert.equal(scope, "pcode-load-store-explicit-v1");
const error = requireMcpToolError(reply);
assert.equal(error.code, "evidence_integrity_mismatch");
assert.deepEqual(error.details.binding_mismatch.profile_digest, {
snapshot: legacyProfile.digest,
selected: observed.analysis_profile.digest,
});
assert.deepEqual(
requireMcpLifecycleResult(
await client.callTool({ name: "binary_session", arguments: {} }),
"binary_session",
),
session,
);
const fresh = await client.callTool(
{ name: "trace_native_values", arguments: { procedure, max_depth: 0 } },
{ timeout: 180000 },
);
assert.notEqual(fresh.isError, true, JSON.stringify(fresh));
const trace = requireMcpOutput(fresh, "trace_native_values");
assert.ok(trace.normalized_result.limitations.some(describesScope));
assert.deepEqual(await readFile(path), bytes);
} finally {
await rm(path);
}
}