@@ -67,12 +67,56 @@ export class Multipart implements Part {
6767 }
6868
6969 /**
70- * The boundary bytes used to separate the parts
70+ * Check if the boundary is valid
71+ * A valid boundary is 1 to 70 characters long, does not end with space, and may only contain:
72+ * A-Z a-z 0-9 '()+_,-./:=? and space
73+ *
74+ * ```bnf
75+ * boundary := 0*69<bchars> bcharsnospace
76+ *
77+ * bchars := bcharsnospace / " "
78+ *
79+ * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
80+ * "+" / "_" / "," / "-" / "." /
81+ * "/" / ":" / "=" / "?"
82+ * ```
83+ *
84+ * From: RFC 2046, Section 5.1.1. Common Syntax
85+ *
86+ * @internal
87+ */
88+ private static isValidBoundary ( boundary : Uint8Array ) : boolean {
89+ if ( boundary . length < 1 || boundary . length > 70 || boundary [ boundary . length - 1 ] === Multipart . SP )
90+ return false ;
91+
92+ for ( const char of boundary ) {
93+ if ( char >= 0x30 && char <= 0x39 ) continue ;
94+ if ( ( char >= 0x41 && char <= 0x5a ) || ( char >= 0x61 && char <= 0x7a ) ) continue ;
95+ if (
96+ char === Multipart . SP ||
97+ ( char >= 0x27 && char <= 0x29 ) ||
98+ ( char >= 0x2b && char <= 0x2f ) ||
99+ char === 0x3a ||
100+ char === 0x3d ||
101+ char === 0x3f ||
102+ char === 0x5f
103+ ) continue ;
104+ return false ;
105+ }
106+
107+ return true ;
108+ }
109+
110+ /**
111+ * Get the boundary bytes used to separate the parts
71112 */
72113 public get boundary ( ) : Uint8Array {
73114 return this . #boundary;
74115 }
75116
117+ /**
118+ * Set the boundary bytes used to separate the parts
119+ */
76120 public set boundary ( boundary : Uint8Array | string ) {
77121 this . #boundary = typeof boundary === "string" ? new TextEncoder ( ) . encode ( boundary ) : boundary ;
78122 this . setHeaders ( ) ;
@@ -96,8 +140,14 @@ export class Multipart implements Part {
96140 /**
97141 * Get the bytes of the body of this multipart. Includes all parts separated by the boundary.
98142 * Does not include the headers.
143+ *
144+ * @throws {RangeError } If the multipart boundary is invalid. A valid boundary is 1 to 70 characters long,
145+ * does not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space
99146 */
100147 public get body ( ) : Uint8Array {
148+ if ( ! Multipart . isValidBoundary ( this . #boundary) )
149+ throw new RangeError ( "Invalid boundary: must be 1 to 70 characters long, not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space" ) ;
150+
101151 const result : ArrayLike < number > [ ] = [ ] ;
102152 for ( const part of this . parts ) result . push ( Multipart . DOUBLE_DASH , this . boundary , Multipart . CRLF , part . bytes ( ) , Multipart . CRLF ) ;
103153 result . push ( Multipart . DOUBLE_DASH , this . boundary , Multipart . DOUBLE_DASH , Multipart . CRLF ) ;
@@ -127,6 +177,9 @@ export class Multipart implements Part {
127177 * @param [mediaType] Multipart media type to pass to the constructor
128178 */
129179 public static parseBody ( data : Uint8Array , boundary : Uint8Array , mediaType ?: string ) : Multipart {
180+ if ( ! Multipart . isValidBoundary ( boundary ) )
181+ console . warn ( "Invalid boundary:" , new TextDecoder ( ) . decode ( boundary ) , "\nMust be 1 to 70 characters long, not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space" ) ;
182+
130183 const parts : Uint8Array [ ] = [ ] ;
131184 const fullBoundarySequence = new Uint8Array ( Multipart . combineArrays ( [ Multipart . DOUBLE_DASH , boundary , Multipart . CRLF ] ) ) ;
132185 const endBoundarySequence = new Uint8Array ( Multipart . combineArrays ( [ Multipart . DOUBLE_DASH , boundary , Multipart . DOUBLE_DASH , Multipart . CRLF ] ) ) ;
@@ -340,6 +393,9 @@ export class Multipart implements Part {
340393
341394 /**
342395 * Get the bytes of the headers and {@link body} of this multipart.
396+ *
397+ * @throws {RangeError } If the multipart boundary is invalid. A valid boundary is 1 to 70 characters long,
398+ * does not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space
343399 */
344400 public bytes ( ) : Uint8Array {
345401 const result : ArrayLike < number > [ ] = [ ] ;
0 commit comments