From 2b52ffdc8b6bd19d74209f685fd919c9288d83db Mon Sep 17 00:00:00 2001 From: dylayed Date: Tue, 23 Jun 2026 22:19:55 +0000 Subject: [PATCH] fix: decode only the viewed bytes of Uint8Array log data When execution-id logging is enabled, stdout/stderr writes are intercepted and re-decoded to attach execution context. The Uint8Array branch decoded `data.buffer`, which is the entire backing ArrayBuffer and ignores the view's `byteOffset`/`byteLength`. Node.js allocates most Buffers from a shared internal pool, so a Buffer written to stdout is frequently a view with a non-zero byteOffset into a much larger ArrayBuffer. Decoding `data.buffer` therefore captured unrelated pool bytes surrounding the real message, producing corrupted log output (e.g. logging the whole 8 KiB pool instead of "hello world"). Decode `Buffer.from(data.buffer, data.byteOffset, data.byteLength)` so only the bytes the view spans are read. Added a regression test that logs through a pooled, non-zero-offset view. --- src/logger.ts | 10 +++++++++- test/logger.ts | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/logger.ts b/src/logger.ts index d4be5d705..515fa18ed 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -163,7 +163,15 @@ function processData(data: Uint8Array | string, encoding?: BufferEncoding) { let decodedData; try { if (data instanceof Uint8Array) { - decodedData = Buffer.from(data.buffer).toString(); + // Decode only the bytes the view actually spans. A Uint8Array (e.g. a + // Buffer carved from Node's shared pool) is often a view into a larger + // ArrayBuffer, so reading `data.buffer` directly would include unrelated + // bytes outside the view's byteOffset/byteLength range. + decodedData = Buffer.from( + data.buffer, + data.byteOffset, + data.byteLength, + ).toString(); } else { decodedData = Buffer.from(data, encoding).toString(); } diff --git a/test/logger.ts b/test/logger.ts index 86a7d0edb..4b9f3f062 100644 --- a/test/logger.ts +++ b/test/logger.ts @@ -147,6 +147,18 @@ describe('getModifiedData', () => { assert.equal(modifiedData, expectedTextOutput); }); + it('uint8Array that is a view into a larger (pooled) buffer', () => { + // Node.js allocates most Buffers from a shared internal pool, so a Buffer + // written to stdout/stderr is frequently a view with a non-zero byteOffset + // into a much larger ArrayBuffer. The logger must honor the view's + // byteOffset/byteLength rather than decoding the entire backing buffer. + const pool = Buffer.alloc(64, 0x2e /* '.' */); + const view = pool.subarray(8, 8 + sampleText.length); + view.write(sampleText); + const modifiedData = getModifiedData(view); + assert.equal(modifiedData, expectedTextOutput); + }); + it('simple text with error', () => { const modifiedData = getModifiedData(sampleText, undefined, true); const expectedOutput =