fix(managed): report stream-name errors at file offsets (#4418)

Co-authored-by: Mark <m-user@Marks-MacBook-Air.local>
This commit is contained in:
duckhunt113andMark authored and GitHub committed 2026-10-16 23:56:08 +08:00
1 parent 9a82046b07
commit 2d95b85aa4
2 files changed
+80 -3

No files matched your search

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import { inspectManagedArtifactBytes } from "./ManagedArtifactInspector.js";
import { inspectManagedMembersBytes } from "./ManagedMemberInspector.js";
import { readManagedPeLayout } from "./ManagedPeReader.js";
import { inspectManagedNativeBoundariesBytes } from "./ManagedNativeBoundaryInspector.js";
import {
buildManagedPeFixture,
@@ -88,3 +89,77 @@ describe("managed artifact failure classification", () => {
});
});
});
describe.each([0, 0x200])(
"stream-name file offsets with %i bytes of padding",
(padding) => {
it.each([
{
label: "non-printable name",
value: 0x1f,
length: 1,
detail: "Metadata stream name is not printable ASCII",
},
{
label: "unterminated name",
value: 0x41,
length: 32,
detail: "Metadata stream name is missing a bounded terminator",
},
])(
"reports the artifact location for $label",
({ value, length, detail }) => {
const original = buildManagedPeFixture();
const section =
original.readUInt32LE(0x3c) +
24 +
original.readUInt16LE(original.readUInt32LE(0x3c) + 20);
const rawOffset = original.readUInt32LE(section + 20);
// Relocate the raw section while preserving RVAs and its internal layout.
const bytes = Buffer.concat([
original.subarray(0, rawOffset),
Buffer.alloc(padding),
original.subarray(rawOffset),
]);
bytes.writeUInt32LE(rawOffset + padding, section + 20);
const baseline = inspectManagedArtifactBytes(
bytes,
managedPeFixtureTarget(bytes),
);
expect(baseline.coverage).toEqual({ state: "complete", issues: [] });
const pe = readManagedPeLayout(bytes);
if (pe.cli === null) throw new Error("Fixture omitted CLI metadata");
const root = pe.rvaToOffset(
pe.cli.metadata.rva,
pe.cli.metadata.size,
"cli.metadata",
);
const versionLength = bytes.readUInt32LE(root + 12);
const nameOffset =
root + Math.ceil((16 + versionLength) / 4) * 4 + 4 + 8;
bytes.fill(value, nameOffset, nameOffset + length);
const target = managedPeFixtureTarget(bytes);
const artifact = inspectManagedArtifactBytes(bytes, target);
const members = inspectManagedMembersBytes(bytes, target);
expect(artifact.classification.status).toBe("malformed");
expect(artifact.coverage.issues).toEqual([
expect.objectContaining({
code: "invalid-stream",
scope: "metadata.stream-name",
detail,
offset: nameOffset,
}),
]);
expect(artifact.classification.evidence).toContainEqual(
expect.objectContaining({
code: "metadata-not-admitted",
file_offset: nameOffset,
}),
);
expect(members.coverage.issues).toEqual(artifact.coverage.issues);
},
);
},
);
+5 -3
View File
@@ -141,8 +141,10 @@ const requireRange = (
const align4 = (value: number): number => Math.ceil(value / 4) * 4;
/** Read within the metadata root; report failures at artifact file offsets. */
const readStreamName = (
bytes: Buffer,
rootOffset: number,
offset: number,
): { readonly name: string; readonly next: number } => {
const endLimit = Math.min(bytes.length, offset + 32);
@@ -153,7 +155,7 @@ const readStreamName = (
"invalid-stream",
"metadata.stream-name",
"Metadata stream name is missing a bounded terminator",
offset,
rootOffset + offset,
);
const nameBytes = bytes.subarray(offset, end);
if (nameBytes.some((byte) => byte < 0x20 || byte > 0x7e))
@@ -161,7 +163,7 @@ const readStreamName = (
"invalid-stream",
"metadata.stream-name",
"Metadata stream name is not printable ASCII",
offset,
rootOffset + offset,
);
return {
name: nameBytes.toString("ascii"),
@@ -250,7 +252,7 @@ const readMetadataStreams = (
requireRange(bytes, rootOffset, cursor, 8, "metadata.stream-header");
const relativeOffset = bytes.readUInt32LE(cursor);
const streamSize = bytes.readUInt32LE(cursor + 4);
const named = readStreamName(bytes, cursor + 8);
const named = readStreamName(bytes, rootOffset, cursor + 8);
if (streams.has(named.name))
throw managedFailure(
"invalid-stream",