diff --git a/src/Http/WebUtilities/src/FormPipeReader.cs b/src/Http/WebUtilities/src/FormPipeReader.cs index c8d8497807..70e85ccfcf 100644 --- a/src/Http/WebUtilities/src/FormPipeReader.cs +++ b/src/Http/WebUtilities/src/FormPipeReader.cs @@ -218,6 +218,7 @@ namespace Microsoft.AspNetCore.WebUtilities { var sequenceReader = new SequenceReader(buffer); var consumed = sequenceReader.Position; + var consumedBytes = default(long); var equalsDelimiter = GetEqualsForEncoding(); var andDelimiter = GetAndForEncoding(); @@ -227,7 +228,7 @@ namespace Microsoft.AspNetCore.WebUtilities if (!sequenceReader.TryReadTo(out ReadOnlySequence key, equalsDelimiter, advancePastDelimiter: false) || !sequenceReader.IsNext(equalsDelimiter, true)) { - if (sequenceReader.Consumed > KeyLengthLimit) + if ((sequenceReader.Consumed - consumedBytes) > KeyLengthLimit) { ThrowKeyTooLargeException(); } @@ -245,7 +246,7 @@ namespace Microsoft.AspNetCore.WebUtilities { if (!isFinalBlock) { - if (sequenceReader.Consumed - key.Length > ValueLengthLimit) + if ((sequenceReader.Consumed - consumedBytes - key.Length) > ValueLengthLimit) { ThrowValueTooLargeException(); } @@ -268,6 +269,7 @@ namespace Microsoft.AspNetCore.WebUtilities AppendAndVerify(ref accumulator, decodedKey, decodedValue); + consumedBytes = sequenceReader.Consumed; consumed = sequenceReader.Position; } diff --git a/src/Http/WebUtilities/test/FormPipeReaderTests.cs b/src/Http/WebUtilities/test/FormPipeReaderTests.cs index 23c3be73fd..192911562f 100644 --- a/src/Http/WebUtilities/test/FormPipeReaderTests.cs +++ b/src/Http/WebUtilities/test/FormPipeReaderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Buffers; using System.Collections.Generic; using System.IO; @@ -152,15 +153,6 @@ namespace Microsoft.AspNetCore.WebUtilities Assert.Equal(expectedValue, form[key]); } - public static TheoryData Encodings => - new TheoryData - { - { Encoding.UTF8 }, - { Encoding.UTF32 }, - { Encoding.ASCII }, - { Encoding.Unicode } - }; - [Theory] [MemberData(nameof(Encodings))] public void TryParseFormValues_SingleSegmentWorks(Encoding encoding) @@ -386,6 +378,67 @@ namespace Microsoft.AspNetCore.WebUtilities } } + [Theory] + [MemberData(nameof(IncompleteFormKeys))] + public void ParseFormWithIncompleteKeyWhenIsFinalBlockSucceeds(ReadOnlySequence readOnlySequence) + { + KeyValueAccumulator accumulator = default; + + var formReader = new FormPipeReader(null) + { + KeyLengthLimit = 3 + }; + + formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false); + + IDictionary values = accumulator.GetResults(); + Assert.Single(values); + Assert.Contains("fo", values); + Assert.Equal("bar", values["fo"]); + } + + [Theory] + [MemberData(nameof(IncompleteFormValues))] + public void ParseFormWithIncompleteValueWhenIsFinalBlockSucceeds(ReadOnlySequence readOnlySequence) + { + KeyValueAccumulator accumulator = default; + + var formReader = new FormPipeReader(null) + { + ValueLengthLimit = 3 + }; + + formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false); + + IDictionary values = accumulator.GetResults(); + Assert.Single(values); + Assert.Contains("fo", values); + Assert.Equal("bar", values["fo"]); + } + + public static TheoryData> IncompleteFormKeys => + new TheoryData> + { + { ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("a")) }, + { new ReadOnlySequence(Encoding.UTF8.GetBytes("fo=bar&ba")) } + }; + + public static TheoryData> IncompleteFormValues => + new TheoryData> + { + { ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("=")) }, + { new ReadOnlySequence(Encoding.UTF8.GetBytes("fo=bar&b=")) } + }; + + public static TheoryData Encodings => + new TheoryData + { + { Encoding.UTF8 }, + { Encoding.UTF32 }, + { Encoding.ASCII }, + { Encoding.Unicode } + }; + internal virtual Task> ReadFormAsync(FormPipeReader reader) { return reader.ReadFormAsync();