mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 18:05:45 +00:00
Universal Mach-O admission reported the selected slice architecture while the IDA provider's private input copy handed the whole FAT container to upstream idalib, which then picked its own slice (x86_64 for /bin/cat) while Evidence labeled the session arm64. FAT parsing now exports the selected slice's byte range as universalSlice, and the private input copy writes exactly those bytes: the whole source digest still anchors the copy against admission-time changes, and the thinned result anchors the post-operation identity check. Thin targets keep the whole-file copy, and mach-o-universal remains the container format across Evidence, snapshots, and provider routing. Validation: typecheck, oxlint (no new warnings), oxfmt, and targeted vitest runs — ida adapters 12 files/116 tests, domain and services projects 60 files/736 tests, ghidra/hopper behavior 2 files/50 tests, filesystem and universal-extraction boundary 2 files/49 tests, native signature MCP 1 file/2 tests, plus a new thinning suite (5 tests, red before the fix: 3 failed). The thinned product was verified byte-identical to the `clang -arch arm64` input of a `lipo -create` universal fixture. Controlled fixtures only; no live IDA analysis was performed for this change. Closes #2139
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { access, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect, it, vi } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
|
|
|
|
import { ArtifactProvider } from "../../../../src/artifacts/ArtifactProvider.js";
|
|
import { ArtifactReaderFailure } from "../../../../src/artifacts/ArtifactReader.js";
|
|
import { artifactExtractionExecutionSchema } from "../../../../src/contracts/artifactToolContracts.js";
|
|
import type { BinaryTarget } from "../../../../src/domain/binaryTargetTypes.js";
|
|
|
|
const sliceReader = vi.hoisted(() => ({
|
|
reached: vi.fn(),
|
|
failure: null as unknown,
|
|
}));
|
|
|
|
vi.mock("../../../../src/artifacts/MachOSliceArtifactReader.js", () => ({
|
|
MachOSliceArtifactReader: class {
|
|
async *entries() {
|
|
sliceReader.reached();
|
|
yield* [];
|
|
throw sliceReader.failure;
|
|
}
|
|
|
|
close() {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
provenance() {
|
|
return [];
|
|
}
|
|
},
|
|
}));
|
|
|
|
it("routes universal Mach-O extraction through slice inventory", async () => {
|
|
sliceReader.reached.mockClear();
|
|
sliceReader.failure = new ArtifactReaderFailure(
|
|
"unavailable",
|
|
"fixture reached universal slice inventory",
|
|
);
|
|
const root = await createTestTempDirectory("rea-extract-universal-");
|
|
const image = join(root, "Universal");
|
|
await writeFile(image, Buffer.from([0xca, 0xfe, 0xba, 0xbe, 0, 0, 0, 0]));
|
|
const output = join(root, "output");
|
|
const result = await new ArtifactProvider(process.env)
|
|
.createClient(universalTarget(image))
|
|
.execute(
|
|
"extract_artifact",
|
|
artifactExtractionExecutionSchema.parse({ output_root: output }),
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
_tag: "ArtifactOperationError",
|
|
reason: "unavailable",
|
|
detail: "fixture reached universal slice inventory",
|
|
},
|
|
});
|
|
expect(sliceReader.reached).toHaveBeenCalledOnce();
|
|
await expect(access(output)).rejects.toThrow();
|
|
});
|
|
|
|
const universalTarget = (path: string): BinaryTarget => ({
|
|
path,
|
|
sourcePath: path,
|
|
sha256: "0".repeat(64),
|
|
kind: "executable",
|
|
format: "mach-o-universal",
|
|
architecture: "arm64",
|
|
availableArchitectures: ["arm64"],
|
|
universalSlice: { offset: 0, size: 32 },
|
|
});
|