From f003a5ac8bd7dc0f0dcf6be876136191a7fef29d Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Thu, 12 Jan 2023 11:50:26 -0800 Subject: [PATCH 1/2] Fix issue #796: Request path segments gets double unescaped --- .../Models/AlternateKeyRepositoryInMemory.cs | 4 +-- .../Microsoft.AspNetCore.OData.xml | 30 ++++++++++--------- .../Routing/Template/KeySegmentTemplate.cs | 3 +- .../Template/ODataTemplateTranslateContext.cs | 10 ++++++- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/sample/ODataAlternateKeySample/Models/AlternateKeyRepositoryInMemory.cs b/sample/ODataAlternateKeySample/Models/AlternateKeyRepositoryInMemory.cs index 44fbf01ee..d2d6a686c 100644 --- a/sample/ODataAlternateKeySample/Models/AlternateKeyRepositoryInMemory.cs +++ b/sample/ODataAlternateKeySample/Models/AlternateKeyRepositoryInMemory.cs @@ -17,11 +17,11 @@ static AlternateKeyRepositoryInMemory() { // Customers var names = new[] { "Tom", "Jerry", "Mike", "Ben", "Sam", "Peter" }; - _customers = Enumerable.Range(1, 5).Select(e => new Customer + _customers = Enumerable.Range(1, 5).Select((e, i) => new Customer { Id = e, Name = names[e - 1], - SSN = "SSN-" + e + "-" + (100 + e), + SSN = i % 2 == 0 ? "SSN-" + e + "-" + (100 + e) : "SSN-%25-" + e + "-" + (100 + e), Titles = new string[] { "abc", null, "efg" } }).ToList(); diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 069a24b55..a955854e2 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1094,20 +1094,6 @@ The type to test. True if the type is a DateTime; false otherwise. - - - Determine if a type is a . - - The type to test. - True if the type is a DateOnly; false otherwise. - - - - Determine if a type is a . - - The type to test. - True if the type is a TimeOnly; false otherwise. - Determine if a type is a TimeSpan. @@ -14644,3 +14630,19 @@ +ummary> + The value segment. + + + + Gets the value segment. + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs b/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs index b139b7cf5..bb314cdf7 100644 --- a/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs +++ b/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs @@ -172,8 +172,9 @@ public override bool TryTranslate(ODataTemplateTranslateContext context) IEdmTypeReference edmType = keyProperty.Type; string strValue = rawValue as string; + + // rawValue from Request route values, it's unescaped. string newStrValue = context.GetParameterAliasOrSelf(strValue); - newStrValue = Uri.UnescapeDataString(newStrValue); if (newStrValue != strValue) { updateValues[templateName] = newStrValue; diff --git a/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs b/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs index 94e9fd482..5165c13b3 100644 --- a/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs +++ b/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs @@ -95,7 +95,15 @@ public ODataTemplateTranslateContext(HttpContext context, Endpoint endpoint, Rou /// The parameter alias name. public string GetParameterAliasOrSelf(string alias) { - return GetParameterAliasOrSelf(alias, new HashSet()); + var set = new HashSet(); + string value = GetParameterAliasOrSelf(alias, set); + if (set.Count > 1) + { + // Since it returns from query, should unescape the string. + return Uri.UnescapeDataString(value); + } + + return value; } private string GetParameterAliasOrSelf(string alias, ISet visited) From 30d2eec130b603aa7601b9c627f88d43b9576602 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Wed, 18 Jan 2023 19:19:31 -0800 Subject: [PATCH 2/2] Fixes #796: Request path segments gets double unescaped --- .../Common/StringExtensions.cs | 16 ++++++++ .../Microsoft.AspNetCore.OData.xml | 38 +++++++++++-------- .../Routing/Template/KeySegmentTemplate.cs | 4 +- .../Template/ODataTemplateTranslateContext.cs | 10 +---- .../Template/KeySegmentTemplateTests.cs | 2 +- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Common/StringExtensions.cs b/src/Microsoft.AspNetCore.OData/Common/StringExtensions.cs index fb4be3ae5..bca3db0b2 100644 --- a/src/Microsoft.AspNetCore.OData/Common/StringExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Common/StringExtensions.cs @@ -11,6 +11,22 @@ namespace Microsoft.AspNetCore.OData.Common { internal static class StringExtensions { + /// + /// Unescape Uri string for %2F + /// See details at: https://github.com/dotnet/aspnetcore/issues/14170#issuecomment-533342396 + /// + /// The Uri string. + /// Unescaped back slash Uri string. + public static string UnescapeBackSlashUriString(this string uriString) + { + if (uriString == null) + { + return null; + } + + return uriString.Replace("%2f", "%2F").Replace("%2F", "/"); + } + /// /// Normalize the http method. /// diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index a955854e2..2e2b82c65 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1047,6 +1047,14 @@ a fast getter. This method is more memory efficient than a dynamically compiled lambda, and about the same speed. + + + Unescape Uri string for %2F + See details at: https://github.com/dotnet/aspnetcore/issues/14170#issuecomment-533342396 + + The Uri string. + Unescaped back slash Uri string. + Normalize the http method. @@ -1094,6 +1102,20 @@ The type to test. True if the type is a DateTime; false otherwise. + + + Determine if a type is a . + + The type to test. + True if the type is a DateOnly; false otherwise. + + + + Determine if a type is a . + + The type to test. + True if the type is a TimeOnly; false otherwise. + Determine if a type is a TimeSpan. @@ -14630,19 +14652,3 @@ -ummary> - The value segment. - - - - Gets the value segment. - - - - - - - - - - diff --git a/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs b/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs index bb314cdf7..a390de3ac 100644 --- a/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs +++ b/src/Microsoft.AspNetCore.OData/Routing/Template/KeySegmentTemplate.cs @@ -173,8 +173,10 @@ public override bool TryTranslate(ODataTemplateTranslateContext context) IEdmTypeReference edmType = keyProperty.Type; string strValue = rawValue as string; - // rawValue from Request route values, it's unescaped. string newStrValue = context.GetParameterAliasOrSelf(strValue); + + // rawValue from Request route values, it's unescaped except the back-slash. + newStrValue = newStrValue.UnescapeBackSlashUriString(); if (newStrValue != strValue) { updateValues[templateName] = newStrValue; diff --git a/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs b/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs index 5165c13b3..94e9fd482 100644 --- a/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs +++ b/src/Microsoft.AspNetCore.OData/Routing/Template/ODataTemplateTranslateContext.cs @@ -95,15 +95,7 @@ public ODataTemplateTranslateContext(HttpContext context, Endpoint endpoint, Rou /// The parameter alias name. public string GetParameterAliasOrSelf(string alias) { - var set = new HashSet(); - string value = GetParameterAliasOrSelf(alias, set); - if (set.Count > 1) - { - // Since it returns from query, should unescape the string. - return Uri.UnescapeDataString(value); - } - - return value; + return GetParameterAliasOrSelf(alias, new HashSet()); } private string GetParameterAliasOrSelf(string alias, ISet visited) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Routing/Template/KeySegmentTemplateTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Routing/Template/KeySegmentTemplateTests.cs index d79a635fe..ab6fb288c 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Routing/Template/KeySegmentTemplateTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Routing/Template/KeySegmentTemplateTests.cs @@ -497,7 +497,7 @@ public void TryTranslateKeySegmentTemplate_WorksWithKeyValue_UsingEscapedString( EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); EdmEntitySet customers = container.AddEntitySet("Customers", customerType); model.AddElement(container); - RouteValueDictionary routeValueDictionary = new RouteValueDictionary(new { First = "'Zhang'", Last = "'Gan%2Fnng%23%20T'" }); + RouteValueDictionary routeValueDictionary = new RouteValueDictionary(new { First = "'Zhang'", Last = "'Gan%2Fnng# T'" }); IDictionary keys = new Dictionary { { "FirstName", "{first}" },