fix(evidence): own cached snapshot data

This commit is contained in:
morluto
2026-10-27 07:16:55 +08:00
parent 08f99dbaf2
commit 087acf26e8
3 changed files with 131 additions and 17 deletions
@@ -33,6 +33,66 @@ import {
import { EvidenceLedger } from "../investigation/EvidenceLedger.js";
import { enhancedOutputSchemas } from "../../contracts/toolOutputSchemaGroups.js";
it.each(["provider", "workflow"] as const)(
"owns nested %s execution data before returning control to callers",
(kind) => {
const cache = new AnalysisSnapshotCache();
const target = structuredClone(ANALYSIS_SNAPSHOT_TARGET);
const profile = structuredClone(ANALYSIS_SNAPSHOT_PROFILE);
const analysisProfile =
kind === "provider" ? profile : workflowAnalysisProfile(profile);
const execution = createAnalysisExecution(
{ nested: { value: "original" } },
structuredClone(
kind === "provider"
? ANALYSIS_SNAPSHOT_PROVIDER
: REA_WORKFLOW_PROVIDER,
),
{
rawResult: { nested: { value: "raw" } },
subject: target,
analysisProfile,
locations: [{ kind: "address", address: "0x1000" }],
relationshipCoverage: {
status: "partial",
limitations: ["original coverage"],
},
},
);
const input = {
target,
profile,
operation: "procedure_info" as const,
parameters: { address: "0x1000", document: "fixture" },
execution: { ...execution, analysisProfile },
};
if (kind === "provider") cache.record(input);
else cache.recordWorkflow(input);
const entries = () =>
kind === "provider" ? cache.entries() : cache.workflowEntries();
const expected = entries();
for (const payload of [execution.result, execution.rawResult]) {
if (payload === null || typeof payload !== "object")
throw new Error("Expected structured observation payload");
Reflect.set(payload, "nested", { value: "caller edit" });
}
const location = execution.locations[0];
if (location !== undefined) Reflect.set(location, "address", "0x2000");
if (execution.relationshipCoverage?.status === "partial")
execution.relationshipCoverage.limitations.push("caller edit");
Reflect.set(execution.limitations, 0, "caller limitation");
Reflect.set(execution.provider, "name", "caller provider");
Reflect.set(execution.subject, "path", "/tmp/caller-edited-subject");
if (kind === "workflow") {
const upstream = analysisProfile.parameters.upstream_analysis_profile;
if (upstream === null || typeof upstream !== "object")
throw new Error("Expected nested workflow profile");
Reflect.set(upstream, "parameters", { loader: "caller profile" });
}
expect(entries()).toEqual(expected);
},
);
it("exports independent nested query, workflow and Evidence payloads", () => {
const cache = new AnalysisSnapshotCache();
const target = ANALYSIS_SNAPSHOT_TARGET;
+18 -17
View File
@@ -358,25 +358,26 @@ type SnapshotExecutionProjection = Pick<
| "subject"
>;
/** Project one authenticated execution into the shared persisted snapshot shape. */
/** Own one execution's snapshot data independently of its caller-visible result. */
const snapshotExecutionProjection = (
execution: SnapshotExecution,
provider: AnalysisSnapshotEntry["execution"]["provider"],
): SnapshotExecutionProjection => ({
result: execution.result,
raw_result: execution.rawResult,
provider,
limitations: [...execution.limitations],
relationship_coverage: execution.relationshipCoverage ?? null,
locations: [...execution.locations],
subject:
execution.subject === null
? null
: subjectSchema.parse({
...execution.subject,
architecture: execution.subject.architecture ?? null,
}),
});
): SnapshotExecutionProjection =>
entrySchema.shape.execution.parse({
result: execution.result,
raw_result: execution.rawResult,
provider,
limitations: execution.limitations,
relationship_coverage: execution.relationshipCoverage ?? null,
locations: execution.locations,
subject:
execution.subject === null
? null
: {
...execution.subject,
architecture: execution.subject.architecture ?? null,
},
});
const canonicalJson = (value: unknown): string => {
const json = jsonValueSchema.parse(value);
@@ -549,7 +550,7 @@ export const createAnalysisSnapshotWorkflowEntry = (input: {
execution,
execution.analysisProfile.provider,
),
analysis_profile: execution.analysisProfile,
analysis_profile: analysisProfileSchema.parse(execution.analysisProfile),
},
};
};
@@ -14,6 +14,59 @@ import { buildManagedPeFixture } from "../../../src/dotnet/ManagedPe.fixture.js"
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
it("keeps real provider replay and persisted Evidence independent of caller edits", async () => {
const directory = await createTestTempDirectory(
"rea-snapshot-result-ownership-",
);
const path = join(directory, "application.exe");
await writeFile(path, buildManagedPeFixture());
const provider = new ManagedStaticProvider();
const profile = createAnalysisProfile(MANAGED_STATIC_PROVIDER, {
fixture: "snapshot-result-ownership",
});
const session = createTestBinarySession(provider, {
resolveAnalysisProfile: () => Promise.resolve(ok({ profile })),
});
onTestFinished(async () => {
const closed = await session.close();
if (!closed.ok) throw closed.error;
});
const opened = await session.open(path);
if (!opened.ok) throw opened.error;
const observed = await session.execute("inspect_managed_artifact", {});
if (!observed.ok) throw observed.error;
const expected = structuredClone(observed.value.result);
if (observed.value.evidenceId === undefined)
throw new Error("Real managed observation has no Evidence identity");
const historical = session.evidenceById(observed.value.evidenceId);
if (historical === undefined) throw new Error("Observation was not retained");
if (
observed.value.result === null ||
typeof observed.value.result !== "object"
)
throw new Error("Expected a structured managed artifact result");
Reflect.set(observed.value.result, "caller_edit", "must not enter replay");
const replayed = await session.execute("inspect_managed_artifact", {});
if (!replayed.ok) throw replayed.error;
expect(replayed.value.evidenceId).toBe(observed.value.evidenceId);
expect(replayed.value.result).toEqual(expected);
expect(session.evidenceById(observed.value.evidenceId)).toEqual(historical);
const snapshot = session.exportAnalysisSnapshot();
if (!snapshot.ok) throw snapshot.error;
const snapshotPath = join(directory, "analysis.json");
const written = await session.writeAnalysisSnapshot(
snapshot.value,
snapshotPath,
false,
);
if (!written.ok) throw written.error;
const restored = await readAnalysisSnapshot(snapshotPath);
if (!restored.ok) throw restored.error;
expect(restored.value.entries[0]?.execution.result).toEqual(expected);
expect(restored.value.evidence_bundle.records).toContainEqual(historical);
});
it("reruns ambiguous imported observations without changing their historical Evidence", async () => {
const directory = await createTestTempDirectory("rea-snapshot-provenance-");
const path = join(directory, "application.exe");