From 7119253e6fcb7f66c652a6bebd3aa07b7168e74a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 1 Mar 2023 15:17:08 +0100 Subject: [PATCH 1/5] Add allowlist of media hosts. --- .../Configuration/Models/ContentSettings.cs | 5 ++ .../Controllers/ImagesController.cs | 51 +++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs index 4014930a5c..a7abdf8a48 100644 --- a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs @@ -262,4 +262,9 @@ public class ContentSettings /// [DefaultValue(StaticDisallowedUploadFiles)] public string[] DisallowedUploadedFileExtensions { get; set; } = StaticDisallowedUploadFiles.Split(','); + + /// + /// Gets or sets the allowed external host for media. If empty only relative paths are allowed. + /// + public string[] AllowedMediaHosts { get; set; } = Array.Empty(); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 8f7901b2b4..e718696ae3 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -1,10 +1,14 @@ using System.Web; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -17,13 +21,31 @@ public class ImagesController : UmbracoAuthorizedApiController { private readonly IImageUrlGenerator _imageUrlGenerator; private readonly MediaFileManager _mediaFileManager; + private ContentSettings _contentSettings; + [Obsolete("Use non obsolete-constructor. Scheduled for removal in Umbraco 13.")] public ImagesController( MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) + : this(mediaFileManager, + imageUrlGenerator, + StaticServiceProvider.Instance.GetRequiredService>()) + { + + } + + [ActivatorUtilitiesConstructor] + public ImagesController( + MediaFileManager mediaFileManager, + IImageUrlGenerator imageUrlGenerator, + IOptionsMonitor contentSettingsMonitor) { _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; + _contentSettings = contentSettingsMonitor.CurrentValue; + + contentSettingsMonitor.OnChange(x => _contentSettings = x); + } /// @@ -58,7 +80,7 @@ public class ImagesController : UmbracoAuthorizedApiController var ext = Path.GetExtension(encodedImagePath); // check if imagePath is local to prevent open redirect - if (!Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) + if (!IsAllowed(encodedImagePath)) { return Unauthorized(); } @@ -90,12 +112,33 @@ public class ImagesController : UmbracoAuthorizedApiController ImageCropMode = ImageCropMode.Max, CacheBusterValue = rnd }); - if (Url.IsLocalUrl(imageUrl)) + + if (imageUrl is not null) { - return new LocalRedirectResult(imageUrl, false); + return new RedirectResult(imageUrl, false); } - return Unauthorized(); + return NotFound(); + } + + private bool IsAllowed(string encodedImagePath) + { + if(Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) + { + return true; + } + + var builder = new UriBuilder(encodedImagePath); + + foreach (var allowedMediaHost in _contentSettings.AllowedMediaHosts) + { + if (string.Equals(builder.Host, allowedMediaHost, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + + return false; } /// From 2538f946fc1af1f3aef2d13da00c8afcc821f629 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 7 Mar 2023 08:15:19 +0100 Subject: [PATCH 2/5] Check explicitly for MaintenanceResult in published request filter (#13911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LGTM 💪 --- .../Controllers/PublishedRequestFilterAttribute.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs b/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs index 10986d3882..9cd0a975a1 100644 --- a/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs +++ b/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Routing; namespace Umbraco.Cms.Web.Common.Controllers; @@ -15,9 +16,11 @@ internal class PublishedRequestFilterAttribute : ResultFilterAttribute /// public override void OnResultExecuting(ResultExecutingContext context) { - if (context.Result is not null) + if (context.Result is MaintenanceResult) { - // If the result is already set, we just skip the execution + // If the result is already set to a maintenance result we can't do anything + // Since the umbraco pipeline has not run. + // Fortunately we don't need to either. return; } From 220414114e51ad2ce63bbb256ce3bf234d756b9f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 7 Mar 2023 11:21:57 +0100 Subject: [PATCH 3/5] Fallback to use ActivatorUtilities to create controller from type, if it is not registered in DI (#13910) --- src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs index f0d0427438..cff5a589f6 100644 --- a/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs +++ b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs @@ -61,8 +61,9 @@ public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute if (controllerType != null) { - // Get the controller for the endpoint - var controller = httpContext.RequestServices.GetRequiredService(controllerType); + // Get the controller for the endpoint. We need to fallback to ActivatorUtilities if the controller is not registered in DI. + var controller = httpContext.RequestServices.GetService(controllerType) + ?? ActivatorUtilities.CreateInstance(httpContext.RequestServices, controllerType); // Try and find the content if this is a virtual page IPublishedContent? publishedContent = FindContent( From 84e20e74d3a3efbf93264f611d225d28ce9fa813 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 7 Mar 2023 11:31:15 +0100 Subject: [PATCH 4/5] Don't replace hyphen and underscore with empty string (#13896) Replace it with a space instead --- src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index 71c0929e39..e843d7954b 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -169,7 +169,10 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher var allLangs = _languageService.GetAllLanguages().Select(x => x.IsoCode.ToLowerInvariant()).ToList(); // the chars [*-_] in the query will mess everything up so let's remove those - query = Regex.Replace(query, "[\\*\\-_]", string.Empty); + // However we cannot just remove - and _ since these signify a space, so we instead replace them with that. + query = Regex.Replace(query, "[\\*]", string.Empty); + query = Regex.Replace(query, "[\\-_]", " "); + //check if text is surrounded by single or double quotes, if so, then exact match var surroundedByQuotes = Regex.IsMatch(query, "^\".*?\"$") From a8a8a7fea544e5d2a512be327b4cc745952d299f Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:14:23 +0100 Subject: [PATCH 5/5] Make Detailed telemetry the default (#13918) --- .../src/installer/steps/user.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js index 28a781a8ec..8b42bdbe27 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js @@ -3,7 +3,7 @@ angular.module("umbraco.install").controller("Umbraco.Install.UserController", f $scope.majorVersion = Umbraco.Sys.ServerVariables.application.version; $scope.passwordPattern = /.*/; $scope.installer.current.model.subscribeToNewsLetter = $scope.installer.current.model.subscribeToNewsLetter || false; - setTelemetryLevelAndDescription($scope.installer.current.model.telemetryIndex ?? 1); + setTelemetryLevelAndDescription($scope.installer.current.model.telemetryIndex ?? 2); if ($scope.installer.current.model.minNonAlphaNumericLength > 0) { var exp = "";