mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 00:07:18 +00:00
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:
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" });
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user