From 75a4a14fa05d352762ffaefd3cd2e16515842b03 Mon Sep 17 00:00:00 2001 From: xin zhuang <65798732+1052326311@users.noreply.github.com> Date: Mon, 12 Oct 2026 15:18:03 +0800 Subject: [PATCH] fix(artifacts): compare package signatures without ASCII folding (#2069) --- src/application/BinaryTargetResolver.ts | 4 +-- src/artifacts/inventory/classify.ts | 4 +-- .../boundary/filesystem/zipDetection.test.ts | 35 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/application/BinaryTargetResolver.ts b/src/application/BinaryTargetResolver.ts index bced0278a..7dfb7ce76 100644 --- a/src/application/BinaryTargetResolver.ts +++ b/src/application/BinaryTargetResolver.ts @@ -299,7 +299,7 @@ const detectArtifactFormat = async ( if ( lower.endsWith(".pkg") && observed.bytesRead >= 4 && - magic.subarray(0, 4).toString("ascii") === "xar!" + magic.subarray(0, 4).equals(Buffer.from("xar!")) ) return "pkg"; if (lower.endsWith(".dmg")) { @@ -310,7 +310,7 @@ const detectArtifactFormat = async ( const trailer = Buffer.alloc(4); const read = await handle.read(trailer, 0, trailer.length, size - 512); throwIfTargetResolutionCancelled(signal); - if (read.bytesRead === 4 && trailer.toString("ascii") === "koly") + if (read.bytesRead === 4 && trailer.equals(Buffer.from("koly"))) return "dmg"; } } diff --git a/src/artifacts/inventory/classify.ts b/src/artifacts/inventory/classify.ts index 9dd050c8e..6322d6028 100644 --- a/src/artifacts/inventory/classify.ts +++ b/src/artifacts/inventory/classify.ts @@ -152,7 +152,7 @@ const classifyRootFormat = async ( if (hasZipSignature(prefix)) return zipRootPackageFormat(lower); if ( lower.endsWith(".pkg") && - prefix.subarray(0, 4).toString("ascii") === "xar!" + prefix.subarray(0, 4).equals(Buffer.from("xar!")) ) return "pkg"; if (lower.endsWith(".dmg") && (await hasKolyTrailer(handle))) return "dmg"; @@ -172,5 +172,5 @@ const hasKolyTrailer = async (handle: FileHandle): Promise => { if (size < 512) return false; const trailer = Buffer.alloc(4); const read = await handle.read(trailer, 0, trailer.length, size - 512); - return read.bytesRead === 4 && trailer.toString("ascii") === "koly"; + return read.bytesRead === 4 && trailer.equals(Buffer.from("koly")); }; diff --git a/tests/boundary/filesystem/zipDetection.test.ts b/tests/boundary/filesystem/zipDetection.test.ts index 08c318257..b403c5975 100644 --- a/tests/boundary/filesystem/zipDetection.test.ts +++ b/tests/boundary/filesystem/zipDetection.test.ts @@ -199,3 +199,38 @@ describe("nested container family", () => { }, ); }); + +describe("package signature byte identity", () => { + it.each([ + { extension: "pkg", signature: "xar!", offset: 0, size: 4 }, + { extension: "dmg", signature: "koly", offset: 0, size: 512 }, + ])( + "rejects high-bit aliases of $extension magic at both admission boundaries", + async ({ extension, signature, offset, size }) => { + const root = await createTestTempDirectory("rea-package-magic-"); + const path = join(root, `input.${extension}`); + const bytes = Buffer.alloc(size); + bytes.write(signature, offset, "ascii"); + await writeFile(path, bytes); + expect( + (await classifyAndHashRoot(path, false, await lstat(path))).format, + ).toBe(extension); + expect(await parseBinaryTarget(path)).toMatchObject({ + ok: true, + value: { format: extension }, + }); + for (let index = 0; index < signature.length; index += 1) { + const changed = Buffer.from(bytes); + changed[offset + index] = changed[offset + index]! | 0x80; + await writeFile(path, changed); + expect( + (await classifyAndHashRoot(path, false, await lstat(path))).format, + ).toBe("file"); + expect(await parseBinaryTarget(path)).toMatchObject({ + ok: false, + error: { _tag: "BinaryTargetError" }, + }); + } + }, + ); +});