mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 18:22:42 +00:00
fix(firmware): derive nested coverage from each reported file (#10077)
* fix(firmware): account for unclassified nested file bytes * fix(firmware): describe nested gaps without invented chunk facts
This commit is contained in:
1 parent
2f87d3edb8
commit
fd9d362d98
3 files changed
+302
-20
No files matched your search
@@ -0,0 +1,264 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { gzipSync } from "node:zlib";
|
||||
import { expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
||||
import { createAnalysisExecution } from "../application/AnalysisProvider.js";
|
||||
import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js";
|
||||
import { ok } from "../domain/result.js";
|
||||
import { startFirmwareDeadline } from "./FirmwareDeadline.js";
|
||||
import {
|
||||
FirmwareFileAccess,
|
||||
inventoryFirmwareOutput,
|
||||
} from "./FirmwareFiles.js";
|
||||
import { publishFirmwareExtraction } from "./FirmwarePublication.js";
|
||||
import { normalizeUnblobReport } from "./FirmwareReports.js";
|
||||
|
||||
const hash = (bytes: Buffer) =>
|
||||
createHash("sha256").update(bytes).digest("hex");
|
||||
const known = (id: string, start: number, end: number) => ({
|
||||
__typename__: "ChunkReport",
|
||||
id,
|
||||
start_offset: start,
|
||||
end_offset: end,
|
||||
size: end - start,
|
||||
handler_name: "authored-handler",
|
||||
is_encrypted: false,
|
||||
extraction_reports: [],
|
||||
});
|
||||
const cases = [
|
||||
{ name: "opaque child", spans: [], gaps: [[0, 6]], coverage: "partial" },
|
||||
{
|
||||
name: "partly classified child",
|
||||
spans: [[2, 4]],
|
||||
gaps: [
|
||||
[0, 2],
|
||||
[4, 2],
|
||||
],
|
||||
coverage: "partial",
|
||||
},
|
||||
{
|
||||
name: "fully classified child",
|
||||
spans: [[0, 6]],
|
||||
gaps: [],
|
||||
coverage: "complete",
|
||||
},
|
||||
{
|
||||
name: "explicit unknown child",
|
||||
spans: [],
|
||||
gaps: [],
|
||||
coverage: "partial",
|
||||
unknown: true,
|
||||
},
|
||||
{
|
||||
name: "empty child",
|
||||
spans: [],
|
||||
gaps: [],
|
||||
coverage: "complete",
|
||||
empty: true,
|
||||
},
|
||||
] as const;
|
||||
|
||||
/** Inert gzip bytes and matching authored Unblob task reports; no engine runs. */
|
||||
const capture = async (selection: (typeof cases)[number]) => {
|
||||
const root = await createTestTempDirectory("rea-nested-firmware-coverage-");
|
||||
const staging = join(root, "staging");
|
||||
await mkdir(staging);
|
||||
const child = Buffer.from("empty" in selection ? "" : "opaque");
|
||||
const compressed = gzipSync(child);
|
||||
const original = Buffer.concat([
|
||||
Buffer.from("prefix!"),
|
||||
compressed,
|
||||
Buffer.from("suffix"),
|
||||
]);
|
||||
const path = join(root, "firmware.bin");
|
||||
const inputPath = join(root, "selected.gz");
|
||||
const childPath = join(staging, "child.bin");
|
||||
await writeFile(path, original);
|
||||
await writeFile(inputPath, compressed);
|
||||
await writeFile(childPath, child);
|
||||
const task = {
|
||||
path: childPath,
|
||||
depth: 1,
|
||||
blob_id: "container",
|
||||
is_multi_file: false,
|
||||
};
|
||||
const stat = (path: string, size: number) => ({
|
||||
__typename__: "StatReport",
|
||||
path,
|
||||
size,
|
||||
is_file: true,
|
||||
is_dir: false,
|
||||
is_link: false,
|
||||
link_target: null,
|
||||
});
|
||||
const raw = [
|
||||
{
|
||||
task: { path: inputPath, depth: 0, blob_id: "", is_multi_file: false },
|
||||
reports: [
|
||||
stat(inputPath, compressed.length),
|
||||
{ __typename__: "HashReport", sha256: hash(compressed) },
|
||||
known("container", 0, compressed.length),
|
||||
],
|
||||
subtasks: [task],
|
||||
},
|
||||
{
|
||||
task,
|
||||
reports: [
|
||||
stat(childPath, child.length),
|
||||
{ __typename__: "HashReport", sha256: hash(child) },
|
||||
...selection.spans.map(([start, end], index) =>
|
||||
known(String(index), start, end),
|
||||
),
|
||||
...("unknown" in selection
|
||||
? [
|
||||
{
|
||||
__typename__: "UnknownChunkReport",
|
||||
id: "opaque",
|
||||
start_offset: 0,
|
||||
end_offset: child.length,
|
||||
size: child.length,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
subtasks: [],
|
||||
},
|
||||
];
|
||||
return {
|
||||
root,
|
||||
staging,
|
||||
path,
|
||||
inputPath,
|
||||
child,
|
||||
original,
|
||||
raw,
|
||||
context: {
|
||||
inputPath,
|
||||
outputRoot: staging,
|
||||
sha256: hash(compressed),
|
||||
length: compressed.length,
|
||||
offset: 7,
|
||||
maxDepth: 3,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/** Exercise actual file publication and the shared Evidence boundary. */
|
||||
const evidence = async (fixture: Awaited<ReturnType<typeof capture>>) => {
|
||||
const files = new FirmwareFileAccess();
|
||||
const output = join(fixture.root, "published");
|
||||
try {
|
||||
const normalized = normalizeUnblobReport(fixture.raw, fixture.context);
|
||||
const published = await publishFirmwareExtraction({
|
||||
files,
|
||||
workspacePath: fixture.root,
|
||||
deadline: startFirmwareDeadline("publication", 120_000),
|
||||
analysisTimeoutMs: 120_000,
|
||||
outputDirectory: output,
|
||||
entries: await inventoryFirmwareOutput(
|
||||
fixture.staging,
|
||||
1024,
|
||||
10,
|
||||
1024,
|
||||
"extract_firmware",
|
||||
),
|
||||
normalized,
|
||||
selection: {
|
||||
offset: 7,
|
||||
length: fixture.context.length,
|
||||
sha256: fixture.context.sha256,
|
||||
},
|
||||
maxDepth: 3,
|
||||
engine: {
|
||||
name: "unblob",
|
||||
version: "authored-fixture",
|
||||
executable_path: "/inert/unused/unblob",
|
||||
executable_sha256: "a".repeat(64),
|
||||
source_revision: null,
|
||||
worker_count: 1,
|
||||
},
|
||||
signal: new AbortController().signal,
|
||||
retainCleanup: () => {
|
||||
throw new Error("Unexpected fixture cleanup failure");
|
||||
},
|
||||
});
|
||||
const execution = createAnalysisExecution(
|
||||
published,
|
||||
{ id: "unblob", name: "Authored Unblob report", version: "fixture" },
|
||||
{
|
||||
subject: {
|
||||
path: fixture.path,
|
||||
sha256: hash(fixture.original),
|
||||
format: "file",
|
||||
},
|
||||
rawResult: fixture.raw,
|
||||
},
|
||||
);
|
||||
const result = await new FirmwareAnalysisService({
|
||||
execute: async () => ok(execution),
|
||||
}).execute("extract_firmware", {
|
||||
path: fixture.path,
|
||||
range: { offset: 7, length: fixture.context.length },
|
||||
output_directory: output,
|
||||
});
|
||||
if (!result.ok) throw result.error;
|
||||
expect(result.value.normalized_result).toHaveProperty(
|
||||
"extraction_gaps",
|
||||
published.extraction_gaps,
|
||||
);
|
||||
expect(result.value.normalized_result).toHaveProperty(
|
||||
"extraction_continuations",
|
||||
published.extraction_continuations,
|
||||
);
|
||||
expect(
|
||||
(await readFile(join(output, "child.bin"))).equals(fixture.child),
|
||||
).toBe(true);
|
||||
expect((await readFile(fixture.path)).equals(fixture.original)).toBe(true);
|
||||
expect(result.value.raw_result).toEqual(fixture.raw);
|
||||
return { value: result.value, published };
|
||||
} finally {
|
||||
await files.close();
|
||||
}
|
||||
};
|
||||
|
||||
it.each(cases)(
|
||||
"retains $name coverage and child-relative gaps in service Evidence",
|
||||
async (selection) => {
|
||||
const fixture = await capture(selection);
|
||||
const result = await evidence(fixture);
|
||||
expect(result.value.normalized_result).toMatchObject({
|
||||
coverage: selection.coverage,
|
||||
});
|
||||
const implicit = result.published.extraction_gaps.filter(
|
||||
(gap) => gap.kind === "unclassified-range",
|
||||
);
|
||||
expect(implicit).toMatchObject(
|
||||
selection.gaps.map(([offset, length]) => ({
|
||||
input_path: "$output/child.bin",
|
||||
range: { offset, length },
|
||||
root_file_range: null,
|
||||
})),
|
||||
);
|
||||
expect(implicit).toHaveLength(selection.gaps.length);
|
||||
if ("unknown" in selection)
|
||||
expect(result.published.extraction_gaps).toMatchObject([
|
||||
{
|
||||
kind: "unknown-chunk-handler",
|
||||
input_path: "$output/child.bin",
|
||||
range: { offset: 0, length: 6 },
|
||||
root_file_range: null,
|
||||
},
|
||||
]);
|
||||
if (selection.gaps.length > 0)
|
||||
for (const continuation of result.published.extraction_continuations)
|
||||
expect(continuation.instruction).not.toContain("was carved");
|
||||
for (const gap of result.published.extraction_gaps) {
|
||||
expect(result.published.extraction_continuations).toContainEqual({
|
||||
id: gap.continuation_id,
|
||||
instruction: expect.stringContaining("relative to the published child"),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user