Skip to content

Commit 0e6fd01

Browse files
committed
Update invalid json test value, fix issue in Base64Url validation
1 parent 9efa0b9 commit 0e6fd01

File tree

6 files changed

+58
-35
lines changed

6 files changed

+58
-35
lines changed

src/libraries/System.Memory/tests/Base64/Base64DecoderUnitTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ public void BasicDecodingWithNonZeroUnusedBits(string inputString)
285285
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
286286

287287
Assert.False(Base64.IsValid(inputString));
288-
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int _));
288+
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int _, out int _));
289+
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(source, out int _));
289290
}
290291

291292
[Theory]

src/libraries/System.Memory/tests/Base64Url/Base64UrlDecoderUnitTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,12 @@ public void BasicDecodingWithFinalBlockTrueKnownInputDone(string inputString, in
300300
[InlineData("AQIDBAUHCAkKCwwNDxD")]
301301
public void BasicDecodingWithNonZeroUnusedBits(string inputString)
302302
{
303-
Span<byte> source = Encoding.ASCII.GetBytes(inputString);
304-
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
303+
byte[] source = Encoding.ASCII.GetBytes(inputString);
304+
Span<byte> decodedBytes = new byte[Base64Url.GetMaxDecodedLength(source.Length)];
305305

306-
Assert.False(Base64.IsValid(inputString));
307-
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int _));
306+
Assert.False(Base64Url.IsValid(inputString));
307+
Assert.Equal(OperationStatus.InvalidData, Base64Url.DecodeFromUtf8(source, decodedBytes, out int _, out int _));
308+
Assert.Throws<FormatException>(() => Base64Url.DecodeFromUtf8InPlace(source));
308309
}
309310

310311
[Theory]

src/libraries/System.Memory/tests/Base64Url/Base64UrlValidationUnitTests.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public void BasicValidationBytes()
3939

4040
Span<byte> source = new byte[numBytes];
4141
Base64TestHelper.InitializeUrlDecodableBytes(source, numBytes);
42+
source[numBytes - 1] = 65; // make sure unused bits set 0
4243

4344
Assert.True(Base64Url.IsValid(source));
4445
Assert.True(Base64Url.IsValid(source, out int decodedLength));
@@ -60,6 +61,7 @@ public void BasicValidationChars()
6061

6162
Span<byte> source = new byte[numBytes];
6263
Base64TestHelper.InitializeUrlDecodableBytes(source, numBytes);
64+
source[numBytes - 1] = 65; // make sure unused bits set 0
6365
Span<char> chars = source
6466
.ToArray()
6567
.Select(Convert.ToChar)
@@ -259,8 +261,8 @@ public void ValidateWithPaddingReturnsCorrectCountChars(string utf8WithByteToBeI
259261
}
260262

261263
[Theory]
262-
[InlineData("YWJ", true, 2)]
263-
[InlineData("YW", true, 1)]
264+
[InlineData("YWI", true, 2)]
265+
[InlineData("YQ", true, 1)]
264266
[InlineData("Y", false, 0)]
265267
public void SmallSizeBytes(string utf8Text, bool isValid, int expectedDecodedLength)
266268
{
@@ -272,8 +274,8 @@ public void SmallSizeBytes(string utf8Text, bool isValid, int expectedDecodedLen
272274
}
273275

274276
[Theory]
275-
[InlineData("YWJ", true, 2)]
276-
[InlineData("YW", true, 1)]
277+
[InlineData("YWI", true, 2)]
278+
[InlineData("YQ", true, 1)]
277279
[InlineData("Y", false, 0)]
278280
public void SmallSizeChars(string utf8Text, bool isValid, int expectedDecodedLength)
279281
{

src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64ValidatorHelper.cs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,11 @@ internal static bool IsValid<T, TBase64Validatable>(TBase64Validatable validatab
108108
}
109109
}
110110

111-
int decoded = validatable.DecodeValue(lastChar);
112-
if (paddingCount == 1 && (decoded & 0x03) != 0 ||
113-
paddingCount == 2 && (decoded & 0x0F) != 0)
114-
{
115-
// unused lower bits are not 0, reject input
116-
decodedLength = 0;
117-
return false;
118-
}
119-
120111
length += paddingCount;
121112
break;
122113
}
123114

