diff --git a/src/Decoder.ts b/src/Decoder.ts index 5b3c884..20a9064 100644 --- a/src/Decoder.ts +++ b/src/Decoder.ts @@ -451,6 +451,10 @@ export class Decoder { } this.totalPos += this.pos; } + + if (arrayItemsLeft > 0 || this.headByte !== HEAD_BYTE_REQUIRED || this.stack.length > 0) { + throw new RangeError("Insufficient data in stream"); + } } finally { this.entered = false; } diff --git a/test/decodeArrayStream.test.ts b/test/decodeArrayStream.test.ts index a8f7ed5..5f4cc44 100644 --- a/test/decodeArrayStream.test.ts +++ b/test/decodeArrayStream.test.ts @@ -88,4 +88,24 @@ describe("decodeArrayStream", () => { } }, /.*Unrecognized array type byte:.*/i); }); + for (const missingItem of [false, true]) { + it(`rejects an array stream ending ${missingItem ? "before" : "inside"} its last item`, async () => { + const first = encode({ id: 1 }); + const second = encode({ label: "unfinished" }); + const createTruncatedStream = async function* () { + yield Uint8Array.of(0x92); + yield first; + if (!missingItem) { + yield second.subarray(0, second.length - 1); + } + }; + const items: Array = []; + await assert.rejects(async () => { + for await (const item of decodeArrayStream(createTruncatedStream())) { + items.push(item); + } + }, RangeError); + assert.deepStrictEqual(items, [{ id: 1 }]); + }); + } }); diff --git a/test/decodeMultiStream.test.ts b/test/decodeMultiStream.test.ts index 4da31e5..88d98a7 100644 --- a/test/decodeMultiStream.test.ts +++ b/test/decodeMultiStream.test.ts @@ -57,4 +57,20 @@ describe("decodeStream", () => { assert.deepStrictEqual(result, items); }); + for (const finalItem of ["unfinished", { nested: [1, 2] }]) { + it(`rejects a stream ending inside ${typeof finalItem}`, async () => { + const encoded = encode(finalItem); + const createTruncatedStream = async function* () { + yield encode({ id: 1 }); + yield encoded.subarray(0, encoded.length - 1); + }; + const items: Array = []; + await assert.rejects(async () => { + for await (const item of decodeMultiStream(createTruncatedStream())) { + items.push(item); + } + }, RangeError); + assert.deepStrictEqual(items, [{ id: 1 }]); + }); + } });