From 6582b2433a955167e33246ad07de1a40bcb20144 Mon Sep 17 00:00:00 2001 From: Kaoru0822 Date: Tue, 6 Oct 2026 05:00:53 -0700 Subject: [PATCH] fix(ghidra): refuse ambiguous universal Mach-O imports --- src/ghidra/GhidraProvider.behavior.test.ts | 27 ++++++++++++++++++++++ src/ghidra/GhidraProvider.ts | 20 ++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/ghidra/GhidraProvider.behavior.test.ts b/src/ghidra/GhidraProvider.behavior.test.ts index 9b38a1ce4..912437770 100644 --- a/src/ghidra/GhidraProvider.behavior.test.ts +++ b/src/ghidra/GhidraProvider.behavior.test.ts @@ -208,6 +208,33 @@ describe("Ghidra provider", () => { }); }); +describe("Ghidra Mach-O slice support", () => { + it("refuses universal targets whose selected slice cannot be enforced", () => { + const ghidra = provider(); + const universal: BinaryTarget = { + path: "/tmp/fixture", + sha256: "a".repeat(64), + kind: "executable", + format: "mach-o", + architecture: "arm64", + availableArchitectures: ["x86_64", "arm64"], + }; + + expect(ghidra.inspectTargetSupport(universal)).toMatchObject({ + status: "unsupported", + code: "architecture_unsupported", + reason: expect.stringContaining("universal Mach-O"), + diagnostics: { + architecture: "arm64", + available_architectures: ["x86_64", "arm64"], + }, + }); + expect( + ghidra.inspectTargetSupport(executableTarget("mach-o", "arm64")), + ).toMatchObject({ status: "supported" }); + }); +}); + describe("Ghidra platform support", () => { it("keeps Windows annotation mutation unavailable independently of native controls", () => { const ghidra = provider({ ...installationHost(), platform: "win32" }); diff --git a/src/ghidra/GhidraProvider.ts b/src/ghidra/GhidraProvider.ts index da34669c3..2cea5684f 100644 --- a/src/ghidra/GhidraProvider.ts +++ b/src/ghidra/GhidraProvider.ts @@ -15,7 +15,11 @@ import { import type { AppConfig } from "../config.js"; import type { AnalysisProfileCommitment } from "../domain/analysisProfile.js"; import type { BinaryTarget } from "../domain/binaryTarget.js"; -import { jsonObjectSchema, jsonValueSchema } from "../domain/jsonValue.js"; +import { + jsonObjectSchema, + jsonValueSchema, + type JsonValue, +} from "../domain/jsonValue.js"; import { nativeLoadImageObservationSchema, nativeLoadImageSchema, @@ -144,6 +148,10 @@ export class GhidraProvider implements AnalysisProviderCandidate { target_kind: target.kind, target_format: target.format, architecture: target.architecture ?? null, + available_architectures: + target.kind === "executable" + ? [...target.availableArchitectures] + : null, executable_role: target.executableRole ?? null, managed: target.managed ?? null, }; @@ -156,6 +164,14 @@ export class GhidraProvider implements AnalysisProviderCandidate { }; if (hostPlatform === "win32") return inspectWindowsP0TargetSupport(target, diagnostics); + if (target.format === "mach-o" && target.availableArchitectures.length > 1) + return { + status: "unsupported", + code: "architecture_unsupported", + reason: + "Ghidra v1 cannot enforce the selected architecture when importing a universal Mach-O; analyze a thinned Mach-O slice instead.", + diagnostics, + }; if (!SUPPORTED_ARCHITECTURES.has(target.architecture)) return { status: "unsupported", @@ -381,7 +397,7 @@ export class GhidraProvider implements AnalysisProviderCandidate { const inspectWindowsP0TargetSupport = ( target: BinaryTarget, - diagnostics: Readonly>, + diagnostics: Readonly>, ): ProviderTargetSupport => { if (target.format !== "pe") return {