From 70611713e140a86201cdef659181012cbdd17f4d Mon Sep 17 00:00:00 2001 From: Faheem Ahmad Date: Tue, 13 Oct 2026 08:37:45 -0700 Subject: [PATCH] fix(targets): compare the Info.plist bplist prefix without ASCII folding (#2100) * fix(targets): compare the Info.plist bplist prefix without ASCII folding The six-byte prefix check used an encoding that maps high-bit bytes onto bplist, so a non-binary property list could be sent to the binary decoder. Co-authored-by: Cursor * fix(targets): satisfy the strict index check in the plist prefix test Co-authored-by: Cursor --------- Co-authored-by: Cursor Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> --- src/application/AppBundleExecutable.test.ts | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/application/AppBundleExecutable.test.ts diff --git a/src/application/AppBundleExecutable.test.ts b/src/application/AppBundleExecutable.test.ts new file mode 100644 index 0000000000..942a001122 --- /dev/null +++ b/src/application/AppBundleExecutable.test.ts @@ -0,0 +1,34 @@ +import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { resolveAppBundleExecutable } from "./AppBundleExecutable.js"; + +describe("app bundle Info.plist magic", () => { + it("does not decode a high-bit fold of bplist as a binary Info.plist", async () => { + const root = await mkdtemp(join(tmpdir(), "rea-bplist-fold-")); + try { + const bundle = join(root, "Fixture.app"); + const plistPath = join(bundle, "Contents", "Info.plist"); + await mkdir(join(bundle, "Contents"), { recursive: true }); + const plist = Buffer.from("bplist00"); + plist[0] = (plist[0] ?? 0) | 0x80; + await writeFile(plistPath, plist); + let decoded = false; + const result = await resolveAppBundleExecutable(bundle, { + readFile: async () => plist, + realpath: async (path) => path, + decodeBinaryPlist: async () => { + decoded = true; + return "Fixture"; + }, + }); + expect(decoded).toBe(false); + expect(result.ok).toBe(false); + } finally { + await rm(root, { recursive: true, force: true }); + } + }); +});