124-
if (!validatable.ValidateAndDecodeLength(length, paddingCount, out decodedLength))
115+
if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
125116
{
126117
goto Fail;
127118
}
@@ -141,19 +132,21 @@ internal interface IBase64Validatable<T>
141132
{
142133
#if NET
143134
int IndexOfAnyExcept(ReadOnlySpan<T> span);
144-
#endif
135+
#else
145136
int DecodeValue(T value);
137+
#endif
146138
bool IsWhiteSpace(T value);
147139
bool IsEncodingPad(T value);
148-
bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength);
140+
bool ValidateAndDecodeLength(T lastChar, int length, int paddingCount, out int decodedLength);
149141
}
150142

151143
internal readonly struct Base64CharValidatable : IBase64Validatable<char>
152144
{
153145
#if NET
154146
private static readonly SearchValues<char> s_validBase64Chars = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
147+
155148
public int IndexOfAnyExcept(ReadOnlySpan<char> span) => span.IndexOfAnyExcept(s_validBase64Chars);
156-
#endif
149+
#else
157150
[MethodImpl(MethodImplOptions.AggressiveInlining)]
158151
public int DecodeValue(char value)
159152
{
@@ -165,26 +158,38 @@ public int DecodeValue(char value)
165158

166159
return default(Base64DecoderByte).DecodingMap[value];
167160
}
161+
#endif
168162
public bool IsWhiteSpace(char value) => Base64Helper.IsWhiteSpace(value);
169163
public bool IsEncodingPad(char value) => value == EncodingPad;
170-
public bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength) =>
171-
default(Base64ByteValidatable).ValidateAndDecodeLength(length, paddingCount, out decodedLength);
164+
public bool ValidateAndDecodeLength(char lastChar, int length, int paddingCount, out int decodedLength) =>
165+
default(Base64ByteValidatable).ValidateAndDecodeLength((byte)lastChar, length, paddingCount, out decodedLength);
172166
}
173167

