Files
reverse-engineering-anything/tests/acceptance/artifactComparisonHistoricalSummary.test.ts
T
d39b3000ec feat(artifacts): inventory and extract U-Boot FIT images (#7771)
* feat(artifacts): inventory and extract U-Boot FIT images

open_binary refused U-Boot Flattened Image Tree files as an unsupported
format, so inspect_artifact and extract_artifact could not list or
recover a FIT's kernel, devicetree, ramdisk and loadable images.

Recognize an FDT v16/v17 with a top-level /images node and parse it with
the existing parseDevicetree. Each /images child becomes an inventory
entry named images/<unit-name>.bin that extracts its stored bytes from
embedded data, data-offset (past the 4-aligned devicetree end) or
data-position (from the file start). Image format_metadata records the
declared type, arch, os, compression, load and entry; root metadata
records the description, timestamp and configurations.

crc32, md5, sha1, sha256, sha384 and sha512 hash nodes are verified in
one streamed pass per image; other algorithms are reported unverified.
Signatures are reported, not verified, and nothing is decompressed,
decrypted or executed. Data outside the file, overlapping or duplicate
image data, a missing or repeated data source and hash mismatches fail
closed. The devicetree is bounded by the fixed 16 MiB allowance;
external image data is streamed from disk.

Refs #7129

* fix(artifacts): bound FIT configuration references and suggest analysis only for plain images

* fix(artifacts): retain buffered FIT stream cancellation

* test(artifacts): gate FIT faults on the observed read phase

* fix(artifacts): verify FIT links and summary identities

* fix(artifacts): bound FIT diagnostics and retain unknown signatures

* fix(artifacts): compare FIT declarations and resolved data links

* fix(artifacts): admit FIT raw probes through the shared loader contract

* fix(artifacts): preserve FIT field availability independently

* fix(artifacts): isolate nested entry metadata from reader facts

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+users.noreply.github.com>
2026-10-29 13:33:26 +08:00

192 lines
7.5 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import {
artifactInventorySummaryViewResultSchema,
sealAnalysisView,
} from "../../src/domain/analysisView/analysisView.js";
import { artifactComparisonResultSchema } from "../../src/domain/artifactComparison.js";
import { digestCanonicalValue } from "../../src/domain/canonicalDigest.js";
import {
evidenceSemanticContent,
parseEvidence,
rebindEvidenceToTargetPath,
type Evidence,
} from "../../src/domain/evidence.js";
import { FIT_EMBEDDED } from "../../src/artifacts/fit/FitImage.fixture.js";
import { parseMcpToolError } from "../fixtures/mcpToolError.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
const resealSummary = (
evidence: Evidence,
result: ReturnType<typeof artifactInventorySummaryViewResultSchema.parse>,
): Evidence => {
const { view_digest: _digest, ...unsigned } = result;
const revised = {
...evidence,
normalized_result: artifactInventorySummaryViewResultSchema.parse(
sealAnalysisView(unsigned),
),
};
return parseEvidence({
...revised,
evidence_id: `ev_${digestCanonicalValue(evidenceSemanticContent(revised), "Evidence")}`,
});
};
it("compares a historical alias summary through its retained complete MCP parent and rejects resealed false facts", async () => {
const root = await createTestTempDirectory("rea-historical-summary-mcp-");
const path = join(root, "original.plist");
await writeFile(
path,
'<?xml version="1.0"?><plist version="1.0"><dict><key>label</key><string>hello</string></dict></plist>',
);
const { call } = await connectLocalToolsMcp();
const opened = await call("open_binary", { path });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const inspected = await call("inspect_artifact", { detail: "complete" });
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
const parent = parseEvidence(inspected.structuredContent);
const originalParent = JSON.stringify(parent);
const alias = join(root, "caller-selected-alias.plist");
const aliasedParent = rebindEvidenceToTargetPath(parent, {
path: alias,
sha256: parent.subject!.digest.sha256,
format: parent.subject!.format,
});
expect(aliasedParent.evidence_id).toBe(parent.evidence_id);
const projected = await call("inspect_analysis_view", {
source: { kind: "inline", evidence: aliasedParent },
view: { kind: "summary" },
});
expect(projected.isError, JSON.stringify(projected)).not.toBe(true);
const current = parseEvidence(projected.structuredContent);
const result = artifactInventorySummaryViewResultSchema.parse(
current.normalized_result,
);
expect(result).toMatchObject({
parent_evidence_id: parent.evidence_id,
artifact: { path: alias },
summary: { source_coverage: { scope: "root", status: "complete" } },
});
// Exact ordinary-root projector before ce1f4b1bb: no source_coverage or
// traversal caveat. Before b395190e4 the hash caveat was also absent; this
// fully hashed root has no such caveat in either era.
const { source_coverage: _coverage, ...summary } = result.summary;
const historical = resealSummary(current, {
...result,
summary,
unknowns: result.unknowns.filter(
(value) =>
value !==
"Producer traversal coverage is unknown; missing matches do not prove source absence." &&
value !==
"Content hashes are unknown for some recorded files; missing node IDs do not prove content absence.",
),
});
expect(historical.evidence_id).not.toBe(current.evidence_id);
const originalHistorical = JSON.stringify(historical);
const compared = await call("compare_artifacts", {
left: historical,
right: parent,
});
expect(compared.isError, JSON.stringify(compared)).not.toBe(true);
const comparison = parseEvidence(compared.structuredContent);
expect(
artifactComparisonResultSchema.parse(comparison.normalized_result),
).toMatchObject({
status: "unchanged",
summary: { unchanged: 1, unknown: 0 },
changes: [],
});
expect(comparison.evidence_links).toContain(parent.evidence_id);
expect(comparison.evidence_links).not.toContain(historical.evidence_id);
const historicalResult = artifactInventorySummaryViewResultSchema.parse(
historical.normalized_result,
);
const forged = resealSummary(historical, {
...historicalResult,
summary: {
...historicalResult.summary,
available_counts: {
...historicalResult.summary.available_counts,
nodes: historicalResult.summary.available_counts.nodes + 1,
},
},
});
const rejected = await call("compare_artifacts", {
left: forged,
right: parent,
});
expect(rejected.isError, JSON.stringify(rejected)).toBe(true);
expect(parseMcpToolError(rejected)).toMatchObject({
error: { code: "evidence_integrity_mismatch" },
});
expect(JSON.stringify(parent)).toBe(originalParent);
expect(JSON.stringify(historical)).toBe(originalHistorical);
const retained = await call("inspect_analysis_view", {
source: { kind: "retained-evidence", evidence_id: parent.evidence_id },
view: { kind: "summary" },
});
expect(retained.isError, JSON.stringify(retained)).not.toBe(true);
expect(
parseEvidence(retained.structuredContent).normalized_result,
).toMatchObject({
parent_evidence_id: parent.evidence_id,
artifact: { path },
summary: { source_coverage: { scope: "root", status: "complete" } },
});
});
it("rejects a fabricated pre-coverage FIT summary while accepting its current projection", async () => {
const root = await createTestTempDirectory("rea-fit-summary-identity-");
const path = join(root, "boot.itb");
await writeFile(path, FIT_EMBEDDED);
const { call } = await connectLocalToolsMcp({
REA_MCP_INPUT_SCHEMA_PROFILE: "compact",
REA_MCP_TOOL_FAMILIES: "artifact,application,session",
});
const opened = await call("open_binary", { path });
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
const inspected = await call("inspect_artifact", { detail: "complete" });
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
const parent = parseEvidence(inspected.structuredContent);
const projected = await call("inspect_analysis_view", {
source: { kind: "inline", evidence: parent },
view: { kind: "summary" },
});
expect(projected.isError, JSON.stringify(projected)).not.toBe(true);
const current = parseEvidence(projected.structuredContent);
const result = artifactInventorySummaryViewResultSchema.parse(
current.normalized_result,
);
expect(result.summary.manifest.root_format).toBe("fit");
expect(result.summary.source_coverage?.status).toBe("complete");
const accepted = await call("compare_artifacts", {
left: current,
right: parent,
});
expect(accepted.isError, JSON.stringify(accepted)).not.toBe(true);
expect(
artifactComparisonResultSchema.parse(
parseEvidence(accepted.structuredContent).normalized_result,
).status,
).toBe("unchanged");
const { source_coverage: _coverage, ...summary } = result.summary;
const fabricated = resealSummary(current, { ...result, summary });
expect(fabricated.evidence_id).not.toBe(current.evidence_id);
const rejected = await call("compare_artifacts", {
left: fabricated,
right: parent,
});
expect(rejected.isError, JSON.stringify(rejected)).toBe(true);
expect(parseMcpToolError(rejected)).toMatchObject({
error: { code: "evidence_integrity_mismatch" },
});
});