Skip to content

Commit 4f53dad

Browse files
committed
validate boundary when constructing Multipart
Based RFC 2046, Section 5.1.1.
1 parent 4413919 commit 4f53dad

File tree

1 file changed

+45
-0
lines changed

1 file changed

+45
-0
lines changed

src/Multipart.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,58 @@ export class Multipart implements Part {
5959
* @param parts The parts to include in the multipart
6060
* @param [boundary] The multipart boundary used to separate the parts. Randomly generated if not provided
6161
* @param [mediaType] The media type of the multipart. Defaults to "multipart/mixed"
62+
*
63+
* @throws {RangeError} If the boundary is invalid. A valid boundary is 1 to 70 characters long, does not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space
6264
*/
6365
public constructor(public readonly parts: Part[], boundary: Uint8Array | string = crypto.randomUUID(), mediaType: string = "multipart/mixed") {
6466
this.#boundary = typeof boundary === "string" ? new TextEncoder().encode(boundary) : boundary;
67+
if (!Multipart.isValidBoundary(this.#boundary))
68+
throw new RangeError("Boundary must be 1 to 70 characters long, not end with space, and may only contain: A-Z a-z 0-9 '()+_,-./:=? and space");
6569
this.#mediaType = mediaType;
6670
this.setHeaders();
6771
}
6872

73+
/**
74+
* Check if the boundary is valid
75+
* A valid boundary is 1 to 70 characters long, does not end with space, and may only contain:
76+
* A-Z a-z 0-9 '()+_,-./:=? and space
77+
*
78+
* ```bnf
79+
* boundary := 0*69<bchars> bcharsnospace
80+
*
81+
* bchars := bcharsnospace / " "
82+
*
83+
* bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
84+
* "+" / "_" / "," / "-" / "." /
85+
* "/" / ":" / "=" / "?"
86+
* ```
87+
*
88+
* From: RFC 2046, Section 5.1.1. Common Syntax
89+
*
90+
* @internal
91+
*/
92+
private static isValidBoundary(boundary: Uint8Array): boolean {
93+
if (boundary.length < 1 || boundary.length > 70 || boundary[boundary.length - 1] === Multipart.SP)
94+
return false;
95+
96+
for (const char of boundary) {
97+
if (char >= 0x30 && char <= 0x39) continue;
98+
if ((char >= 0x41 && char <= 0x5a) || (char >= 0x61 && char <= 0x7a)) continue;
99+
if (
100+
char === Multipart.SP ||
101+
(char >= 0x27 && char <= 0x29) ||
102+
(char >= 0x2b && char <= 0x2f) ||
103+
char === 0x3a ||
104+
char === 0x3d ||
105+
char === 0x3f ||
106+
char === 0x5f
107+
) continue;
108+
return false;
109+
}
110+
111+
return true;
112+
}
113+
69114
/**
70115
* The boundary bytes used to separate the parts
71116
*/

0 commit comments

Comments
 (0)