mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 22:06:10 +00:00
fix(managed): classify truncated #US and #Blob length prefixes by item
A two- or four-byte compressed-length prefix cut off by the end of the artifact went through the generic range check and was reported as invalid-row. The prefix reader now uses the item's own code for both reserved and truncated prefixes: invalid-string for #US, invalid-blob for #Blob. The unused generic range helper is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f05157562
commit
1c20e2f511
2 files changed
+41
-21
No files matched your search
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { readMetadataBlob } from "./ManagedMetadataHeaps.js";
|
||||
import {
|
||||
readMetadataBlob,
|
||||
readMetadataUserString,
|
||||
} from "./ManagedMetadataHeaps.js";
|
||||
import type { ManagedMetadataLayout } from "./ManagedMetadataLayout.js";
|
||||
|
||||
describe("managed metadata heaps", () => {
|
||||
@@ -31,4 +34,26 @@ describe("managed metadata heaps", () => {
|
||||
itemLength,
|
||||
);
|
||||
});
|
||||
|
||||
it.each([[[0x80]], [[0xc0, 0x00, 0x00]]])(
|
||||
"reports a #US length prefix cut off by the end of the artifact as a string failure",
|
||||
(prefix) => {
|
||||
// #US ends at the end of the artifact, inside a multi-byte length prefix.
|
||||
const bytes = Buffer.from([0x00, ...prefix]);
|
||||
const layout = {
|
||||
userStrings: { name: "#US", offset: 0, size: bytes.length },
|
||||
} as unknown as ManagedMetadataLayout;
|
||||
|
||||
expect(() =>
|
||||
readMetadataUserString(bytes, layout, "0x70000001"),
|
||||
).toThrowError(
|
||||
expect.objectContaining({
|
||||
issue: expect.objectContaining({
|
||||
code: "invalid-string",
|
||||
scope: "metadata.#US:0x70000001",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -8,21 +8,6 @@ import {
|
||||
} from "./ManagedMetadataLayout.js";
|
||||
import { managedFailure } from "./ManagedReaderFailure.js";
|
||||
|
||||
const requireRange = (
|
||||
bytes: Buffer,
|
||||
offset: number,
|
||||
length: number,
|
||||
scope: string,
|
||||
): void => {
|
||||
if (offset < 0 || length < 0 || offset > bytes.length - length)
|
||||
throw managedFailure(
|
||||
"invalid-row",
|
||||
scope,
|
||||
`${scope} leaves the artifact byte range`,
|
||||
offset >= 0 ? offset : null,
|
||||
);
|
||||
};
|
||||
|
||||
/** Cursor for checked fixed-width metadata row decoding. */
|
||||
export class MetadataRowCursor {
|
||||
#offset: number;
|
||||
@@ -81,18 +66,28 @@ const compressedLength = (
|
||||
scope: string,
|
||||
item: keyof typeof PREFIXED_ITEMS,
|
||||
): { readonly length: number; readonly prefix: number } => {
|
||||
requireRange(bytes, offset, 1, scope);
|
||||
const { code, label } = PREFIXED_ITEMS[item];
|
||||
const requirePrefix = (size: number): void => {
|
||||
if (offset < 0 || offset > bytes.length - size)
|
||||
throw managedFailure(
|
||||
code,
|
||||
scope,
|
||||
`${label} length prefix leaves the artifact byte range`,
|
||||
offset >= 0 ? offset : null,
|
||||
);
|
||||
};
|
||||
requirePrefix(1);
|
||||
const first = bytes[offset] ?? 0;
|
||||
if ((first & 0x80) === 0) return { length: first, prefix: 1 };
|
||||
if ((first & 0xc0) === 0x80) {
|
||||
requireRange(bytes, offset, 2, scope);
|
||||
requirePrefix(2);
|
||||
return {
|
||||
length: ((first & 0x3f) << 8) | (bytes[offset + 1] ?? 0),
|
||||
prefix: 2,
|
||||
};
|
||||
}
|
||||
if ((first & 0xe0) === 0xc0) {
|
||||
requireRange(bytes, offset, 4, scope);
|
||||
requirePrefix(4);
|
||||
return {
|
||||
length:
|
||||
(first & 0x1f) * 0x01_00_00_00 +
|
||||
@@ -103,9 +98,9 @@ const compressedLength = (
|
||||
};
|
||||
}
|
||||
throw managedFailure(
|
||||
PREFIXED_ITEMS[item].code,
|
||||
code,
|
||||
scope,
|
||||
`${PREFIXED_ITEMS[item].label} uses a reserved compressed-length prefix`,
|
||||
`${label} uses a reserved compressed-length prefix`,
|
||||
offset,
|
||||
);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user