From bed93b8550284ffa043721f2432002ee1d5ea735 Mon Sep 17 00:00:00 2001 From: Oliver Lassen Date: Fri, 22 Dec 2023 09:59:31 +0100 Subject: [PATCH 1/2] Try add totalMaxSize --- lib/types/multipart.js | 70 ++++++++++++++++++------------ test/test-types-multipart.js | 83 ++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 28 deletions(-) diff --git a/lib/types/multipart.js b/lib/types/multipart.js index cc0d7bb..0b7bceb 100644 --- a/lib/types/multipart.js +++ b/lib/types/multipart.js @@ -16,7 +16,7 @@ const BUF_CRLF = Buffer.from('\r\n'); const BUF_CR = Buffer.from('\r'); const BUF_DASH = Buffer.from('-'); -function noop() {} +function noop() { } const MAX_HEADER_PAIRS = 2000; // From node const MAX_HEADER_SIZE = 16 * 1024; // From node (its default value) @@ -201,8 +201,8 @@ class FileStream extends Readable { } const ignoreData = { - push: (chunk, pos) => {}, - destroy: () => {}, + push: (chunk, pos) => { }, + destroy: () => { }, }; function callAndUnsetCb(self, err) { @@ -224,8 +224,8 @@ class Multipart extends Writable { autoDestroy: true, emitClose: true, highWaterMark: (typeof cfg.highWaterMark === 'number' - ? cfg.highWaterMark - : undefined), + ? cfg.highWaterMark + : undefined), }; super(streamOpts); @@ -234,35 +234,38 @@ class Multipart extends Writable { const boundary = cfg.conType.params.boundary; const paramDecoder = (typeof cfg.defParamCharset === 'string' - && cfg.defParamCharset - ? getDecoder(cfg.defParamCharset) - : nullDecoder); + && cfg.defParamCharset + ? getDecoder(cfg.defParamCharset) + : nullDecoder); const defCharset = (cfg.defCharset || 'utf8'); const preservePath = cfg.preservePath; const fileOpts = { autoDestroy: true, emitClose: true, highWaterMark: (typeof cfg.fileHwm === 'number' - ? cfg.fileHwm - : undefined), + ? cfg.fileHwm + : undefined), }; const limits = cfg.limits; const fieldSizeLimit = (limits && typeof limits.fieldSize === 'number' - ? limits.fieldSize - : 1 * 1024 * 1024); + ? limits.fieldSize + : 1 * 1024 * 1024); const fileSizeLimit = (limits && typeof limits.fileSize === 'number' - ? limits.fileSize - : Infinity); + ? limits.fileSize + : Infinity); const filesLimit = (limits && typeof limits.files === 'number' - ? limits.files - : Infinity); + ? limits.files + : Infinity); const fieldsLimit = (limits && typeof limits.fields === 'number' - ? limits.fields - : Infinity); + ? limits.fields + : Infinity); const partsLimit = (limits && typeof limits.parts === 'number' - ? limits.parts - : Infinity); + ? limits.parts + : Infinity); + this.totalSizeLimit = (limits && typeof limits.totalSize === 'number') + ? limits.totalSize + : Infinity; let parts = -1; // Account for initial boundary let fields = 0; @@ -303,7 +306,7 @@ class Multipart extends Writable { } const disp = parseDisposition(header['content-disposition'][0], - paramDecoder); + paramDecoder); if (!disp || disp.type !== 'form-data') { skipPart = true; return; @@ -359,9 +362,11 @@ class Multipart extends Writable { 'file', partName, this._fileStream, - { filename, + { + filename, encoding: partEncoding, - mimeType: partType } + mimeType: partType + } ); } else { // Non-file @@ -388,7 +393,7 @@ class Multipart extends Writable { let matchPostBoundary = 0; const ssCb = (isMatch, data, start, end, isDataSafe) => { -retrydata: + retrydata: while (data) { if (this._hparser !== null) { const ret = this._hparser.push(data, start, end); @@ -538,10 +543,12 @@ retrydata: 'field', partName, data, - { nameTruncated: false, + { + nameTruncated: false, valueTruncated: partTruncated, encoding: partEncoding, - mimeType: partType } + mimeType: partType + } ); } @@ -554,6 +561,8 @@ retrydata: this._writecb = null; this._finalcb = null; + this.writtenSize = 0; + // Just in case there is no preamble this.write(BUF_CRLF); } @@ -563,8 +572,13 @@ retrydata: } _write(chunk, enc, cb) { - this._writecb = cb; - this._bparser.push(chunk, 0); + this.writtenSize += chunk.length; + if (this.writtenSize > this.totalSizeLimit) { + this.destroy(new Error('Max size reached!')); + } else { + this._writecb = cb; + this._bparser.push(chunk, 0); + } if (this._writecb) callAndUnsetCb(this); } diff --git a/test/test-types-multipart.js b/test/test-types-multipart.js index 9755642..69dcd92 100644 --- a/test/test-types-multipart.js +++ b/test/test-types-multipart.js @@ -128,6 +128,89 @@ const tests = [ ], what: 'Fields only' }, + { source: [ + ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="cont"', + '', + 'some random content', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="pass"', + '', + 'some random pass', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name=bit', + '', + '2', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--' + ].join('\r\n') + ], + limits: { + totalSize: 200 + }, + events:[], + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [ + { error: 'Max size reached!' } + ], + what: 'MaxSize limit' + }, + { source: [ + ['------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="cont"', + '', + 'some random content', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name="pass"', + '', + 'some random pass', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY', + 'Content-Disposition: form-data; name=bit', + '', + '2', + '------WebKitFormBoundaryTB2MiQ36fnSJlrhY--' + ].join('\r\n') + ], + limits: { + totalSize: 350 + }, + boundary: '----WebKitFormBoundaryTB2MiQ36fnSJlrhY', + expected: [ + { + type: 'field', + name: 'cont', + val: 'some random content', + info: { + nameTruncated: false, + valueTruncated: false, + encoding: '7bit', + mimeType: 'text/plain' + } + }, + { + type: 'field', + name: 'pass', + val: 'some random pass', + info: { + nameTruncated: false, + valueTruncated: false, + encoding: '7bit', + mimeType: 'text/plain' + } + }, + { + type: 'field', + name: 'bit', + val: '2', + info: { + nameTruncated: false, + valueTruncated: false, + encoding: '7bit', + mimeType: 'text/plain' + } + }, + ], + what: 'MaxSize limit is able to read entire thing' + }, { source: [ '' ], From ff83090aaa10bc2d45cc8fae9e4021906174aeef Mon Sep 17 00:00:00 2001 From: Oliver Lassen Date: Fri, 22 Dec 2023 10:03:19 +0100 Subject: [PATCH 2/2] Formatting stuff --- lib/types/multipart.js | 69 ++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/lib/types/multipart.js b/lib/types/multipart.js index 0b7bceb..4088ca6 100644 --- a/lib/types/multipart.js +++ b/lib/types/multipart.js @@ -16,7 +16,7 @@ const BUF_CRLF = Buffer.from('\r\n'); const BUF_CR = Buffer.from('\r'); const BUF_DASH = Buffer.from('-'); -function noop() { } +function noop() {} const MAX_HEADER_PAIRS = 2000; // From node const MAX_HEADER_SIZE = 16 * 1024; // From node (its default value) @@ -201,8 +201,8 @@ class FileStream extends Readable { } const ignoreData = { - push: (chunk, pos) => { }, - destroy: () => { }, + push: (chunk, pos) => {}, + destroy: () => {}, }; function callAndUnsetCb(self, err) { @@ -224,8 +224,8 @@ class Multipart extends Writable { autoDestroy: true, emitClose: true, highWaterMark: (typeof cfg.highWaterMark === 'number' - ? cfg.highWaterMark - : undefined), + ? cfg.highWaterMark + : undefined), }; super(streamOpts); @@ -234,38 +234,40 @@ class Multipart extends Writable { const boundary = cfg.conType.params.boundary; const paramDecoder = (typeof cfg.defParamCharset === 'string' - && cfg.defParamCharset - ? getDecoder(cfg.defParamCharset) - : nullDecoder); + && cfg.defParamCharset + ? getDecoder(cfg.defParamCharset) + : nullDecoder); const defCharset = (cfg.defCharset || 'utf8'); const preservePath = cfg.preservePath; const fileOpts = { autoDestroy: true, emitClose: true, highWaterMark: (typeof cfg.fileHwm === 'number' - ? cfg.fileHwm - : undefined), + ? cfg.fileHwm + : undefined), }; const limits = cfg.limits; const fieldSizeLimit = (limits && typeof limits.fieldSize === 'number' - ? limits.fieldSize - : 1 * 1024 * 1024); + ? limits.fieldSize + : 1 * 1024 * 1024); const fileSizeLimit = (limits && typeof limits.fileSize === 'number' - ? limits.fileSize - : Infinity); + ? limits.fileSize + : Infinity); const filesLimit = (limits && typeof limits.files === 'number' - ? limits.files - : Infinity); + ? limits.files + : Infinity); const fieldsLimit = (limits && typeof limits.fields === 'number' - ? limits.fields - : Infinity); + ? limits.fields + : Infinity); const partsLimit = (limits && typeof limits.parts === 'number' - ? limits.parts - : Infinity); + ? limits.parts + : Infinity); this.totalSizeLimit = (limits && typeof limits.totalSize === 'number') - ? limits.totalSize - : Infinity; + ? limits.totalSize + : Infinity; + + this._writtenSize = 0; let parts = -1; // Account for initial boundary let fields = 0; @@ -306,7 +308,7 @@ class Multipart extends Writable { } const disp = parseDisposition(header['content-disposition'][0], - paramDecoder); + paramDecoder); if (!disp || disp.type !== 'form-data') { skipPart = true; return; @@ -362,11 +364,9 @@ class Multipart extends Writable { 'file', partName, this._fileStream, - { - filename, + { filename, encoding: partEncoding, - mimeType: partType - } + mimeType: partType } ); } else { // Non-file @@ -393,7 +393,7 @@ class Multipart extends Writable { let matchPostBoundary = 0; const ssCb = (isMatch, data, start, end, isDataSafe) => { - retrydata: +retrydata: while (data) { if (this._hparser !== null) { const ret = this._hparser.push(data, start, end); @@ -543,12 +543,10 @@ class Multipart extends Writable { 'field', partName, data, - { - nameTruncated: false, + { nameTruncated: false, valueTruncated: partTruncated, encoding: partEncoding, - mimeType: partType - } + mimeType: partType } ); } @@ -561,8 +559,6 @@ class Multipart extends Writable { this._writecb = null; this._finalcb = null; - this.writtenSize = 0; - // Just in case there is no preamble this.write(BUF_CRLF); } @@ -572,13 +568,14 @@ class Multipart extends Writable { } _write(chunk, enc, cb) { - this.writtenSize += chunk.length; - if (this.writtenSize > this.totalSizeLimit) { + this._writtenSize += chunk.length; + if (this._writtenSize > this.totalSizeLimit) { this.destroy(new Error('Max size reached!')); } else { this._writecb = cb; this._bparser.push(chunk, 0); } + if (this._writecb) callAndUnsetCb(this); }