Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@ private void AppendContentLength(ReadOnlySpan<byte> value)
KestrelBadHttpRequestException.Throw(RequestRejectionReason.MultipleContentLengths);
}

if (!Utf8Parser.TryParse(value, out long parsed, out var consumed) ||
// Per RFC 9110 §8.6, Content-Length is 1*DIGIT: only ASCII digits are allowed.
Comment thread
DeagleGross marked this conversation as resolved.
// https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6
// Utf8Parser.TryParse accepts a leading '+' or '-' sign, so ensure the first byte is a digit.
long parsed = 0;
if (value.IsEmpty ||
value[0] is < (byte)'0' or > (byte)'9' ||
!Utf8Parser.TryParse(value, out parsed, out var consumed) ||
parsed < 0 ||
consumed != value.Length)
{
Expand All @@ -122,8 +128,11 @@ private unsafe void AppendContentLengthCustomEncoding(ReadOnlySpan<byte> value,
var numChars = customEncoding.GetChars(value, decodedChars);
long parsed = -1;

// Per RFC 9110 §8.6, Content-Length is 1*DIGIT. NumberStyles.None
// https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6
// disallows leading signs and whitespace that NumberStyles.Integer would accept.
if (numChars > 19 ||
!long.TryParse(decodedChars.Slice(0, numChars), NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed) ||
!long.TryParse(decodedChars.Slice(0, numChars), NumberStyles.None, CultureInfo.InvariantCulture, out parsed) ||
Comment thread
DeagleGross marked this conversation as resolved.
parsed < 0)
{
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderString(HeaderNames.ContentLength, EncodingSelector, checkForNewlineChars: false));
Expand Down
21 changes: 21 additions & 0 deletions src/Servers/Kestrel/Core/test/HttpRequestHeadersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,27 @@ public void CanSpecifyEncodingForContentLength()
new HttpRequestHeaders().Append(contentLengthNameBytes, contentLengthValueBytes, checkForNewlineChars: false));
}

[Theory]
[InlineData("+1")]
[InlineData("+0")]
[InlineData("-0")]
[InlineData("-1")]
[InlineData(" 1")]
[InlineData("1a")]
[InlineData("")]
public void ContentLengthWithCustomEncodingRejectsNonDigits(string value)
{
var contentLengthNameBytes = Encoding.ASCII.GetBytes(HeaderNames.ContentLength);
var contentLengthValueBytes = Encoding.UTF32.GetBytes(value);

var headers = new HttpRequestHeaders(encodingSelector: _ => Encoding.UTF32);

#pragma warning disable CS0618 // Type or member is obsolete
Assert.Throws<BadHttpRequestException>(() =>
#pragma warning restore CS0618 // Type or member is obsolete
headers.Append(contentLengthNameBytes, contentLengthValueBytes, checkForNewlineChars: false));
}

[Fact]
public void ValueReuseNeverWhenUnknownHeader()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ public Task BadRequestIfMethodRequiresLengthButNoContentLengthInHttp10Request(st
[Theory]
[InlineData("NaN")]
[InlineData("-1")]
[InlineData("+1")]
[InlineData("+0")]
[InlineData("-0")]
Comment thread
DeagleGross marked this conversation as resolved.
[InlineData("")]
public Task BadRequestIfContentLengthInvalid(string contentLength)
{
return TestBadRequest(
Expand Down
Loading