mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 17:28:08 +00:00
* fix: bind identity and coverage to the producing rule Logical paths, container families, examined bytes, path-segment conventions, destination collisions, file-id leases, Mach-O CPU selection, and firmware cleanup now follow the observation that owns each rule. * fix: fail unmatched Mach-O slices and keep firmware roots independent A fat target whose architecture lipo did not list now stops before slice tools run. Chained fixups still count unread LC_DYLD_INFO rebase bytes, and a firmware cleanup failure no longer blocks the next workspace. * test: cover path identity and runtime families at the projection boundary NFC joins and segment conventions are now asserted by the path resolver, the Apple projection, and a real APK inventory instead of helper unit tests. * test: expect unread dyld rebase bytes in the Apple dispatch fixture The legacy LC_DYLD_INFO fixture keeps a rebase range this reader does not decode, so the public CLI and MCP check now requires that partial coverage beside the bind opcodes. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
|
import { err, ok } from "../domain/result.js";
|
|
import type { NativeCommandCapture } from "./CommandRunner.js";
|
|
import { inspectNativeMacho } from "./NativeMachoInspection.js";
|
|
|
|
const capture = (tool: string, stdout: string): NativeCommandCapture => ({
|
|
tool,
|
|
executable: tool,
|
|
executableSha256: "a".repeat(64),
|
|
toolVersion: null,
|
|
versionReason: null,
|
|
arguments: [],
|
|
stdout,
|
|
stderr: "",
|
|
stdoutBytes: stdout.length,
|
|
stderrBytes: 0,
|
|
exitCode: 0,
|
|
signal: null,
|
|
});
|
|
|
|
const inspect = async (lipo: string, architecture: "x86" | "arm64") => {
|
|
const commands: {
|
|
readonly tool: string;
|
|
readonly args: readonly string[];
|
|
}[] = [];
|
|
const result = await inspectNativeMacho({
|
|
target: {
|
|
kind: "executable",
|
|
format: "mach-o",
|
|
path: "/fixture/app",
|
|
sha256: "b".repeat(64),
|
|
architecture,
|
|
availableArchitectures: ["x86", "arm64"],
|
|
},
|
|
run: (tool, args) => {
|
|
commands.push({ tool, args });
|
|
if (tool === "lipo") return Promise.resolve(ok(capture(tool, lipo)));
|
|
if (tool === "file") return Promise.resolve(ok(capture(tool, "Mach-O")));
|
|
return Promise.resolve(err(new AnalysisCancelledError("inspect_native")));
|
|
},
|
|
invocation: () => {
|
|
throw new Error("invocation is not reached");
|
|
},
|
|
});
|
|
return { result, commands };
|
|
};
|
|
|
|
describe("Mach-O tool architecture selection", () => {
|
|
it("rejects a CPU lipo did not list instead of reading another slice", async () => {
|
|
const { result, commands } = await inspect("architecture arm64\n", "x86");
|
|
expect(result.ok).toBe(false);
|
|
if (result.ok) return;
|
|
expect(result.error).toMatchObject({
|
|
_tag: "AnalysisOutputError",
|
|
reason: "lipo did not list i386; refusing to read another slice",
|
|
});
|
|
expect(commands.map(({ tool }) => tool)).toEqual(["file", "lipo"]);
|
|
});
|
|
|
|
it("passes the arm64e slice lipo listed for an arm64 target", async () => {
|
|
const { commands } = await inspect("architecture arm64e\n", "arm64");
|
|
const otool = commands.find(({ tool }) => tool === "otool");
|
|
expect(otool?.args).toEqual([
|
|
"-h",
|
|
"-l",
|
|
"-arch",
|
|
"arm64e",
|
|
"/fixture/app",
|
|
]);
|
|
});
|
|
});
|