Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions cadente/Sisk.Cadente/HttpSerializer/HttpRequestReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,18 @@ private static async ValueTask<int> ReadWithTimeoutAsync ( Stream stream, Memory
}
break;
case 1: // Connection
keepAliveEnabled = !Ascii.EqualsIgnoreCase ( valueSpan, CloseValue );
keepAliveEnabled = !TokenListContains ( valueSpan, CloseValue );
break;
case 2: // Expect
expect100 = Ascii.EqualsIgnoreCase ( valueSpan, ContinueValue );
expect100 = TokenListContains ( valueSpan, ContinueValue );
break;
case 3: // Transfer-Encoding
if (seenTransferEncoding) {
failReason = "duplicate Transfer-Encoding header is a request-smuggling vector (RFC 9112 §6.3.3)";
goto ParseFailed; // duplicate TE = request-smuggling vector (RFC 9112 §6.3.3)
}
seenTransferEncoding = true;
isChunked = Ascii.EqualsIgnoreCase ( valueSpan, ChunkedValue );
isChunked = TokenListContains ( valueSpan, ChunkedValue );
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject non-final chunked transfer coding

TokenListContains treats any occurrence of chunked as sufficient, so a request like Transfer-Encoding: chunked, gzip will be accepted as chunked (isChunked = true) instead of rejected. In HTTP/1.1 this is invalid framing (chunked must be the final transfer-coding), and accepting it can create request-boundary mismatches with intermediaries/backends that follow RFC 9112 and return 400, which is a request-smuggling risk on reused connections.

Useful? React with 👍 / 👎.

if (isChunked)
contentLength = -1;
break;
Expand Down Expand Up @@ -406,4 +406,25 @@ private static bool IsSupportedHttpProtocol ( ReadOnlySpan<byte> protocol )
|| protocol.SequenceEqual ( Http10 )
|| protocol.SequenceEqual ( Http09 );

[MethodImpl ( MethodImplOptions.AggressiveInlining )]
private static bool TokenListContains ( ReadOnlySpan<byte> value, ReadOnlySpan<byte> expectedToken ) {
while (!value.IsEmpty) {
int commaIndex = value.IndexOf ( (byte) ',' );
ReadOnlySpan<byte> token = commaIndex >= 0
? value.Slice ( 0, commaIndex )
: value;

if (Ascii.EqualsIgnoreCase ( token.Trim ( TrimChars ), expectedToken )) {
return true;
}

if (commaIndex < 0) {
break;
}

value = value.Slice ( commaIndex + 1 );
}

return false;
}
}
Loading