From 1b73b5ab4bc75bd424e643de8e49f142ffa2b336 Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:41:03 -0700 Subject: [PATCH] fix(native): refuse to list architectures of non-Mach-O targets (#1325) list_architectures ran lipo on every admitted target. For a PE DLL, ELF binary or plist, lipo's refusal surfaced as execution_failure with "Retry once; if it continues, run rea doctor", which neither can fix. Refuse non-Mach-O targets as capability_unavailable before running lipo, as inspect_macho already does. Co-authored-by: Claude Opus 5.5 --- src/native/NativeMacOSProvider.ts | 10 ++++++++ .../providers/native/nativeProvider.test.ts | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/native/NativeMacOSProvider.ts b/src/native/NativeMacOSProvider.ts index c73cd7605..0d25f969c 100644 --- a/src/native/NativeMacOSProvider.ts +++ b/src/native/NativeMacOSProvider.ts @@ -283,6 +283,16 @@ class NativeMacOSClient implements AnalysisClient { async #listArchitectures( signal?: AbortSignal, ): Promise> { + // lipo reads only Mach-O; its refusal of a PE, ELF, or plist target is not + // a tool failure that a retry or `rea doctor` could repair. + if (this.target.format !== "mach-o") + return err( + new AnalysisCapabilityUnavailableError( + IDENTITY.id, + "list_architectures", + "Active artifact is not Mach-O.", + ), + ); const capture = await this.#run( "list_architectures", "lipo", diff --git a/tests/boundary/providers/native/nativeProvider.test.ts b/tests/boundary/providers/native/nativeProvider.test.ts index 7653d871c..d5be920ba 100644 --- a/tests/boundary/providers/native/nativeProvider.test.ts +++ b/tests/boundary/providers/native/nativeProvider.test.ts @@ -694,6 +694,31 @@ describe("native macOS provider failures and parsing", () => { } }); + it.each([ + { + ...machoTarget("/fixture.dll"), + format: "pe" as const, + executableRole: "shared-library" as const, + managed: false, + }, + { ...machoTarget("/fixture.elf"), format: "elf" as const }, + ])( + "refuses to list architectures of a $format target without running lipo", + async (target) => { + const runner = new CountingRunner(); + const result = await new NativeMacOSProvider({}, runner, "darwin") + .createClient(target) + .execute("list_architectures", {}); + if (result.ok) throw new Error("Expected a non-Mach-O refusal"); + expect(result.error._tag).toBe("AnalysisCapabilityUnavailableError"); + expect(projectAnalysisError(result.error)).toMatchObject({ + code: "capability_unavailable", + details: { reason: "Active artifact is not Mach-O." }, + }); + expect(runner.calls).toBe(0); + }, + ); + it("classifies pre-aborted requests before operation or runner discovery", async () => { const controller = new AbortController(); controller.abort();