From 184cf9f26d1860dcf6aafd330aea5cd5de5f6675 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 31 May 2023 12:14:26 +0200 Subject: [PATCH] Fix URL encoded paths for the item endpoint (#14311) * Fix URL encoded paths for the item endpoint * Sanity check accept-language headers before accepting them --------- Co-authored-by: Elitsa --- .../Controllers/ByRouteContentApiController.cs | 16 +++++++++++++++- .../Services/RequestCultureService.cs | 16 +++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs index a506b0ce53..77bc86f807 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/ByRouteContentApiController.cs @@ -1,3 +1,4 @@ +using System.Net; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -5,6 +6,7 @@ using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; namespace Umbraco.Cms.Api.Delivery.Controllers; @@ -40,8 +42,20 @@ public class ByRouteContentApiController : ContentApiItemControllerBase [ProducesResponseType(typeof(IApiContentResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task ByRoute(string path = "/") + public async Task ByRoute(string path = "") { + // OpenAPI does not allow reserved chars as "in:path" parameters, so clients based on the Swagger JSON will URL + // encode the path. Normally, ASP.NET Core handles that encoding with an automatic decoding - apparently just not + // for forward slashes, for whatever reason... so we need to deal with those. Hopefully this will be addressed in + // an upcoming version of ASP.NET Core. + // See also https://github.com/dotnet/aspnetcore/issues/11544 + if (path.Contains("%2F", StringComparison.OrdinalIgnoreCase)) + { + path = WebUtility.UrlDecode(path); + } + + path = path.EnsureStartsWith("/"); + var contentRoute = _requestRoutingService.GetContentRoute(path); IPublishedContent? contentItem = ApiPublishedContentCache.GetByRoute(contentRoute); diff --git a/src/Umbraco.Cms.Api.Delivery/Services/RequestCultureService.cs b/src/Umbraco.Cms.Api.Delivery/Services/RequestCultureService.cs index 9fe8b79251..4c2d877701 100644 --- a/src/Umbraco.Cms.Api.Delivery/Services/RequestCultureService.cs +++ b/src/Umbraco.Cms.Api.Delivery/Services/RequestCultureService.cs @@ -1,11 +1,12 @@ -using Microsoft.AspNetCore.Http; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Cms.Api.Delivery.Services; -internal sealed class RequestCultureService : RequestHeaderHandler, IRequestCultureService +internal sealed partial class RequestCultureService : RequestHeaderHandler, IRequestCultureService { private readonly IVariationContextAccessor _variationContextAccessor; @@ -14,7 +15,11 @@ internal sealed class RequestCultureService : RequestHeaderHandler, IRequestCult _variationContextAccessor = variationContextAccessor; /// - public string? GetRequestedCulture() => GetHeaderValue(HeaderNames.AcceptLanguage); + public string? GetRequestedCulture() + { + var acceptLanguage = GetHeaderValue(HeaderNames.AcceptLanguage) ?? string.Empty; + return ValidLanguageHeaderRegex().IsMatch(acceptLanguage) ? acceptLanguage : null; + } /// public void SetRequestCulture(string culture) @@ -26,4 +31,9 @@ internal sealed class RequestCultureService : RequestHeaderHandler, IRequestCult _variationContextAccessor.VariationContext = new VariationContext(culture); } + + // at the time of writing we're introducing this to get rid of accept-language header values like "en-GB,en-US;q=0.9,en;q=0.8", + // so we don't want to be too restrictive in this regex - keep it simple for now. + [GeneratedRegex(@"^[\w-]*$")] + private static partial Regex ValidLanguageHeaderRegex(); }