From 1e11dd3fd8b11ccac485ebe819ce96c8ab2a980d Mon Sep 17 00:00:00 2001 From: xCyanGrizzly Date: Mon, 27 Jul 2026 11:55:59 +0200 Subject: [PATCH] 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) --- worker/src/archive/ranged/sevenz-ranged.test.ts | 4 ++++ worker/src/archive/ranged/sevenz-ranged.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/worker/src/archive/ranged/sevenz-ranged.test.ts b/worker/src/archive/ranged/sevenz-ranged.test.ts index 692c440..b489f32 100644 --- a/worker/src/archive/ranged/sevenz-ranged.test.ts +++ b/worker/src/archive/ranged/sevenz-ranged.test.ts @@ -76,6 +76,10 @@ describe("read7zNumber", () => { // 500 = 0x01F4 -> first 0x81, trailing 0xF4 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", () => { diff --git a/worker/src/archive/ranged/sevenz-ranged.ts b/worker/src/archive/ranged/sevenz-ranged.ts index f27de73..0ca9d7a 100644 --- a/worker/src/archive/ranged/sevenz-ranged.ts +++ b/worker/src/archive/ranged/sevenz-ranged.ts @@ -16,6 +16,7 @@ const K_SIZE = 0x09; /** 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. */ 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]; let mask = 0x80; let value = 0;