| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 58d9685 commit 5933516
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -472,6 +472,11 @@ function processChunkSync(self, chunk, flushFlag) { | |||
| 472 | 472 | } | |
| 473 | 473 | } | |
| 474 | 474 | ||
| 475 | + if (availInAfter > 0 && self._rejectGarbageAfterEnd) { | ||
| 476 | + _close(self); | ||
| 477 | + throw new ERR_TRAILING_JUNK_AFTER_STREAM_END(); | ||
| 478 | + } | ||
| 479 | + | ||
| 475 | 480 | self.bytesWritten = inputRead; | |
| 476 | 481 | _close(self); | |
| 477 | 482 | ||
@@ -678,7 +683,8 @@ function Zlib(opts, mode) { | |||
| 678 | 683 | strategy, | |
| 679 | 684 | this._writeState, | |
| 680 | 685 | processCallback, | |
| 681 | - dictionary); | ||
| 686 | + dictionary, | ||
| 687 | + opts?.rejectGarbageAfterEnd === true); | ||
| 682 | 688 | ||
| 683 | 689 | ZlibBase.call(this, opts, mode, handle, zlibDefaultOpts); | |
| 684 | 690 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -196,6 +196,7 @@ class ZlibContext final : public MemoryRetainer { | |||
| 196 | 196 | int window_bits, | |
| 197 | 197 | int mem_level, | |
| 198 | 198 | int strategy, | |
| 199 | + bool reject_garbage_after_end, | ||
| 199 | 200 | std::vector<unsigned char>&& dictionary); | |
| 200 | 201 | CompressionError SetParams(int level, int strategy); | |
| 201 | 202 | ||
@@ -223,6 +224,7 @@ class ZlibContext final : public MemoryRetainer { | |||
| 223 | 224 | node_zlib_mode mode_ = NONE; | |
| 224 | 225 | int strategy_ = 0; | |
| 225 | 226 | int window_bits_ = 0; | |
| 227 | + bool reject_garbage_after_end_ = false; | ||
| 226 | 228 | unsigned int gzip_id_bytes_read_ = 0; | |
| 227 | 229 | std::vector<unsigned char> dictionary_; | |
| 228 | 230 | ||
@@ -749,9 +751,10 @@ class ZlibStream final : public CompressionStream<ZlibContext> { | |||
| 749 | 751 | "a version of npm (> 5.5.1 or < 5.4.0) or node-tar (> 4.0.1) " | |
| 750 | 752 | "that is compatible with Node.js 9 and above.\n"); | |
| 751 | 753 | } | |
| 752 | - CHECK(args.Length() == 7 && | ||
| 753 | - "init(windowBits, level, memLevel, strategy, writeResult, writeCallback," | ||
| 754 | - " dictionary)"); | ||
| 754 | + CHECK((args.Length() == 7 || args.Length() == 8) && | ||
| 755 | + "init(windowBits, level, memLevel, strategy, writeResult, " | ||
| 756 | + "writeCallback," | ||
| 757 | + " dictionary[, rejectGarbageAfterEnd])"); | ||
| 755 | 758 | ||
| 756 | 759 | ZlibStream* wrap; | |
| 757 | 760 | ASSIGN_OR_RETURN_UNWRAP(&wrap, args.This()); | |
@@ -791,10 +794,20 @@ class ZlibStream final : public CompressionStream<ZlibContext> { | |||
| 791 | 794 | data + Buffer::Length(args[6])); | |
| 792 | 795 | } | |
| 793 | 796 | ||
| 797 | + bool reject_garbage_after_end = false; | ||
| 798 | + if (args.Length() == 8) { | ||
| 799 | + CHECK(args[7]->IsBoolean()); | ||
| 800 | + reject_garbage_after_end = args[7]->IsTrue(); | ||
| 801 | + } | ||
| 802 | + | ||
| 794 | 803 | wrap->InitStream(write_result, write_js_callback); | |
| 795 | 804 | ||
| 796 | 805 | AllocScope alloc_scope(wrap); | |
| 797 | - wrap->context()->Init(level, window_bits, mem_level, strategy, | ||
| 806 | + wrap->context()->Init(level, | ||
| 807 | + window_bits, | ||
| 808 | + mem_level, | ||
| 809 | + strategy, | ||
| 810 | + reject_garbage_after_end, | ||
| 798 | 811 | std::move(dictionary)); | |
| 799 | 812 | } | |
| 800 | 813 | ||
@@ -1124,10 +1137,8 @@ void ZlibContext::DoThreadPoolWork() { | |||
| 1124 | 1137 | } | |
| 1125 | 1138 | } | |
| 1126 | 1139 | ||
| 1127 | - while (strm_.avail_in > 0 && | ||
| 1128 | - mode_ == GUNZIP && | ||
| 1129 | - err_ == Z_STREAM_END && | ||
| 1130 | - strm_.next_in[0] != 0x00) { | ||
| 1140 | + while (strm_.avail_in > 0 && mode_ == GUNZIP && err_ == Z_STREAM_END && | ||
| 1141 | + !reject_garbage_after_end_ && strm_.next_in[0] != 0x00) { | ||
| 1131 | 1142 | // Bytes remain in input buffer. Perhaps this is another compressed | |
| 1132 | 1143 | // member in the same archive, or just trailing garbage. | |
| 1133 | 1144 | // Trailing zero bytes are okay, though, since they are frequently | |
@@ -1226,9 +1237,12 @@ CompressionError ZlibContext::ResetStream() { | |||
| 1226 | 1237 | return SetDictionary(); | |
| 1227 | 1238 | } | |
| 1228 | 1239 | ||
| 1229 | - void ZlibContext::Init( | ||
| 1230 | - int level, int window_bits, int mem_level, int strategy, | ||
| 1231 | - std::vector<unsigned char>&& dictionary) { | ||
| 1240 | + void ZlibContext::Init(int level, | ||
| 1241 | + int window_bits, | ||
| 1242 | + int mem_level, | ||
| 1243 | + int strategy, | ||
| 1244 | + bool reject_garbage_after_end, | ||
| 1245 | + std::vector<unsigned char>&& dictionary) { | ||
| 1232 | 1246 | // Set allocation functions | |
| 1233 | 1247 | strm_.zalloc = CompressionStreamMemoryOwner::AllocForZlib; | |
| 1234 | 1248 | strm_.zfree = CompressionStreamMemoryOwner::FreeForZlib; | |
@@ -1259,6 +1273,7 @@ void ZlibContext::Init( | |||
| 1259 | 1273 | window_bits_ = window_bits; | |
| 1260 | 1274 | mem_level_ = mem_level; | |
| 1261 | 1275 | strategy_ = strategy; | |
| 1276 | + reject_garbage_after_end_ = reject_garbage_after_end; | ||
| 1262 | 1277 | ||
| 1263 | 1278 | flush_ = Z_NO_FLUSH; | |
| 1264 | 1279 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,36 +2,67 @@ | |||
| 2 | 2 | require('../common'); | |
| 3 | 3 | const assert = require('assert'); | |
| 4 | 4 | const test = require('node:test'); | |
| 5 | + const zlib = require('zlib'); | ||
| 5 | 6 | const { DecompressionStream } = require('stream/web'); | |
| 6 | 7 | ||
| 7 | - test('DecompressStream deflat emits error on trailing data', async () => { | ||
| 8 | + async function assertDecompressionStreamRejects(format, chunks) { | ||
| 9 | + await assert.rejects( | ||
| 10 | + Array.fromAsync( | ||
| 11 | + new Blob(chunks).stream().pipeThrough(new DecompressionStream(format)) | ||
| 12 | + ), | ||
| 13 | + { name: 'TypeError' }, | ||
| 14 | + ); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + test('DecompressionStream deflate emits TypeError on trailing data', async () => { | ||
| 8 | 18 | const valid = new Uint8Array([120, 156, 75, 4, 0, 0, 98, 0, 98]); // deflate('a') | |
| 9 | 19 | const empty = new Uint8Array(1); | |
| 10 | 20 | const invalid = new Uint8Array([...valid, ...empty]); | |
| 11 | 21 | const double = new Uint8Array([...valid, ...valid]); | |
| 12 | 22 | ||
| 13 | - for (const chunk of [[invalid], [valid, empty], [valid, valid], [valid, double]]) { | ||
| 14 | - await assert.rejects( | ||
| 15 | - Array.fromAsync( | ||
| 16 | - new Blob([chunk]).stream().pipeThrough(new DecompressionStream('deflate')) | ||
| 17 | - ), | ||
| 18 | - { name: 'TypeError' }, | ||
| 19 | - ); | ||
| 23 | + for (const chunks of [[invalid], [valid, empty], [valid, valid], [double]]) { | ||
| 24 | + await assertDecompressionStreamRejects('deflate', chunks); | ||
| 20 | 25 | } | |
| 21 | 26 | }); | |
| 22 | 27 | ||
| 23 | - test('DecompressStream gzip emits error on trailing data', async () => { | ||
| 28 | + test('DecompressionStream gzip emits TypeError on trailing data', async () => { | ||
| 24 | 29 | const valid = new Uint8Array([31, 139, 8, 0, 0, 0, 0, 0, 0, 19, 75, 4, | |
| 25 | 30 | 0, 67, 190, 183, 232, 1, 0, 0, 0]); // gzip('a') | |
| 26 | 31 | const empty = new Uint8Array(1); | |
| 27 | 32 | const invalid = new Uint8Array([...valid, ...empty]); | |
| 28 | 33 | const double = new Uint8Array([...valid, ...valid]); | |
| 29 | - for (const chunk of [[invalid], [valid, empty], [valid, valid], [double]]) { | ||
| 30 | - await assert.rejects( | ||
| 31 | - Array.fromAsync( | ||
| 32 | - new Blob([chunk]).stream().pipeThrough(new DecompressionStream('gzip')) | ||
| 33 | - ), | ||
| 34 | - { name: 'TypeError' }, | ||
| 35 | - ); | ||
| 34 | + for (const chunks of [[invalid], [valid, empty], [valid, valid], [double]]) { | ||
| 35 | + await assertDecompressionStreamRejects('gzip', chunks); | ||
| 36 | + } | ||
| 37 | + }); | ||
| 38 | + | ||
| 39 | + test('DecompressionStream brotli emits TypeError on trailing data', async () => { | ||
| 40 | + const valid = zlib.brotliCompressSync(Buffer.from('a')); | ||
| 41 | + const empty = new Uint8Array(1); | ||
| 42 | + const invalid = new Uint8Array([...valid, ...empty]); | ||
| 43 | + const double = new Uint8Array([...valid, ...valid]); | ||
| 44 | + for (const chunks of [[invalid], [valid, empty], [valid, valid], [double]]) { | ||
| 45 | + await assertDecompressionStreamRejects('brotli', chunks); | ||
| 36 | 46 | } | |
| 37 | 47 | }); | |
| 48 | + | ||
| 49 | + test('zlib sync decompression honors rejectGarbageAfterEnd', () => { | ||
| 50 | + const valid = new Uint8Array([31, 139, 8, 0, 0, 0, 0, 0, 0, 19, 75, 4, | ||
| 51 | + 0, 67, 190, 183, 232, 1, 0, 0, 0]); // gzip('a') | ||
| 52 | + const double = new Uint8Array([...valid, ...valid]); | ||
| 53 | + | ||
| 54 | + assert.deepStrictEqual(zlib.gunzipSync(double), Buffer.from('aa')); | ||
| 55 | + assert.throws( | ||
| 56 | + () => zlib.gunzipSync(double, { rejectGarbageAfterEnd: true }), | ||
| 57 | + { code: 'ERR_TRAILING_JUNK_AFTER_STREAM_END', name: 'TypeError' }, | ||
| 58 | + ); | ||
| 59 | + | ||
| 60 | + const brotli = zlib.brotliCompressSync(Buffer.from('a')); | ||
| 61 | + const brotliDouble = Buffer.concat([brotli, brotli]); | ||
| 62 | + | ||
| 63 | + assert.deepStrictEqual(zlib.brotliDecompressSync(brotliDouble), Buffer.from('a')); | ||
| 64 | + assert.throws( | ||
| 65 | + () => zlib.brotliDecompressSync(brotliDouble, { rejectGarbageAfterEnd: true }), | ||
| 66 | + { code: 'ERR_TRAILING_JUNK_AFTER_STREAM_END', name: 'TypeError' }, | ||
| 67 | + ); | ||
| 68 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments