| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5933516 commit 90007a5
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -801,6 +801,9 @@ These advanced options are available for controlling decompression: | |||
| 801 | 801 | <!-- YAML | |
| 802 | 802 | added: v0.11.1 | |
| 803 | 803 | changes: | |
| 804 | + - version: REPLACEME | ||
| 805 | + pr-url: https://github.com/nodejs/node/pull/64023 | ||
| 806 | + description: The `rejectGarbageAfterEnd` option was added. | ||
| 804 | 807 | - version: | |
| 805 | 808 | - v14.5.0 | |
| 806 | 809 | - v12.19.0 | |
@@ -836,6 +839,10 @@ ignored by the decompression classes. | |||
| 836 | 839 | * `info` {boolean} (If `true`, returns an object with `buffer` and `engine`.) | |
| 837 | 840 | * `maxOutputLength` {integer} Limits output size when using | |
| 838 | 841 | [convenience methods][]. **Default:** [`buffer.kMaxLength`][] | |
| 842 | + * `rejectGarbageAfterEnd` {boolean} If `true`, decompression fails when | ||
| 843 | + trailing input is detected after the end of the compressed stream. This | ||
| 844 | + includes unreadable bytes and, when decompressing gzip, additional gzip | ||
| 845 | + members following the first member. **Default:** `false` | ||
| 839 | 846 | ||
| 840 | 847 | See the [`deflateInit2` and `inflateInit2`][] documentation for more | |
| 841 | 848 | information. | |
@@ -845,6 +852,9 @@ information. | |||
| 845 | 852 | <!-- YAML | |
| 846 | 853 | added: v11.7.0 | |
| 847 | 854 | changes: | |
| 855 | + - version: REPLACEME | ||
| 856 | + pr-url: https://github.com/nodejs/node/pull/64023 | ||
| 857 | + description: The `rejectGarbageAfterEnd` option was added. | ||
| 848 | 858 | - version: | |
| 849 | 859 | - v14.5.0 | |
| 850 | 860 | - v12.19.0 | |
@@ -863,6 +873,8 @@ Each Brotli-based class takes an `options` object. All options are optional. | |||
| 863 | 873 | * `maxOutputLength` {integer} Limits output size when using | |
| 864 | 874 | [convenience methods][]. **Default:** [`buffer.kMaxLength`][] | |
| 865 | 875 | * `info` {boolean} If `true`, returns an object with `buffer` and `engine`. **Default:** `false` | |
| 876 | + * `rejectGarbageAfterEnd` {boolean} If `true`, decompression fails when | ||
| 877 | + input remains after the first complete compressed stream. **Default:** `false` | ||
| 866 | 878 | ||
| 867 | 879 | For example: | |
| 868 | 880 | ||
@@ -1086,6 +1098,10 @@ the inflate and deflate algorithms. | |||
| 1086 | 1098 | added: | |
| 1087 | 1099 | - v23.8.0 | |
| 1088 | 1100 | - v22.15.0 | |
| 1101 | + changes: | ||
| 1102 | + - version: REPLACEME | ||
| 1103 | + pr-url: https://github.com/nodejs/node/pull/64023 | ||
| 1104 | + description: The `rejectGarbageAfterEnd` option was added. | ||
| 1089 | 1105 | --> | |
| 1090 | 1106 | ||
| 1091 | 1107 | <!--type=misc--> | |
@@ -1102,6 +1118,8 @@ Each Zstd-based class takes an `options` object. All options are optional. | |||
| 1102 | 1118 | * `dictionary` {Buffer} Optional dictionary used to | |
| 1103 | 1119 | improve compression efficiency when compressing or decompressing data that | |
| 1104 | 1120 | shares common patterns with the dictionary. | |
| 1121 | + * `rejectGarbageAfterEnd` {boolean} If `true`, decompression fails when | ||
| 1122 | + input remains after the first complete compressed stream. **Default:** `false` | ||
| 1105 | 1123 | ||
| 1106 | 1124 | For example: | |
| 1107 | 1125 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -65,6 +65,7 @@ const { | |||
| 65 | 65 | const { owner_symbol } = require('internal/async_hooks').symbols; | |
| 66 | 66 | const { | |
| 67 | 67 | checkRangesOrGetDefault, | |
| 68 | + validateBoolean, | ||
| 68 | 69 | validateFunction, | |
| 69 | 70 | validateUint32, | |
| 70 | 71 | validateFiniteNumber, | |
@@ -246,6 +247,13 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { | |||
| 246 | 247 | opts.maxOutputLength, 'options.maxOutputLength', | |
| 247 | 248 | 1, kMaxLength, kMaxLength); | |
| 248 | 249 | ||
| 250 | + if (opts.rejectGarbageAfterEnd !== undefined) { | ||
| 251 | + validateBoolean( | ||
| 252 | + opts.rejectGarbageAfterEnd, | ||
| 253 | + 'options.rejectGarbageAfterEnd', | ||
| 254 | + ); | ||
| 255 | + } | ||
| 256 | + | ||
| 249 | 257 | if (opts.encoding || opts.objectMode || opts.writableObjectMode) { | |
| 250 | 258 | opts = { ...opts }; | |
| 251 | 259 | opts.encoding = null; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,144 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + require('../common'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + const test = require('node:test'); | ||
| 6 | + const { finished } = require('stream/promises'); | ||
| 7 | + const zlib = require('zlib'); | ||
| 8 | + | ||
| 9 | + const trailingJunkError = { | ||
| 10 | + code: 'ERR_TRAILING_JUNK_AFTER_STREAM_END', | ||
| 11 | + name: 'TypeError', | ||
| 12 | + }; | ||
| 13 | + | ||
| 14 | + function callAsync(fn, input, options) { | ||
| 15 | + return new Promise((resolve, reject) => { | ||
| 16 | + fn(input, options, (err, result) => { | ||
| 17 | + if (err) { | ||
| 18 | + reject(err); | ||
| 19 | + } else { | ||
| 20 | + resolve(result); | ||
| 21 | + } | ||
| 22 | + }); | ||
| 23 | + }); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + async function collect(stream, input) { | ||
| 27 | + const chunks = []; | ||
| 28 | + stream.on('data', (chunk) => chunks.push(chunk)); | ||
| 29 | + stream.end(input); | ||
| 30 | + await finished(stream); | ||
| 31 | + return Buffer.concat(chunks); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + const cases = [ | ||
| 35 | + { | ||
| 36 | + label: 'inflate', | ||
| 37 | + compress: zlib.deflateSync, | ||
| 38 | + decompress: zlib.inflate, | ||
| 39 | + decompressSync: zlib.inflateSync, | ||
| 40 | + createDecompress: zlib.createInflate, | ||
| 41 | + defaultOutput: 'a', | ||
| 42 | + }, | ||
| 43 | + { | ||
| 44 | + label: 'inflateRaw', | ||
| 45 | + compress: zlib.deflateRawSync, | ||
| 46 | + decompress: zlib.inflateRaw, | ||
| 47 | + decompressSync: zlib.inflateRawSync, | ||
| 48 | + createDecompress: zlib.createInflateRaw, | ||
| 49 | + defaultOutput: 'a', | ||
| 50 | + }, | ||
| 51 | + { | ||
| 52 | + label: 'gunzip', | ||
| 53 | + compress: zlib.gzipSync, | ||
| 54 | + decompress: zlib.gunzip, | ||
| 55 | + decompressSync: zlib.gunzipSync, | ||
| 56 | + createDecompress: zlib.createGunzip, | ||
| 57 | + defaultOutput: 'aa', | ||
| 58 | + }, | ||
| 59 | + { | ||
| 60 | + label: 'unzip', | ||
| 61 | + compress: zlib.gzipSync, | ||
| 62 | + decompress: zlib.unzip, | ||
| 63 | + decompressSync: zlib.unzipSync, | ||
| 64 | + createDecompress: zlib.createUnzip, | ||
| 65 | + defaultOutput: 'aa', | ||
| 66 | + }, | ||
| 67 | + { | ||
| 68 | + label: 'brotli', | ||
| 69 | + compress: zlib.brotliCompressSync, | ||
| 70 | + decompress: zlib.brotliDecompress, | ||
| 71 | + decompressSync: zlib.brotliDecompressSync, | ||
| 72 | + createDecompress: zlib.createBrotliDecompress, | ||
| 73 | + defaultOutput: 'a', | ||
| 74 | + }, | ||
| 75 | + { | ||
| 76 | + label: 'zstd', | ||
| 77 | + compress: zlib.zstdCompressSync, | ||
| 78 | + decompress: zlib.zstdDecompress, | ||
| 79 | + decompressSync: zlib.zstdDecompressSync, | ||
| 80 | + createDecompress: zlib.createZstdDecompress, | ||
| 81 | + defaultOutput: 'a', | ||
| 82 | + }, | ||
| 83 | + ]; | ||
| 84 | + | ||
| 85 | + for (const { | ||
| 86 | + label, | ||
| 87 | + compress, | ||
| 88 | + decompress, | ||
| 89 | + decompressSync, | ||
| 90 | + createDecompress, | ||
| 91 | + defaultOutput, | ||
| 92 | + } of cases) { | ||
| 93 | + test(`rejectGarbageAfterEnd rejects trailing input for ${label}`, async () => { | ||
| 94 | + const compressed = compress(Buffer.from('a')); | ||
| 95 | + const withTrailingInput = Buffer.concat([compressed, compressed]); | ||
| 96 | + | ||
| 97 | + assert.strictEqual(decompressSync(withTrailingInput).toString(), defaultOutput); | ||
| 98 | + assert.strictEqual( | ||
| 99 | + (await callAsync(decompress, withTrailingInput)).toString(), | ||
| 100 | + defaultOutput, | ||
| 101 | + ); | ||
| 102 | + assert.strictEqual( | ||
| 103 | + (await collect(createDecompress(), withTrailingInput)).toString(), | ||
| 104 | + defaultOutput, | ||
| 105 | + ); | ||
| 106 | + | ||
| 107 | + assert.throws( | ||
| 108 | + () => decompressSync(withTrailingInput, { rejectGarbageAfterEnd: true }), | ||
| 109 | + trailingJunkError, | ||
| 110 | + ); | ||
| 111 | + await assert.rejects( | ||
| 112 | + callAsync(decompress, withTrailingInput, { rejectGarbageAfterEnd: true }), | ||
| 113 | + trailingJunkError, | ||
| 114 | + ); | ||
| 115 | + await assert.rejects( | ||
| 116 | + collect( | ||
| 117 | + createDecompress({ rejectGarbageAfterEnd: true }), | ||
| 118 | + withTrailingInput, | ||
| 119 | + ), | ||
| 120 | + trailingJunkError, | ||
| 121 | + ); | ||
| 122 | + }); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + test('rejectGarbageAfterEnd must be a boolean', () => { | ||
| 126 | + const compressed = zlib.deflateSync(Buffer.from('a')); | ||
| 127 | + | ||
| 128 | + for (const value of [1, 'true', null]) { | ||
| 129 | + assert.throws( | ||
| 130 | + () => zlib.inflateSync(compressed, { rejectGarbageAfterEnd: value }), | ||
| 131 | + { | ||
| 132 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 133 | + name: 'TypeError', | ||
| 134 | + }, | ||
| 135 | + ); | ||
| 136 | + assert.throws( | ||
| 137 | + () => zlib.createInflate({ rejectGarbageAfterEnd: value }), | ||
| 138 | + { | ||
| 139 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 140 | + name: 'TypeError', | ||
| 141 | + }, | ||
| 142 | + ); | ||
| 143 | + } | ||
| 144 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments