Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Diagnostics;
using System.Text.Json.Nodes;
using System.Text.Json.Schema;
Expand All @@ -13,8 +14,6 @@ internal sealed class VersionConverter : JsonPrimitiveConverter<Version?>
private const int MinimumVersionLength = 3; // 0.0

private const int MaximumVersionLength = 43; // 2147483647.2147483647.2147483647.2147483647

private const int MaximumEscapedVersionLength = JsonConstants.MaxExpansionFactorWhileEscaping * MaximumVersionLength;
#endif

public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
Expand All @@ -37,38 +36,45 @@ private static Version ReadCore(ref Utf8JsonReader reader)
Debug.Assert(reader.TokenType is JsonTokenType.PropertyName or JsonTokenType.String);

#if NET
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, MinimumVersionLength, MaximumEscapedVersionLength))
{
ThrowHelper.ThrowFormatException(DataType.Version);
}
int bufferLength = reader.ValueLength;
char[]? rentedBuffer = null;
Span<char> charBuffer = bufferLength <= JsonConstants.StackallocCharThreshold
? stackalloc char[JsonConstants.StackallocCharThreshold]
: (rentedBuffer = ArrayPool<char>.Shared.Rent(bufferLength));

Span<char> charBuffer = stackalloc char[MaximumEscapedVersionLength];
int bytesWritten = reader.CopyString(charBuffer);
ReadOnlySpan<char> source = charBuffer.Slice(0, bytesWritten);

if (!char.IsDigit(source[0]) || !char.IsDigit(source[^1]))
// Enforce leading and trailing whitespace rejection
if (source.Length > 0 && (char.IsWhiteSpace(source[0]) || char.IsWhiteSpace(source[^1])))
{
// Since leading and trailing whitespaces are forbidden throughout System.Text.Json converters
// we need to make sure that our input doesn't have them,
// and if it has - we need to throw, to match behaviour of other converters
// since Version.TryParse allows them and silently parses input to Version
if (rentedBuffer is not null)
{
ArrayPool<char>.Shared.Return(rentedBuffer);
}
ThrowHelper.ThrowFormatException(DataType.Version);
}

if (Version.TryParse(source, out Version? result))
bool success = Version.TryParse(source, out Version? result);

if (rentedBuffer is not null)
{
return result;
ArrayPool<char>.Shared.Return(rentedBuffer);
}

if (success)
{
return result!;
}
#else
string? versionString = reader.GetString();
if (!string.IsNullOrEmpty(versionString) && (!char.IsDigit(versionString[0]) || !char.IsDigit(versionString[versionString.Length - 1])))

// Enforce leading and trailing whitespace rejection
if (!string.IsNullOrEmpty(versionString) && (char.IsWhiteSpace(versionString[0]) || char.IsWhiteSpace(versionString[versionString.Length - 1])))
{
// Since leading and trailing whitespaces are forbidden throughout System.Text.Json converters
// we need to make sure that our input doesn't have them,
// and if it has - we need to throw, to match behaviour of other converters
// since Version.TryParse allows them and silently parses input to Version
ThrowHelper.ThrowFormatException(DataType.Version);
}

if (Version.TryParse(versionString, out Version? result))
{
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,10 @@ public static void ValueFail()
[InlineData("2147483647.2147483647.2147483647.2147483647")]
[InlineData("\\u0032\\u0031\\u0034\\u0037\\u0034\\u0038\\u0033\\u0036\\u0034\\u0037\\u002e\\u0032\\u0031\\u0034\\u0037\\u0034\\u0038\\u0033\\u0036\\u0034\\u0037\\u002e\\u0032\\u0031\\u0034\\u0037\\u0034\\u0038\\u0033\\u0036\\u0034\\u0037\\u002e\\u0032\\u0031\\u0034\\u0037\\u0034\\u0038\\u0033\\u0036\\u0034\\u0037",
"2147483647.2147483647.2147483647.2147483647")]
[InlineData("1.+1", "1.1")] // Plus in components should work as before
[InlineData("1 .1", "1.1")] // Whitespace before dot should work as before
[InlineData("1. 1", "1.1")] // Whitespace after dot should work as before
[InlineData("1 . +1", "1.1")] // Combined whitespace and plus should work as before
public static void Version_Read_Success(string json, string? actual = null)
{
actual ??= json;
Expand All @@ -348,16 +352,17 @@ public static void Version_Read_Success(string json, string? actual = null)
[InlineData("")]
[InlineData(" ")]
[InlineData(" ")]
[InlineData(" 1.2.3.4")] // Leading whitespace should be rejected
[InlineData("1.2.3.4 ")] // Trailing whitespace should be rejected
[InlineData(" 1.2.3.4 ")] // Leading and trailing whitespace should be rejected
[InlineData("+1.1")] // Leading plus should be rejected
[InlineData("2147483648.2147483648.2147483648.2147483648")] //int.MaxValue + 1
[InlineData("2147483647.2147483647.2147483647.21474836477")] // Slightly bigger in size than max length of Version
[InlineData("-2147483648.-2147483648")]
[InlineData("-2147483648.-2147483648.-2147483648")]
[InlineData("-2147483648.-2147483648.-2147483648.-2147483648")]
[InlineData("1.-1")]
[InlineData("1")]
[InlineData(" 1.2.3.4")] //Valid but has leading whitespace
[InlineData("1.2.3.4 ")] //Valid but has trailing whitespace
[InlineData(" 1.2.3.4 ")] //Valid but has trailing and leading whitespaces
[InlineData("{}", false)]
[InlineData("[]", false)]
[InlineData("true", false)]
Expand Down
Loading