File tree Expand file tree Collapse file tree 2 files changed +59
-4
lines changed Expand file tree Collapse file tree 2 files changed +59
-4
lines changed Original file line number Diff line number Diff line change @@ -49,7 +49,8 @@ internals.state = {
4949
5050
5151internals . defaults = {
52- maxBytes : Infinity
52+ maxBytes : Infinity ,
53+ maxParts : Infinity
5354} ;
5455
5556
@@ -71,8 +72,10 @@ exports.Dispenser = class extends Stream.Writable {
7172 this . _name = '' ;
7273 this . _pendingHeader = '' ;
7374 this . _error = null ;
74- this . _bytes = 0 ;
75+ this . _bytesCount = 0 ;
76+ this . _partsCount = 0 ;
7577 this . _maxBytes = settings . maxBytes ;
78+ this . _maxParts = settings . maxParts ;
7679
7780 this . _parts = new Nigel . Stream ( Buffer . from ( '--' + settings . boundary ) ) ;
7881 this . _lines = new Nigel . Stream ( Buffer . from ( '\r\n' ) ) ;
@@ -132,9 +135,9 @@ exports.Dispenser = class extends Stream.Writable {
132135
133136 const onReqData = ( data ) => {
134137
135- this . _bytes += Buffer . byteLength ( data ) ;
138+ this . _bytesCount += Buffer . byteLength ( data ) ;
136139
137- if ( this . _bytes > this . _maxBytes ) {
140+ if ( this . _bytesCount > this . _maxBytes ) {
138141 finish ( Boom . entityTooLarge ( 'Maximum size exceeded' ) ) ;
139142 }
140143 } ;
@@ -193,6 +196,12 @@ exports.Dispenser = class extends Stream.Writable {
193196
194197 this . _parts . needle ( Buffer . from ( '\r\n--' + this . _boundary ) ) ; // CRLF no longer optional
195198 }
199+ else {
200+ this . _partsCount ++ ;
201+ if ( this . _partsCount > this . _maxParts ) {
202+ return this . #abort( Boom . badRequest ( 'Maximum parts exceeded' ) ) ;
203+ }
204+ }
196205
197206 this . _state = internals . state . boundary ;
198207
Original file line number Diff line number Diff line change @@ -543,6 +543,52 @@ describe('Dispenser', () => {
543543 await team . work ;
544544 } ) ;
545545
546+ it ( 'errors if the payload size exceeds the part limit' , async ( ) => {
547+
548+ const createParts = ( n ) => {
549+
550+ return [
551+ '--AaB03x\r\n' ,
552+ `content-disposition: form-data; name="field${ n } "\r\n` ,
553+ '\r\n' ,
554+ 'one\r\ntwo\r\n' ,
555+ '--AaB03x\r\n' ,
556+ `content-disposition: form-data; name="file${ n } "; filename="file${ n } .txt"\r\n` ,
557+ 'Content-Type: text/plain\r\n' ,
558+ '\r\n' ,
559+ 'hello world\r\n'
560+ ] . join ( '' ) ;
561+ } ;
562+
563+ const endParts = ( ) => '--AaB03x--\r\n' ;
564+
565+ const payload = [
566+ createParts ( 2 ) ,
567+ createParts ( 4 ) ,
568+ createParts ( 6 ) ,
569+ createParts ( 8 ) ,
570+ createParts ( 10 ) ,
571+ endParts ( )
572+ ] . join ( '' ) ;
573+
574+ const req = new internals . Payload ( payload , true ) ;
575+ req . headers = { 'content-type' : 'multipart/form-data; boundary="AaB03x"' } ;
576+
577+ const dispenser = new Pez . Dispenser ( { boundary : 'AaB03x' , maxParts : 9 } ) ;
578+
579+ const team = new Teamwork . Team ( ) ;
580+ dispenser . once ( 'error' , ( err ) => {
581+
582+ expect ( err ) . to . exist ( ) ;
583+ expect ( err . message ) . to . equal ( 'Maximum parts exceeded' ) ;
584+ expect ( err . output . statusCode ) . to . equal ( 400 ) ;
585+ team . attend ( ) ;
586+ } ) ;
587+
588+ req . pipe ( dispenser ) ;
589+ await team . work ;
590+ } ) ;
591+
546592 it ( 'parses a request with "=" in the boundary' , async ( ) => {
547593
548594 const payload =
You can’t perform that action at this time.
0 commit comments