peachPayload.binaryreader-bounds.test.ts

2.8 KB · raw

1// PCH-02-008 regression tests — BinaryReader must bound every read and fail2// with the decoder's existing PAYLOAD_TOO_SHORT code on truncated input.3//4// DESTINATION: src/crypto/peachPayload.binaryreader-bounds.test.ts5// Requires the `export class BinaryReader` seam from the PCH-02-008 patch.6// Imports are written in destination form; this file is excluded from the7// suite while it lives under src/drafts/.89import { describe, expect, it } from "vitest";10import { BinaryReader } from "./peachPayload";1112describe("PCH-02-008 — BinaryReader bounds checks", () => {13  it("u8 past end of buffer throws PAYLOAD_TOO_SHORT", () => {14    const r = new BinaryReader(Uint8Array.of(0xaa), 0);15    expect(r.u8()).toBe(0xaa);16    expect(() => r.u8()).toThrow("PAYLOAD_TOO_SHORT");17  });1819  it("u16 with only one byte remaining throws PAYLOAD_TOO_SHORT", () => {20    const r = new BinaryReader(Uint8Array.of(0x01), 0);21    expect(() => r.u16()).toThrow("PAYLOAD_TOO_SHORT");22  });2324  it("u32 with three bytes remaining throws PAYLOAD_TOO_SHORT", () => {25    const r = new BinaryReader(Uint8Array.of(0x01, 0x02, 0x03), 0);26    expect(() => r.u32()).toThrow("PAYLOAD_TOO_SHORT");27  });2829  it("read(n) past end throws instead of clamping short", () => {30    const r = new BinaryReader(Uint8Array.of(1, 2, 3, 4), 0);31    expect(() => r.read(5)).toThrow("PAYLOAD_TOO_SHORT");32  });3334  it("lpString whose declared length overruns the buffer throws", () => {35    // length prefix says 200 bytes; only 2 follow.36    const r = new BinaryReader(Uint8Array.of(200, 0x61, 0x62), 0);37    expect(() => r.lpString(1)).toThrow("PAYLOAD_TOO_SHORT");38  });3940  it("valid reads at exact buffer boundaries still work (regression)", () => {41    const buf = new Uint8Array(11);42    const view = new DataView(buf.buffer);43    buf[0] = 0x7f;44    view.setUint16(1, 0xbeef, true);45    view.setUint32(3, 0xdeadbeef, true);46    buf.set([0x70, 0x65, 0x61, 0x63], 7); // "peac"4748    const r = new BinaryReader(buf, 0);49    expect(r.u8()).toBe(0x7f);50    expect(r.u16()).toBe(0xbeef);51    expect(r.u32()).toBe(0xdeadbeef);52    expect(Array.from(r.read(4))).toEqual([0x70, 0x65, 0x61, 0x63]);53    // Buffer fully consumed; the very next read must throw.54    expect(() => r.u8()).toThrow("PAYLOAD_TOO_SHORT");55  });5657  it("lpString round-trips a valid length-prefixed string (regression)", () => {58    const text = new TextEncoder().encode("peach");59    const buf = new Uint8Array(1 + text.length);60    buf[0] = text.length;61    buf.set(text, 1);6263    const r = new BinaryReader(buf, 0);64    expect(r.lpString(1)).toBe("peach");65    expect(r.cursor).toBe(buf.length);66  });6768  it("respects a non-zero start offset and bounds from it", () => {69    const buf = Uint8Array.of(0xff, 0xff, 0x2a);70    const r = new BinaryReader(buf, 2);71    expect(r.u8()).toBe(0x2a);72    expect(() => r.u8()).toThrow("PAYLOAD_TOO_SHORT");73  });74});