diff --git a/src/core.ts b/src/core.ts index 39c320149f..401e5da18b 100644 --- a/src/core.ts +++ b/src/core.ts @@ -938,7 +938,14 @@ export class ProcessOutput extends Error { } buffer(): Buffer { - return Buffer.from(this.stdall) + // Reconstruct from the raw store chunks. `this.stdall` is a UTF-8 decoded + // string, so building a Buffer from it corrupts any non-UTF-8 output + // (each invalid byte becomes U+FFFD). The original chunks are lossless. + return Buffer.concat( + [...this._dto.store.stdall].map((chunk) => + isString(chunk) ? Buffer.from(chunk) : chunk + ) + ) } blob(type = 'text/plain'): Blob { diff --git a/test/core.test.js b/test/core.test.js index b80a2eda91..0ffcca45b0 100644 --- a/test/core.test.js +++ b/test/core.test.js @@ -1526,6 +1526,15 @@ describe('core', () => { assert.equal(o.buffer().compare(Buffer.from('foo\n', 'utf-8')), 0) }) + test('buffer() preserves non-utf8 bytes', async () => { + const bytes = Buffer.from([0xff, 0xfe, 0x81, 0x82]) // invalid utf-8 + const o = new ProcessOutput({ + store: { stdall: [bytes], stdout: [bytes], stderr: [] }, + }) + assert.equal(o.buffer().toString('hex'), 'fffe8182') + assert.equal(o.text('hex'), 'fffe8182') + }) + test('blob()', async () => { const o = new ProcessOutput(null, null, '', '', 'foo\n') assert.equal(await o.blob().text(), 'foo\n')