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 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-09 09:41:03 -07:00 committed by GitHub
parent 56598beb4e
commit 1b73b5ab4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 0 deletions

View File

@ -283,6 +283,16 @@ class NativeMacOSClient implements AnalysisClient {
async #listArchitectures(
signal?: AbortSignal,
): Promise<Result<NativeObservation, AnalysisError>> {
// 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",

View File

@ -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();