test: guard target format vocabularies and DOL snapshot saves (#7481)

* test(mcp): save and reopen a GameCube DOL snapshot

#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.

* test(binary): require every target format in each target vocabulary

A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.

This is a guard until target formats have a single owner.

* test(mcp): assert the replayed DOL result and the owned replay limitation

The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.

* test: list Intel HEX and S-record targets in the vocabulary guard

Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.

* test: list UnityFS among the target format fixtures

Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list uImage among the target format fixtures

Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list the ESP image among the target format fixtures

Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
This commit is contained in:
Entrpi authored and GitHub committed 2026-10-30 22:35:53 +08:00
1 parent 19ab239278
commit 54756ff24e
3 files changed
+250 -1

No files matched your search

@@ -140,6 +140,10 @@ export const requiresNativeSourceAdmissionBeforeReplay = (
providerId: string | undefined,
): boolean => process.platform === "win32" && providerId === "ghidra";
/** Limitation that marks every observation replayed from a snapshot. */
export const SNAPSHOT_REPLAY_LIMITATION =
"Loaded from a local REA analysis snapshot; this call did not re-run the provider.";
/** Bounded in-memory cache for one immutable binary identity. */
export class AnalysisSnapshotCache {
readonly #entries = new Map<string, AnalysisSnapshotEntry>();
@@ -496,7 +500,7 @@ export class AnalysisSnapshotCache {
analysisProfile: structuredClone(profile),
limitations: [
...cached.execution.limitations,
"Loaded from a local REA analysis snapshot; this call did not re-run the provider.",
SNAPSHOT_REPLAY_LIMITATION,
],
...(cached.execution.relationship_coverage === null &&
RELATIONSHIP_OPERATIONS.has(operation)
@@ -0,0 +1,177 @@
import { describe, expect, it } from "vitest";
import {
analysisSnapshotSchema,
snapshotTarget,
} from "../../domain/analysisSnapshot.js";
import type { BinaryTarget } from "../../domain/binaryTargetTypes.js";
import { executableFormatHintSchema } from "../../domain/dosCom.js";
import { createEvidence, parseEvidence } from "../../domain/evidence.js";
import {
createImmutableEvidenceBundle,
parseEvidenceBundle,
} from "../../domain/evidenceBundle.js";
import { binaryTargetSummary } from "./BinaryTargetSummary.js";
/**
* One admitted target per format. A format added to `BinaryTarget` fails
* compilation here until it has a fixture, and the cases below then require
* every vocabulary that persists or projects targets to accept it. This guards
* the failure class of #7290 until target formats have a single owner.
*/
type TargetFixtures = {
readonly [Format in BinaryTarget["format"]]: BinaryTarget & {
readonly format: Format;
};
};
const identity = { path: "/fixture/target", sha256: "a".repeat(64) } as const;
const archive = { ...identity, kind: "archive" } as const;
const artifact = { ...identity, kind: "artifact" } as const;
const TARGETS: TargetFixtures = {
"mach-o": {
...identity,
kind: "executable",
format: "mach-o",
architecture: "arm64",
availableArchitectures: ["arm64"],
},
"mach-o-universal": {
...identity,
kind: "executable",
format: "mach-o-universal",
architecture: "arm64",
availableArchitectures: ["x86_64", "arm64"],
universalSlice: { offset: 0x4000, size: 0x4000 },
},
elf: {
...identity,
kind: "executable",
format: "elf",
architecture: "x86_64",
availableArchitectures: ["x86_64"],
},
pe: {
...identity,
kind: "executable",
format: "pe",
architecture: "x86_64",
availableArchitectures: ["x86_64"],
executableRole: "application",
managed: false,
},
"dos-mz": {
...identity,
kind: "executable",
format: "dos-mz",
architecture: "x86",
availableArchitectures: ["x86"],
},
"dos-com": {
...identity,
kind: "executable",
format: "dos-com",
architecture: "x86",
availableArchitectures: ["x86"],
},
// A raw-only family: detected formats must not admit it.
"raw-binary": {
...identity,
kind: "executable",
format: "raw-binary",
architecture: "m68k",
availableArchitectures: ["m68k"],
entryPoint: "0x1000",
entryPointSource: "0x1000",
},
"gamecube-dol": {
...identity,
kind: "executable",
format: "gamecube-dol",
architecture: "powerpc",
availableArchitectures: ["powerpc"],
byteOrder: "big",
bits: 32,
entryPoint: "0x80003100",
entryPointSource: "dol-header",
dol: {
sections: [],
bss: { address: 0, size: 0 },
bssOverlaps: [],
bssGaps: [],
entryPoint: 0x8000_3100,
},
},
"analysis-database": {
...identity,
kind: "database",
format: "analysis-database",
},
zip: { ...archive, format: "zip" },
ipa: { ...archive, format: "ipa" },
apk: { ...archive, format: "apk" },
msix: { ...archive, format: "msix" },
appx: { ...archive, format: "appx" },
hap: { ...archive, format: "hap" },
hsp: { ...archive, format: "hsp" },
"app-pack": { ...archive, format: "app-pack" },
asar: { ...archive, format: "asar" },
"godot-pck": { ...archive, format: "godot-pck" },
unityfs: { ...archive, format: "unityfs" },
uimage: { ...archive, format: "uimage" },
dmg: { ...archive, format: "dmg" },
pkg: { ...archive, format: "pkg" },
"intel-hex": { ...archive, format: "intel-hex" },
srec: { ...archive, format: "srec" },
"esp-image": { ...archive, format: "esp-image" },
fit: { ...archive, format: "fit" },
plist: { ...artifact, format: "plist" },
javascript: { ...artifact, format: "javascript" },
"source-map": { ...artifact, format: "source-map" },
};
const provider = { id: "fixture", name: "Fixture", version: "1" } as const;
const roundTrip = (value: unknown): unknown =>
JSON.parse(JSON.stringify(value));
describe("target format vocabularies", () => {
it.each(Object.values(TARGETS))(
"persists and projects $format targets",
(target) => {
const evidence = createEvidence(target, provider, {
operation: "fixture",
parameters: {},
result: { observed: true },
});
expect(parseEvidence(roundTrip(evidence)).subject).toMatchObject({
format: target.format,
architecture: target.architecture ?? null,
});
const bundle = createImmutableEvidenceBundle([evidence]);
expect(parseEvidenceBundle(roundTrip(bundle)).artifacts).toEqual([
{
digest: { sha256: target.sha256 },
format: target.format,
architecture: target.architecture ?? null,
},
]);
expect(
analysisSnapshotSchema.shape.target.parse(
roundTrip(snapshotTarget(target)),
),
).toMatchObject({ format: target.format, kind: target.kind });
expect(binaryTargetSummary(target)).toMatchObject({
format: target.format,
kind: target.kind,
});
},
);
it.each(executableFormatHintSchema.options)(
"admits the caller-selected %s hint only as an executable target format",
(hint) => {
expect(TARGETS[hint]).toMatchObject({ format: hint, kind: "executable" });
},
);
});