fix(formats): preserve high bits in remaining magic-byte checks (#2384)

* fix(formats): preserve high bits in remaining magic-byte checks

Node's "ascii" decoder masks the high bit, so byte-for-byte signature
comparisons accepted lookalikes such as CD DA ("MZ" | 0x80) or a
high-bit "bplist00"/"NIBArchive" header. ac57c88ef fixed this for the
shared DOS/MZ reader; apply the same latin1 decoding to the remaining
signature checks: NativeAOT and Windows native loader MZ/PE, binary
plist and NIBArchive detection, and PNG IHDR checks.

Display-name decodes (section and metadata stream names) are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* style(windows): format signature admission regressions

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Lucas LSMO 2026-10-13 09:54:37 -03:00 committed by GitHub
parent 43738bec57
commit 8e6f37e2a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 41 additions and 6 deletions

View File

@ -27,9 +27,9 @@ if (
);
const offset = bytes.length >= 64 ? bytes.readUInt32LE(0x3c) : bytes.length;
if (
bytes.toString("ascii", 0, 2) !== "MZ" ||
bytes.toString("latin1", 0, 2) !== "MZ" ||
offset > bytes.length - 26 ||
bytes.toString("ascii", offset, offset + 4) !== "PE\0\0" ||
bytes.toString("latin1", offset, offset + 4) !== "PE\0\0" ||
bytes.readUInt16LE(offset + 4) !== 0x8664 ||
bytes.readUInt16LE(offset + 24) !== 0x20b
)

View File

@ -159,3 +159,22 @@ describe("read-only NativeAOT PE parser", () => {
});
});
});
describe("NativeAOT PE signature admission", () => {
it.each([
["MZ", "Not a PE image"],
["PE", "Invalid PE signature"],
])(
"rejects a high-bit lookalike of the %s signature",
(signature, reason) => {
const bytes = nativeAotPeFixture();
const offset = signature === "MZ" ? 0 : bytes.readUInt32LE(0x3c);
for (const position of [offset, offset + 1])
bytes.writeUInt8(bytes.readUInt8(position) | 0x80, position);
expect(
parseNativeAotPe(bytes, nativeAotPeDigest(bytes), SUMMARY_BUDGET)
.summary,
).toMatchObject({ status: "not_applicable", reason });
},
);
});

View File

@ -384,11 +384,11 @@ class PeImage {
constructor(bytes: Buffer) {
this.#bytes = bytes;
if (bytes.length < 0x40 || bytes.toString("ascii", 0, 2) !== "MZ")
if (bytes.length < 0x40 || bytes.toString("latin1", 0, 2) !== "MZ")
throw new Error("Not a PE image");
const peOffset = this.#u32(0x3c);
this.#range(peOffset, 24);
if (bytes.toString("ascii", peOffset, peOffset + 4) !== "PE\0\0")
if (bytes.toString("latin1", peOffset, peOffset + 4) !== "PE\0\0")
throw new Error("Invalid PE signature");
this.#machine = this.#u16(peOffset + 4);
const count = this.#u16(peOffset + 6);

View File

@ -143,6 +143,22 @@ describe("packaged Windows native admission", () => {
},
);
it.each([
{ signature: "MZ", offset: 0 },
{ signature: "PE", offset: 64 },
])(
"rejects digest-valid high-bit $signature lookalikes before loading",
({ offset }) => {
const value = fixture();
value.bytes[offset] = (value.bytes[offset] ?? 0) | 0x80;
value.manifest.artifactSha256 = createHash("sha256")
.update(value.bytes)
.digest("hex");
expect(loadWindowsNativeAuthority(value.host).available).toBe(false);
expect(value.loads()).toBe(0);
},
);
it("rejects a digest-valid artifact for another machine", () => {
const value = fixture();
value.bytes.writeUInt16LE(0xaa64, 68);

View File

@ -132,12 +132,12 @@ export const loadWindowsNativeAuthority = (
};
const assertWindowsX64Artifact = (bytes: Buffer): void => {
if (bytes.length < 64 || bytes.toString("ascii", 0, 2) !== "MZ")
if (bytes.length < 64 || bytes.toString("latin1", 0, 2) !== "MZ")
throw new Error("Windows native artifact is not a PE image");
const offset = bytes.readUInt32LE(0x3c);
if (
offset > bytes.length - 26 ||
bytes.toString("ascii", offset, offset + 4) !== "PE\0\0" ||
bytes.toString("latin1", offset, offset + 4) !== "PE\0\0" ||
bytes.readUInt16LE(offset + 4) !== 0x8664 ||
bytes.readUInt16LE(offset + 24) !== 0x20b
)