mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
* 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
124 lines
4.2 KiB
JavaScript
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);
|
|
}
|
|
}
|