From c20b70768050d85cbcce9a5e293cd307341bb117 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:48:07 +0800 Subject: [PATCH] fix(native): preserve negative Mach-O admission identity --- docs/native-investigation.md | 7 +++++++ src/artifacts/apple/machoContainer.test.ts | 4 ++-- src/domain/binaryTarget.ts | 3 +-- .../boundary/filesystem/binaryTarget.test.ts | 21 +++++++++++++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/native-investigation.md b/docs/native-investigation.md index 8f11bcbdb1..100e34494d 100644 --- a/docs/native-investigation.md +++ b/docs/native-investigation.md @@ -346,6 +346,13 @@ Mach-O fixtures check each case against source-owned return values. ## Static inspection +Native target admission checks Mach-O slice headers before assigning FAT +identity. A shared `CAFEBABE` prefix whose declarations do not identify Mach-O +images receives the generic unsupported-format refusal, including JVM class +files larger than the bounded admission probe. This does not identify a partial +prefix as a validated JVM class. Malformed recognized FAT declarations retain +their format-specific reasons. + `inspect_artifact` and `extract_artifact` retain universal Mach-O slices under their exact `lipo` names, including 32-bit ARM implementations such as `armv7`, `armv7s`, and `armv7k`. These names are reconciled with the FAT CPU subtype; diff --git a/src/artifacts/apple/machoContainer.test.ts b/src/artifacts/apple/machoContainer.test.ts index 3f584fa7d6..235ba4d6e7 100644 --- a/src/artifacts/apple/machoContainer.test.ts +++ b/src/artifacts/apple/machoContainer.test.ts @@ -43,7 +43,7 @@ describe("FAT Mach-O identity", () => { }); expect(parseExecutableHeader(Buffer.from(bytes), "arm64")).toEqual({ ok: false, - error: "FAT table does not identify a Mach-O image", + error: "unsupported binary format", }); }); @@ -62,7 +62,7 @@ describe("FAT Mach-O identity", () => { }); expect(parseExecutableHeader(Buffer.from(bytes), "arm64")).toEqual({ ok: false, - error: "FAT table does not identify a Mach-O image", + error: "unsupported binary format", }); }); diff --git a/src/domain/binaryTarget.ts b/src/domain/binaryTarget.ts index b9ff4c9a20..4d34fd9ab0 100644 --- a/src/domain/binaryTarget.ts +++ b/src/domain/binaryTarget.ts @@ -373,8 +373,7 @@ const fatIdentityError = ( }); if (identity.state === "needs-slice-header") return "FAT slice identity is outside the bounded prefix"; - if (identity.state === "not-mach-o") - return "FAT table does not identify a Mach-O image"; + if (identity.state === "not-mach-o") return "unsupported binary format"; if (identity.state === "malformed") return identity.reason; return null; }; diff --git a/tests/boundary/filesystem/binaryTarget.test.ts b/tests/boundary/filesystem/binaryTarget.test.ts index 399985ccc7..3b735d136a 100644 --- a/tests/boundary/filesystem/binaryTarget.test.ts +++ b/tests/boundary/filesystem/binaryTarget.test.ts @@ -31,6 +31,7 @@ import { hasXmlPlistSignature } from "../../../src/domain/apple/appleArchiveForm import { parseExecutableHeader } from "../../../src/domain/binaryTarget.js"; import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; +import { javaClassFile } from "../../fixtures/javaClassFile.js"; import { arArchive, CPU, @@ -38,6 +39,26 @@ import { machoImage, } from "../../../src/artifacts/apple/MachoImage.fixture.js"; +it.each([4000, 9000])( + "refuses a JVM class with %i unused constant bytes without claiming FAT identity", + async (constantBytes) => { + const directory = await createTestTempDirectory("rea-class-target-"); + const path = join(directory, "input.class"); + await writeFile(path, javaClassFile(constantBytes)); + const resolved = await parseBinaryTarget(path); + expect(resolved).toMatchObject({ + ok: false, + error: { reason: "unsupported binary format" }, + }); + if (resolved.ok) throw new Error("JVM class was admitted as executable"); + expect(projectAnalysisError(resolved.error)).toMatchObject({ + code: "target_unavailable", + category: "unsupported_target", + details: { reason: "unsupported binary format" }, + }); + }, +); + it.each([0x01c0, 0x01c2, 0x01c4, 0xaa64])( "retains original PE COFF machine %i through file-backed target resolution", async (peMachine) => {