fix(formats): preserve high bits in Windows signature checks

Decode MZ and Windows signature bytes without ASCII high-bit masking. Move malformed signature cases into the shared Go diagnostics fixtures so provider, compiled CLI, and MCP workflows cover NE, LE, LX, and PE lookalikes; remove the superseded provider-only cases.

Validation: runtime build, check:ci, and focused DOS/Go/target/CLI/MCP tests. Windows-only reference-import cases await CI.
This commit is contained in:
morluto 2026-10-13 04:24:29 +08:00
parent fd9588089d
commit ac57c88efe
4 changed files with 31 additions and 28 deletions

View File

@ -170,18 +170,26 @@ describe("MZ Windows header observations", () => {
});
});
it("keeps a complete non-Windows signature candidate as DOS", () => {
const bytes = dosMz(64);
const offset = 32;
bytes.writeUInt32LE(offset, 60);
bytes.write("PEZZ", offset, "ascii");
it.each(["PEZZ", "NE", "LE", "LX", "PE"])(
"keeps a non-Windows %s signature candidate as DOS",
(signature) => {
const bytes = dosMz(64);
const offset = 32;
bytes.writeUInt32LE(offset, 60);
const candidate = Buffer.from(
[...Buffer.from(signature)].map((byte) =>
signature === "PEZZ" ? byte : byte | 0x80,
),
);
bytes.set(candidate, offset);
expect(observeMzWindowsHeader(bytes)).toEqual({ state: "absent" });
expect(parseExecutableHeader(bytes, "x64")).toMatchObject({
ok: true,
value: { format: "dos-mz", architecture: "x86" },
});
});
expect(observeMzWindowsHeader(bytes)).toEqual({ state: "absent" });
expect(parseExecutableHeader(bytes, "x64")).toMatchObject({
ok: true,
value: { format: "dos-mz", architecture: "x86" },
});
},
);
it("accepts a short-header candidate only when it points at a Windows signature", () => {
const stub = dosMz(64);

View File

@ -62,7 +62,7 @@ export const observeMzWindowsHeader = (
const observedPePrefix =
available !== undefined &&
available.length >= 2 &&
available.toString("ascii", 0, 2) === "PE";
available.toString("latin1", 0, 2) === "PE";
if (fileSize - offset < (observedPePrefix ? 4 : 2))
return { state: "absent" };
return { state: "signature-needed", offset };
@ -72,7 +72,7 @@ const windowsHeaderSignature = (
bytes: Buffer | undefined,
): "PE" | "NE" | "LE" | "LX" | "other" | "unobserved" => {
if (bytes === undefined || bytes.length < 2) return "unobserved";
const prefix = bytes.toString("ascii", 0, 2);
const prefix = bytes.toString("latin1", 0, 2);
if (prefix === "NE" || prefix === "LE" || prefix === "LX") return prefix;
if (prefix !== "PE") return "other";
if (bytes.length < 4) return "unobserved";
@ -91,7 +91,7 @@ export const parseDosMzHeader = (
fileSize = bytes.length,
signatureAtOffset?: Buffer,
): Result<DosMzHeader, string> => {
if (bytes.length < 28 || bytes.toString("ascii", 0, 2) !== "MZ")
if (bytes.length < 28 || bytes.toString("latin1", 0, 2) !== "MZ")
return err("invalid or truncated DOS MZ header");
if (!Number.isSafeInteger(fileSize) || fileSize < bytes.length)
return err("invalid DOS MZ file length");

View File

@ -49,20 +49,6 @@ it.each([32, 64] as const)(
},
);
it.each([{ signature: [0xce, 0xc5] }, { signature: [0xcc, 0xd8] }])(
"rejects high-bit lookalikes of Windows signatures %j",
async ({ signature }) => {
const root = await createTestTempDirectory("rea-go-pe-signature-");
const fixture = createGoBinaryFixture({ format: "pe" });
fixture.bytes.set(signature, 128);
const path = join(root, "corrupt-signature.exe");
await writeFile(path, fixture.bytes);
const result = await new GoBinaryProvider().inspect({ path });
expect(result.ok).toBe(false);
if (!result.ok) expect(result.error).toBeInstanceOf(AnalysisInputError);
},
);
it("preserves the actual build-info byte guard as a resource failure", async () => {
const fixture = createGoBinaryFixture();
fixture.bytes.set([0x81, 0x80, 0x40], fixture.headerOffset + 32);

View File

@ -203,6 +203,14 @@ export const createGoBinaryDiagnosticFixtures = () => {
}
const damagedPe = createGoBinaryFixture({ format: "pe" }).bytes;
damagedPe.write("NO", 128, "ascii");
const highBitSignatures = ["NE", "LE", "LX", "PE"].map((signature) => {
const bytes = createGoBinaryFixture({ format: "pe" }).bytes;
bytes.set(
[...Buffer.from(signature)].map((byte) => byte | 0x80),
128,
);
return { name: `high-bit-${signature.toLowerCase()}`, bytes };
});
const absentDirectories = createGoBinaryFixture({ format: "pe" }).bytes;
absentDirectories.writeUInt32LE(1, 152 + 108);
const shortOptionalHeaders = [0, 1].map((size) => {
@ -220,6 +228,7 @@ export const createGoBinaryDiagnosticFixtures = () => {
})),
...[
{ name: "damaged-pe", bytes: damagedPe },
...highBitSignatures,
{ name: "absent-directories", bytes: absentDirectories },
...shortOptionalHeaders,
].map((fixture) => ({