fix(worker): read7zNumber throws on first-byte buffer overrun

Prevent silent masking of short reads by validating buffer bounds before
accessing the first byte. Continuation-byte overflow was already caught,
but a short read that leaves pos at/past buffer.length would return {0, pos+1}
instead of throwing, masking the error from callers' try/catch handlers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 11:55:59 +02:00
co-authored by Claude Opus 4.8
parent 086f58f9dd
commit 1e11dd3fd8
2 changed files with 5 additions and 0 deletions
@@ -76,6 +76,10 @@ describe("read7zNumber", () => {
// 500 = 0x01F4 -> first 0x81, trailing 0xF4 // 500 = 0x01F4 -> first 0x81, trailing 0xF4
expect(read7zNumber(Buffer.from([0x81, 0xf4]), 0)).toEqual({ value: 500, next: 2 }); expect(read7zNumber(Buffer.from([0x81, 0xf4]), 0)).toEqual({ value: 500, next: 2 });
}); });
it("throws when pos starts past the buffer end (short read)", () => {
expect(() => read7zNumber(Buffer.from([0x2a]), 5)).toThrow(RangeError);
expect(() => read7zNumber(Buffer.alloc(0), 0)).toThrow(RangeError);
});
}); });
describe("locate7zEncodedHeaderPack", () => { describe("locate7zEncodedHeaderPack", () => {
@@ -16,6 +16,7 @@ const K_SIZE = 0x09;
/** Read a 7z variable-length number: first byte is a length mask, followed by /** Read a 7z variable-length number: first byte is a length mask, followed by
* little-endian bytes. Math.pow keeps values exact above 2^31. */ * little-endian bytes. Math.pow keeps values exact above 2^31. */
export function read7zNumber(buf: Buffer, pos: number): { value: number; next: number } { export function read7zNumber(buf: Buffer, pos: number): { value: number; next: number } {
if (pos >= buf.length) throw new RangeError("7z number reads past buffer end");
const first = buf[pos]; const first = buf[pos];
let mask = 0x80; let mask = 0x80;
let value = 0; let value = 0;