mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 07:16:13 +00:00
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:
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);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user