Skip to content

Commit a9890cb

Browse files
authored
Merge pull request #2333 from DuendeSoftware/jmdc/7.4/querystring-regression
Fixed a regression where the + character was not treated as a space in query params
2 parents 8057e0f + 5b16c9a commit a9890cb

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

identity-server/src/Shared/Extensions/StringExtensions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,15 +260,19 @@ public static NameValueCollection ReadQueryStringAsNameValueCollection(this stri
260260
}
261261

262262
var kvp = pair.Split('=', 2);
263-
var key = Uri.UnescapeDataString(kvp[0]);
263+
var key = kvp[0];
264264
var value = kvp.Length > 1 ? kvp[1] : null;
265-
var unescaped = value.IsPresent() ? Uri.UnescapeDataString(kvp[1]) : null;
266-
collection.Add(key, unescaped);
265+
if (value.IsPresent())
266+
{
267+
collection.Add(Decode(key), Decode(value));
268+
}
267269
}
268270

269271
return collection;
270272
}
271273

274+
private static string Decode(string value) => Uri.UnescapeDataString(value.Replace('+', ' '));
275+
272276
private static string? QueryString(this string url)
273277
{
274278
var queryStringStart = url.IndexOf('?', StringComparison.InvariantCulture);

identity-server/test/IdentityServer.UnitTests/Extensions/StringExtensionsTests.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44

55
using Duende.IdentityServer.Extensions;
6+
using Microsoft.AspNetCore.WebUtilities;
67

78
namespace UnitTests.Extensions;
89

9-
public class StringExtensionsTests
10+
public class StringExtensionsTests()
1011
{
1112
private const string Category = "StringExtensions Tests";
1213

@@ -230,6 +231,35 @@ public void ReadQueryStringAsNameValueCollection_should_decode_urlencoded_values
230231
nvc["baz"].ShouldBe("qux+test");
231232
}
232233

234+
private IEnumerable<string> AllCharactersAndEncodings()
235+
{
236+
for (var i = 0; i < 256; i++)
237+
{
238+
var c = Convert.ToChar(i);
239+
var s = Convert.ToString(c);
240+
yield return s;
241+
yield return Uri.EscapeDataString(s);
242+
}
243+
}
244+
245+
[Fact]
246+
public void ReadQueryStringAsNameValueCollection_should_decode_urlencoded_values_identically_to_QueryHelpersParseNullableQuery()
247+
{
248+
foreach (var c in AllCharactersAndEncodings())
249+
{
250+
var url = $"https://example.com?foo{c}bar=baz{c}quux";
251+
252+
var queryIndex = url.IndexOf('?');
253+
var query = url.Substring(queryIndex + 1);
254+
var oldParseResult = QueryHelpers.ParseNullableQuery(query);
255+
oldParseResult.ShouldNotBeNull();
256+
var oldNvc = oldParseResult.AsNameValueCollection();
257+
var newNvc = url.ReadQueryStringAsNameValueCollection();
258+
259+
newNvc.ShouldBeEquivalentTo(oldNvc, "Failure for character or encoding:" + c);
260+
}
261+
}
262+
233263
[Fact]
234264
[Trait("Category", Category)]
235265
public void ReadQueryStringAsNameValueCollection_should_handle_duplicate_keys()

0 commit comments

Comments
 (0)