fix: HTTP request smuggling via Transfer-Encoding token whitespace

Closes https://github.com/dart-lang/sdk/pull/63250

GitOrigin-RevId: 7c6b7bf7d033dd31dc23a7cc3a30b3dc4db41d16
Change-Id: I573328660bbef7ec9cd829ec376fcee894a6bc34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/498420
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
mohammadmseet-hue
2026-04-30 00:52:02 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 1a7e73a7e5
commit c5d23e0df3
2 changed files with 40 additions and 8 deletions
+19 -8
View File
@@ -1112,20 +1112,31 @@ class _HttpParser extends Stream<_HttpIncoming> {
value.length = length;
}
static bool _isOWS(int c) {
return c == _CharCode.SP || c == _CharCode.HT;
}
static List<String> _tokenizeFieldValue(String headerValue) {
int length = headerValue.length;
List<String> tokens = <String>[];
// First non-OWS character of the token. Note that headerValue is already
// trimmed from start and end so we don't need to skip characters at start.
int start = 0;
int index = 0;
while (index < headerValue.length) {
if (headerValue[index] == ",") {
tokens.add(headerValue.substring(start, index));
start = index + 1;
} else if (headerValue[index] == " " || headerValue[index] == "\t") {
while (start < length) {
int end = start + 1;
while (end < length && headerValue.codeUnitAt(end) != _CharCode.COMMA) {
end++;
}
int comma = end;
while (start < end && _isOWS(headerValue.codeUnitAt(end - 1))) {
end--;
}
tokens.add(headerValue.substring(start, end));
start = comma + 1;
while (start < length && _isOWS(headerValue.codeUnitAt(start))) {
start++;
}
index++;
}
tokens.add(headerValue.substring(start, index));
return tokens;
}
+21
View File
@@ -718,6 +718,27 @@ Content-Length: 38\r
GET /admin HTTP/1.1\r\nHost: backend\r\n\r\n""";
_testParseInvalidRequest(request);
// RFC 7230 section 3.2.6 forbids SP/HT inside a token. With the tokenizer
// treating "x chunked" as a single token (not "chunked"), the request has
// a Transfer-Encoding header whose final coding is not chunked, so it
// MUST be rejected (RFC 7230 section 3.3.3 rule 3) rather than falling
// back to Content-Length.
request = """
POST /test HTTP/1.1\r
Content-Length: 10\r
Transfer-Encoding: x chunked\r
\r
0123456789""";
_testParseInvalidRequest(request);
request = """
POST /test HTTP/1.1\r
Content-Length: 10\r
Transfer-Encoding: x\tchunked\r
\r
0123456789""";
_testParseInvalidRequest(request);
// Content-Length and "Transfer-Encoding: chunked" are specified.
request = """
POST /test HTTP/1.1\r