From 672bdea21fd943688e081d209a993e234c45507f Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Mon, 12 Oct 2026 04:09:53 -0700 Subject: [PATCH] fix(target): recognize binary plists by magic under any extension (#2133) Target resolution admitted a property list only when its name ended in .plist, so keyed archives with other extensions (for example .archive) were rejected as an unsupported binary format before any plist tool ran. Classify bplist00 bytes as a plist, as the artifact inventory already does. Fixes #2129 Co-authored-by: Claude Opus 5.5 --- src/application/BinaryTargetResolver.ts | 3 +++ tests/boundary/filesystem/binaryTarget.test.ts | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/application/BinaryTargetResolver.ts b/src/application/BinaryTargetResolver.ts index 7dfb7ce76e..cbccb04189 100644 --- a/src/application/BinaryTargetResolver.ts +++ b/src/application/BinaryTargetResolver.ts @@ -294,6 +294,9 @@ const detectArtifactFormat = async ( if (lower.endsWith(".app")) return "app-pack"; return zipPackageFormatForPath(lower) ?? "zip"; } + // Binary plists, including keyed archives, often use other extensions. + if (observed.bytesRead === 8 && magic.equals(Buffer.from("bplist00"))) + return "plist"; const named = namedArtifactFormat(lower); if (named !== undefined) return named; if ( diff --git a/tests/boundary/filesystem/binaryTarget.test.ts b/tests/boundary/filesystem/binaryTarget.test.ts index 3ab9bdd27e..0aae910e85 100644 --- a/tests/boundary/filesystem/binaryTarget.test.ts +++ b/tests/boundary/filesystem/binaryTarget.test.ts @@ -245,6 +245,17 @@ describe("binary target I/O: package classification", () => { }); }); + it("classifies a binary plist by its magic under any extension", async () => { + const directory = await createTestTempDirectory("rea-bplist-target-"); + const path = join(directory, "Model.archive"); + await writeFile(path, buildBinary({ $archiver: "NSKeyedArchiver" })); + const result = await parseBinaryTarget(path); + expect(result.ok && result.value).toMatchObject({ + kind: "artifact", + format: "plist", + }); + }); + it("does not trust a ZIP-family extension without ZIP magic", async () => { const directory = await createTestTempDirectory("rea-fake-archive-"); const path = join(directory, "fake.zip");