174168
internal readonly struct Base64ByteValidatable : IBase64Validatable<byte>
175169
{
176170
#if NET
177171
private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(default(Base64EncoderByte).EncodingMap);
172+
178173
public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
179-
#endif
174+
#else
180175
public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
176+
#endif
181177
public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
182178
public bool IsEncodingPad(byte value) => value == EncodingPad;
183179
[MethodImpl(MethodImplOptions.AggressiveInlining)]
184-
public bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength)
180+
public bool ValidateAndDecodeLength(byte lastChar, int length, int paddingCount, out int decodedLength)
185181
{
186182
if (length % 4 == 0)
187183
{
184+
int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
185+
if (paddingCount == 1 && (decoded & 0x03) != 0 ||
186+
paddingCount == 2 && (decoded & 0x0F) != 0)
187+
{
188+
// unused lower bits are not 0, reject input
189+
decodedLength = 0;
190+
return false;
191+
}
192+
188193
// Remove padding to get exact length.
189194
decodedLength = (int)((uint)length / 4 * 3) - paddingCount;
190195
return true;

src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Url/Base64UrlValidator.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Runtime.CompilerServices;
5+
using static System.Buffers.Text.Base64Helper;
56

67
namespace System.Buffers.Text
78
{
@@ -57,8 +58,9 @@ public static bool IsValid(ReadOnlySpan<byte> utf8Base64UrlText, out int decoded
5758
{
5859
#if NET
5960
private static readonly SearchValues<char> s_validBase64UrlChars = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
61+
6062
public int IndexOfAnyExcept(ReadOnlySpan<char> span) => span.IndexOfAnyExcept(s_validBase64UrlChars);
61-
#endif
63+
#else
6264
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6365
public int DecodeValue(char value)
6466
{
@@ -70,24 +72,27 @@ public int DecodeValue(char value)
7072

7173
return default(Base64UrlDecoderByte).DecodingMap[value];
7274
}
75+
#endif
7376
public bool IsWhiteSpace(char value) => Base64Helper.IsWhiteSpace(value);
7477
public bool IsEncodingPad(char value) => value == Base64Helper.EncodingPad || value == UrlEncodingPad;
7578
[MethodImpl(MethodImplOptions.AggressiveInlining)]
76-
public bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength) =>
77-
default(Base64UrlByteValidatable).ValidateAndDecodeLength(length, paddingCount, out decodedLength);
79+
public bool ValidateAndDecodeLength(char lastChar, int length, int paddingCount, out int decodedLength) =>
80+
default(Base64UrlByteValidatable).ValidateAndDecodeLength((byte)lastChar, length, paddingCount, out decodedLength);
7881
}
7982

8083
private readonly struct Base64UrlByteValidatable : Base64Helper.IBase64Validatable<byte>
8184
{
8285
#if NET
8386
private static readonly SearchValues<byte> s_validBase64UrlChars = SearchValues.Create(default(Base64UrlEncoderByte).EncodingMap);
87+
8488
public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64UrlChars);
85-
#endif
89+
#else
8690
public int DecodeValue(byte value) => default(Base64UrlDecoderByte).DecodingMap[value];
91+
#endif
8792
public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
8893
public bool IsEncodingPad(byte value) => value == Base64Helper.EncodingPad || value == UrlEncodingPad;
8994
[MethodImpl(MethodImplOptions.AggressiveInlining)]
90-
public bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength)
95+
public bool ValidateAndDecodeLength(byte lastChar, int length, int paddingCount, out int decodedLength)
9196
{
9297
// Padding is optional for Base64Url, so need to account remainder. If remainder is 1, then it's invalid.
9398
#if NET
@@ -109,6 +114,15 @@ public bool ValidateAndDecodeLength(int length, int paddingCount, out int decode
109114

110115
decodedLength = (length >> 2) * 3 + (remainder > 0 ? remainder - 1 : 0) - paddingCount;
111116
#endif
117+
int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
118+
if ((remainder == 3 || paddingCount == 1) && (decoded & 0x03) != 0 ||
119+
(remainder == 2 || paddingCount == 2) && (decoded & 0x0F) != 0)
120+
{
121+
// unused lower bits are not 0, reject input
122+
decodedLength = 0;
123+
return false;
124+
}
125+
112126
return true;
113127
}
114128
}

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonBase64TestData.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal class JsonBase64TestData
99
{
1010
public static IEnumerable<object[]> ValidBase64Tests()
1111
{
12-
yield return new object[] { "\"ABC=\"" };
12+
yield return new object[] { "\"ABA=\"" };
1313
yield return new object[] { "\"AB+D\"" };
1414
yield return new object[] { "\"ABCD\"" };
1515
yield return new object[] { "\"ABC/\"" };
@@ -19,9 +19,9 @@ public static IEnumerable<object[]> ValidBase64Tests()
1919

2020
public static IEnumerable<object[]> InvalidBase64Tests()
2121
{
22-
yield return new object[] { "\"ABC===\"" };
23-
yield return new object[] { "\"ABC\"" };
24-
yield return new object[] { "\"ABC!\"" };
22+
yield return new object[] { "\"ABA===\"" };
23+
yield return new object[] { "\"ABA\"" };
24+
yield return new object[] { "\"ABA!\"" };
2525
yield return new object[] { GenerateRandomInvalidLargeString(includeEscapedCharacter: true) };
2626
yield return new object[] { GenerateRandomInvalidLargeString(includeEscapedCharacter: false) };
2727
}

0 commit comments

Comments
 (0)