Skip to content

Commit 093d639

Browse files
committed
Fix CWE-190: properly parse RFC 2046 quoted-string boundary values
The previous code stripped all double-quote characters from the boundary string using a blanket replace(), which would silently corrupt boundary values that contain escaped quotes (\") and could leave a trailing semi-colon if the quoted string was followed by another parameter. Replace with a proper RFC 2045/2046 quoted-string parser: - Token form (no leading quote): trim and stop at the next ';'. - Quoted-string form (leading '"'): scan forward for the closing quote, skipping backslash-escaped quotes (\") along the way; extract the content between the outer quotes. Reject unterminated quoted strings (missing closing quote) immediately with PARSE_REQ_FAIL + abort(). This was reviewed and proposed by GitHub Copilot Autofix.
1 parent c7114c0 commit 093d639

1 file changed

Lines changed: 36 additions & 8 deletions

File tree

src/WebRequest.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -511,16 +511,44 @@ bool AsyncWebServerRequest::_parseReqHeader() {
511511
return true;
512512
}
513513

514-
// Extract the boundary value; strip any following parameters, optional
515-
// surrounding whitespace and quotes (RFC 2046 allows quoted-string).
514+
// Extract the boundary value that follows "boundary=" and strip leading/
515+
// trailing whitespace. The value may be either a token or a
516+
// quoted-string (RFC 2045 §5.1 / RFC 2046 §5.1).
516517
_boundary = value.substring(bpos + (int)T_BOUNDARY_LEN);
517-
int semi = _boundary.indexOf(';');
518-
if (semi >= 0) {
519-
_boundary = _boundary.substring(0, semi);
520-
}
521-
522518
_boundary.trim();
523-
_boundary.replace(String('"'), String());
519+
520+
if (_boundary.startsWith("\"")) {
521+
// Quoted-string form: scan forward from position 1 for the closing
522+
// double-quote, skipping any backslash-escaped quotes (\" sequences)
523+
// along the way. The boundary value is the content between the pair
524+
// of outer quotes, with escape sequences left as-is (the boundary is
525+
// compared verbatim against incoming data).
526+
int endQuote = 1;
527+
while (true) {
528+
endQuote = _boundary.indexOf('"', endQuote);
529+
if (endQuote < 0 || _boundary.charAt(endQuote - 1) != '\\') {
530+
break; // found the real closing quote (or string ran out)
531+
}
532+
endQuote++; // skip escaped quote (\") and keep scanning
533+
}
534+
if (endQuote < 0) {
535+
// Opening quote was never closed — malformed header.
536+
async_ws_log_d("Invalid multipart boundary (unterminated quote), aborting");
537+
_parseState = PARSE_REQ_FAIL;
538+
abort();
539+
return true;
540+
}
541+
// Strip the surrounding quotes; content between them is the boundary.
542+
_boundary = _boundary.substring(1, endQuote);
543+
} else {
544+
// Token form: the value ends at the next ';' (start of the next
545+
// parameter) or at the end of the header field.
546+
int semi = _boundary.indexOf(';');
547+
if (semi >= 0) {
548+
_boundary = _boundary.substring(0, semi);
549+
}
550+
_boundary.trim();
551+
}
524552

525553
// CWE-190 / DoS fix: RFC 2046 §5.1 limits boundary strings to 70
526554
// characters. _boundaryPosition was formerly uint8_t, so a boundary

0 commit comments

Comments
 (0)