Correctly check for limits in the split buffer case (#12117)
- We were looking all total consumed bytes instead of bytes consumed since the last time we parsed a form value. This resulted in thinking that the key or value was too long if it couldn't be parsed after having parsed a bunch of data in the same read. - Added tests
This commit is contained in:
parent
dfe159023a
commit
72f91b8507
|
|
@ -218,6 +218,7 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
{
|
{
|
||||||
var sequenceReader = new SequenceReader<byte>(buffer);
|
var sequenceReader = new SequenceReader<byte>(buffer);
|
||||||
var consumed = sequenceReader.Position;
|
var consumed = sequenceReader.Position;
|
||||||
|
var consumedBytes = default(long);
|
||||||
var equalsDelimiter = GetEqualsForEncoding();
|
var equalsDelimiter = GetEqualsForEncoding();
|
||||||
var andDelimiter = GetAndForEncoding();
|
var andDelimiter = GetAndForEncoding();
|
||||||
|
|
||||||
|
|
@ -227,7 +228,7 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
if (!sequenceReader.TryReadTo(out ReadOnlySequence<byte> key, equalsDelimiter, advancePastDelimiter: false) ||
|
if (!sequenceReader.TryReadTo(out ReadOnlySequence<byte> key, equalsDelimiter, advancePastDelimiter: false) ||
|
||||||
!sequenceReader.IsNext(equalsDelimiter, true))
|
!sequenceReader.IsNext(equalsDelimiter, true))
|
||||||
{
|
{
|
||||||
if (sequenceReader.Consumed > KeyLengthLimit)
|
if ((sequenceReader.Consumed - consumedBytes) > KeyLengthLimit)
|
||||||
{
|
{
|
||||||
ThrowKeyTooLargeException();
|
ThrowKeyTooLargeException();
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +246,7 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
{
|
{
|
||||||
if (!isFinalBlock)
|
if (!isFinalBlock)
|
||||||
{
|
{
|
||||||
if (sequenceReader.Consumed - key.Length > ValueLengthLimit)
|
if ((sequenceReader.Consumed - consumedBytes - key.Length) > ValueLengthLimit)
|
||||||
{
|
{
|
||||||
ThrowValueTooLargeException();
|
ThrowValueTooLargeException();
|
||||||
}
|
}
|
||||||
|
|
@ -268,6 +269,7 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
|
|
||||||
AppendAndVerify(ref accumulator, decodedKey, decodedValue);
|
AppendAndVerify(ref accumulator, decodedKey, decodedValue);
|
||||||
|
|
||||||
|
consumedBytes = sequenceReader.Consumed;
|
||||||
consumed = sequenceReader.Position;
|
consumed = sequenceReader.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -152,15 +153,6 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
Assert.Equal(expectedValue, form[key]);
|
Assert.Equal(expectedValue, form[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData<Encoding> Encodings =>
|
|
||||||
new TheoryData<Encoding>
|
|
||||||
{
|
|
||||||
{ Encoding.UTF8 },
|
|
||||||
{ Encoding.UTF32 },
|
|
||||||
{ Encoding.ASCII },
|
|
||||||
{ Encoding.Unicode }
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(Encodings))]
|
[MemberData(nameof(Encodings))]
|
||||||
public void TryParseFormValues_SingleSegmentWorks(Encoding encoding)
|
public void TryParseFormValues_SingleSegmentWorks(Encoding encoding)
|
||||||
|
|
@ -386,6 +378,67 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(IncompleteFormKeys))]
|
||||||
|
public void ParseFormWithIncompleteKeyWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence)
|
||||||
|
{
|
||||||
|
KeyValueAccumulator accumulator = default;
|
||||||
|
|
||||||
|
var formReader = new FormPipeReader(null)
|
||||||
|
{
|
||||||
|
KeyLengthLimit = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
|
||||||
|
|
||||||
|
IDictionary<string, StringValues> values = accumulator.GetResults();
|
||||||
|
Assert.Single(values);
|
||||||
|
Assert.Contains("fo", values);
|
||||||
|
Assert.Equal("bar", values["fo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(IncompleteFormValues))]
|
||||||
|
public void ParseFormWithIncompleteValueWhenIsFinalBlockSucceeds(ReadOnlySequence<byte> readOnlySequence)
|
||||||
|
{
|
||||||
|
KeyValueAccumulator accumulator = default;
|
||||||
|
|
||||||
|
var formReader = new FormPipeReader(null)
|
||||||
|
{
|
||||||
|
ValueLengthLimit = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
formReader.ParseFormValues(ref readOnlySequence, ref accumulator, isFinalBlock: false);
|
||||||
|
|
||||||
|
IDictionary<string, StringValues> values = accumulator.GetResults();
|
||||||
|
Assert.Single(values);
|
||||||
|
Assert.Contains("fo", values);
|
||||||
|
Assert.Equal("bar", values["fo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData<ReadOnlySequence<byte>> IncompleteFormKeys =>
|
||||||
|
new TheoryData<ReadOnlySequence<byte>>
|
||||||
|
{
|
||||||
|
{ ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("a")) },
|
||||||
|
{ new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&ba")) }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static TheoryData<ReadOnlySequence<byte>> IncompleteFormValues =>
|
||||||
|
new TheoryData<ReadOnlySequence<byte>>
|
||||||
|
{
|
||||||
|
{ ReadOnlySequenceFactory.CreateSegments(Encoding.UTF8.GetBytes("fo=bar&b"), Encoding.UTF8.GetBytes("=")) },
|
||||||
|
{ new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("fo=bar&b=")) }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static TheoryData<Encoding> Encodings =>
|
||||||
|
new TheoryData<Encoding>
|
||||||
|
{
|
||||||
|
{ Encoding.UTF8 },
|
||||||
|
{ Encoding.UTF32 },
|
||||||
|
{ Encoding.ASCII },
|
||||||
|
{ Encoding.Unicode }
|
||||||
|
};
|
||||||
|
|
||||||
internal virtual Task<Dictionary<string, StringValues>> ReadFormAsync(FormPipeReader reader)
|
internal virtual Task<Dictionary<string, StringValues>> ReadFormAsync(FormPipeReader reader)
|
||||||
{
|
{
|
||||||
return reader.ReadFormAsync();
|
return reader.ReadFormAsync();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue