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:
parent
43738bec57
commit
8e6f37e2a0
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue