From d752853d8955a92b527550a82e340c4986a5b817 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 10 Dec 2023 19:18:32 +0100 Subject: [PATCH 1/6] Fix disposed DbContext issue (#15090) (#15410) --- ...izeMemberApplicationNotificationHandler.cs | 24 ++++++---- ...mbracoEFCoreServiceCollectionExtensions.cs | 25 ---------- .../UmbracoPooledDbContextFactory.cs | 47 ------------------- .../UmbracoDbContext.cs | 5 ++ 4 files changed, 20 insertions(+), 81 deletions(-) delete mode 100644 src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs diff --git a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs index 242fd47857..06b6472506 100644 --- a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs +++ b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; @@ -11,20 +12,20 @@ namespace Umbraco.Cms.Api.Delivery.Handlers; internal sealed class InitializeMemberApplicationNotificationHandler : INotificationAsyncHandler { - private readonly IMemberApplicationManager _memberApplicationManager; private readonly IRuntimeState _runtimeState; private readonly ILogger _logger; private readonly DeliveryApiSettings _deliveryApiSettings; + private readonly IServiceScopeFactory _serviceScopeFactory; public InitializeMemberApplicationNotificationHandler( - IMemberApplicationManager memberApplicationManager, IRuntimeState runtimeState, IOptions deliveryApiSettings, - ILogger logger) + ILogger logger, + IServiceScopeFactory serviceScopeFactory) { - _memberApplicationManager = memberApplicationManager; _runtimeState = runtimeState; _logger = logger; + _serviceScopeFactory = serviceScopeFactory; _deliveryApiSettings = deliveryApiSettings.Value; } @@ -35,26 +36,31 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica return; } + // we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during + // install that is not allowed (no connection string means no DbContext) + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService(); + if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true) { - await _memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); + await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); return; } if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false) { - await _memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); + await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); return; } if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any() && ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false) { - await _memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); + await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken); return; } - await _memberApplicationManager.EnsureMemberApplicationAsync( + await memberApplicationManager.EnsureMemberApplicationAsync( _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls, _deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls, cancellationToken); diff --git a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs index 3d7e01a0ad..7694f83dd2 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs @@ -1,14 +1,10 @@ -using System; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Serilog; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DistributedLocking; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Persistence.EFCore.Factories; using Umbraco.Cms.Persistence.EFCore.Locking; using Umbraco.Cms.Persistence.EFCore.Scoping; @@ -22,13 +18,6 @@ public static class UmbracoEFCoreServiceCollectionExtensions public static IServiceCollection AddUmbracoEFCoreContext(this IServiceCollection services, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null) where T : DbContext { - var optionsBuilder = new DbContextOptionsBuilder(); - services.TryAddSingleton>( - sp => - { - SetupDbContext(defaultEFCoreOptionsAction, sp, optionsBuilder); - return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); - }); services.AddPooledDbContextFactory((provider, builder) => SetupDbContext(defaultEFCoreOptionsAction, provider, builder)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); @@ -52,13 +41,6 @@ public static class UmbracoEFCoreServiceCollectionExtensions connectionString = connectionString.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory); } - var optionsBuilder = new DbContextOptionsBuilder(); - services.TryAddSingleton>( - sp => - { - defaultEFCoreOptionsAction?.Invoke(optionsBuilder, providerName, connectionString); - return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); - }); services.AddPooledDbContextFactory(options => defaultEFCoreOptionsAction?.Invoke(options, providerName, connectionString)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); @@ -99,13 +81,6 @@ public static class UmbracoEFCoreServiceCollectionExtensions { optionsAction ??= (sp, options) => { }; - var optionsBuilder = new DbContextOptionsBuilder(); - - services.TryAddSingleton>(sp => - { - optionsAction.Invoke(sp, optionsBuilder); - return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); - }); services.AddPooledDbContextFactory(optionsAction); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); diff --git a/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs b/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs deleted file mode 100644 index de0f7db200..0000000000 --- a/src/Umbraco.Cms.Persistence.EFCore/Factories/UmbracoPooledDbContextFactory.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Cms.Persistence.EFCore.Factories; - -/// -internal class UmbracoPooledDbContextFactory : PooledDbContextFactory - where TContext : DbContext -{ - private readonly IRuntimeState _runtimeState; - private readonly DbContextOptions _options; - - /// - public UmbracoPooledDbContextFactory(IRuntimeState runtimeState, DbContextOptions options, int poolSize = 1024 /*DbContextPool.DefaultPoolSize*/) : base(options, poolSize) - { - _runtimeState = runtimeState; - _options = options; - } - - /// - public override TContext CreateDbContext() - { - if (_runtimeState.Level == RuntimeLevel.Run) - { - return base.CreateDbContext(); - } - else - { - return (TContext?)Activator.CreateInstance(typeof(TContext), _options) ?? throw new InvalidOperationException("Unable to create DbContext"); - } - } - - /// - public override async Task CreateDbContextAsync(CancellationToken cancellationToken = default) - { - if (_runtimeState.Level == RuntimeLevel.Run) - { - return await base.CreateDbContextAsync(cancellationToken); - } - else - { - return (TContext?)Activator.CreateInstance(typeof(TContext), _options) ?? throw new InvalidOperationException("Unable to create DbContext"); - } - } -} diff --git a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs index a27630bd34..3df757ee15 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs @@ -1,3 +1,4 @@ +using System.Configuration; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.DependencyInjection; @@ -55,6 +56,10 @@ public class UmbracoDbContext : DbContext { ILogger logger = StaticServiceProvider.Instance.GetRequiredService>(); logger.LogCritical("No connection string was found, cannot setup Umbraco EF Core context"); + + // we're throwing an exception here to make it abundantly clear that one should never utilize (or have a + // dependency on) the DbContext before the connection string has been initialized by the installer. + throw new ConfigurationErrorsException("No connection string was found, cannot setup Umbraco EF Core context"); } IEnumerable migrationProviders = StaticServiceProvider.Instance.GetServices(); From 3a697d90fccd6831a83190f8fee95b5c3091928d Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Tue, 21 Nov 2023 09:49:04 +0100 Subject: [PATCH 2/6] Suppress flow when queueing background threads --- .../Examine/ExamineIndexRebuilder.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs index 5394cdc275..2a89327a33 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -66,7 +66,17 @@ public class ExamineIndexRebuilder : IIndexRebuilder _logger.LogInformation("Starting async background thread for rebuilding index {indexName}.", indexName); _backgroundTaskQueue.QueueBackgroundWorkItem( - cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken))); + cancellationToken => + { + // Do not flow AsyncLocal to the child thread + using (ExecutionContext.SuppressFlow()) + { + Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken)); + + // immediately return so the queue isn't waiting. + return Task.CompletedTask; + } + }); } else { @@ -96,12 +106,16 @@ public class ExamineIndexRebuilder : IIndexRebuilder _backgroundTaskQueue.QueueBackgroundWorkItem( cancellationToken => { - // This is a fire/forget task spawned by the background thread queue (which means we - // don't need to worry about ExecutionContext flowing). - Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken)); + // Do not flow AsyncLocal to the child thread + using (ExecutionContext.SuppressFlow()) + { + // This is a fire/forget task spawned by the background thread queue (which means we + // don't need to worry about ExecutionContext flowing). + Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken)); - // immediately return so the queue isn't waiting. - return Task.CompletedTask; + // immediately return so the queue isn't waiting. + return Task.CompletedTask; + } }); } else From 50a107f88561f7a1b27cc24ebcb247ed5ae76110 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:59:03 +0100 Subject: [PATCH 3/6] Merge pull request from GHSA-6324-52pr-h4p5 Co-authored-by: Zeegaan --- .../Repositories/Implement/CreatedPackageSchemaRepository.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs index 61f0fe126d..8152eb70e9 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs @@ -311,7 +311,12 @@ public class CreatedPackageSchemaRepository : ICreatedPackagesRepository definition.Name.Replace(' ', '_'))); Directory.CreateDirectory(directoryName); + var expectedRoot = _hostingEnvironment.MapPathContentRoot(_createdPackagesFolderPath); var finalPackagePath = Path.Combine(directoryName, fileName); + if (finalPackagePath.StartsWith(expectedRoot) == false) + { + throw new IOException("Invalid path due to the package name"); + } // Clean existing files foreach (var packagePath in new[] { definition.PackagePath, finalPackagePath }) From 3331788c5cf35c8591f92ebcdd958af2db6a4eec Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:59:35 +0100 Subject: [PATCH 4/6] Merge pull request from GHSA-8qp8-9rpw-j46c * Ensure that missing access rules do not break the site (#15081) (cherry picked from commit 67771450797eb2c72449ddcabfc38d5ebf99c7f3) * Added Exception handling and replicated error and info message * Update auth.resource.js Fixed the message * Changed Delay introduction to early phase to avoid repeating code. --------- Co-authored-by: Kenn Jacobsen Co-authored-by: jey Co-authored-by: Jey --- .../Persistence/Dtos/AccessDto.cs | 2 +- .../Controllers/AuthenticationController.cs | 15 ++++++++++++--- .../src/common/resources/auth.resource.js | 4 ++-- .../views/components/application/umb-login.html | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs index 354083dfa8..0821232826 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs @@ -37,5 +37,5 @@ internal class AccessDto [ResultColumn] [Reference(ReferenceType.Many, ReferenceMemberName = "AccessId")] - public List Rules { get; set; } = null!; + public List Rules { get; set; } = new(); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index beb8787c79..19cc4ae70f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -404,6 +404,9 @@ public class AuthenticationController : UmbracoApiControllerBase } BackOfficeIdentityUser? identityUser = await _userManager.FindByEmailAsync(model.Email); + + await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); // To randomize response time preventing user enumeration + if (identityUser != null) { IUser? user = _userService.GetByEmail(model.Email); @@ -424,14 +427,20 @@ public class AuthenticationController : UmbracoApiControllerBase var mailMessage = new EmailMessage(from, user.Email, subject, message, true); - await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.PasswordReset, true); + try + { + await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.PasswordReset, true); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error sending email, please check your SMTP configuration: {ErrorMessage}", ex.Message); + return Ok(); + } _userManager.NotifyForgotPasswordRequested(User, user.Id.ToString()); } } - await Task.Delay(RandomNumberGenerator.GetInt32(400, 2500)); - return Ok(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js index e09718176c..7b0a10cf31 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js @@ -28,7 +28,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { * }); * * @returns {Promise} resourcePromise object - * + * */ get2FAProviders: function () { @@ -203,7 +203,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) { "PostRequestPasswordReset"), { email: email }), - 'Request password reset failed for email ' + email); + 'An email with password reset instructions will be sent to the specified address if it matched our records'); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 4a9dc85865..69dc038cb8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -158,7 +158,7 @@
- +
From 42ae37e502c78452c3a8429bd16e23be9886dac4 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:59:59 +0100 Subject: [PATCH 5/6] Merge pull request from GHSA-cfr5-7p54-4qg8 * Apply authorization policies to controllers * Return bad request if we urltracking is disabled --------- Co-authored-by: Zeegaan --- .../Controllers/AnalyticsController.cs | 3 +++ .../Controllers/LanguageController.cs | 1 + .../PublishedSnapshotCacheStatusController.cs | 3 +++ .../Controllers/RedirectUrlManagementController.cs | 13 +++++++++++-- .../Controllers/StylesheetController.cs | 3 +++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs b/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs index 1820f2b5e0..b9980308b9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AnalyticsController.cs @@ -1,9 +1,12 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Web.BackOffice.Controllers; +[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class AnalyticsController : UmbracoAuthorizedJsonController { private readonly IMetricsConsentService _metricsConsentService; diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs index 4cb7dc52fc..cef2352ab4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs @@ -18,6 +18,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers; /// Backoffice controller supporting the dashboard for language administration. /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] +[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class LanguageController : UmbracoAuthorizedJsonController { private readonly ILocalizationService _localizationService; diff --git a/src/Umbraco.Web.BackOffice/Controllers/PublishedSnapshotCacheStatusController.cs b/src/Umbraco.Web.BackOffice/Controllers/PublishedSnapshotCacheStatusController.cs index c8c391d990..91cd16a0f6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PublishedSnapshotCacheStatusController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PublishedSnapshotCacheStatusController.cs @@ -1,13 +1,16 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Controllers; [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] +[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class PublishedSnapshotCacheStatusController : UmbracoAuthorizedApiController { private readonly DistributedCache _distributedCache; diff --git a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs index d87398d574..34cd759dbb 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System.Security; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -14,11 +15,13 @@ using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Controllers; [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] +[Authorize(Policy = AuthorizationPolicies.SectionAccessContent)] public class RedirectUrlManagementController : UmbracoAuthorizedApiController { private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; @@ -45,6 +48,8 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController _configManipulator = configManipulator ?? throw new ArgumentNullException(nameof(configManipulator)); } + private bool IsEnabled => _webRoutingSettings.CurrentValue.DisableRedirectUrlTracking == false; + /// /// Returns true/false of whether redirect tracking is enabled or not /// @@ -52,9 +57,8 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController [HttpGet] public IActionResult GetEnableState() { - var enabled = _webRoutingSettings.CurrentValue.DisableRedirectUrlTracking == false; var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.IsAdmin() ?? false; - return Ok(new { enabled, userIsAdmin }); + return Ok(new { enabled = IsEnabled, userIsAdmin }); } //add paging @@ -104,6 +108,11 @@ public class RedirectUrlManagementController : UmbracoAuthorizedApiController [HttpPost] public IActionResult DeleteRedirectUrl(Guid id) { + if (IsEnabled is false) + { + return BadRequest("Redirect URL tracking is disabled, and therefore no URLs can be deleted."); + } + _redirectUrlService.Delete(id); return Ok(); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs index 32adfcfdf6..616edfa04f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs @@ -1,8 +1,10 @@ +using Microsoft.AspNetCore.Authorization; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Extensions; using Stylesheet = Umbraco.Cms.Core.Models.ContentEditing.Stylesheet; @@ -12,6 +14,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers; /// The API controller used for retrieving available stylesheets /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] +[Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)] public class StylesheetController : UmbracoAuthorizedJsonController { private readonly IFileService _fileService; From 237e4e56b2b1a9163f9cee2637ba01cf62239f6a Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:00:23 +0100 Subject: [PATCH 6/6] Merge pull request from GHSA-v98m-398x-269r Co-authored-by: kjac --- .../src/views/common/login.controller.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js index 13ca4cb193..ec039dfdd7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js @@ -11,11 +11,12 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events //check if there's a returnPath query string, if so redirect to it var locationObj = $location.search(); if (locationObj.returnPath) { - // decodeURIComponent(...) does not play nice with OAuth redirect URLs, so until we have a - // dedicated login screen for the new back-office, we need to hardcode this exception - path = locationObj.returnPath.indexOf("/security/back-office/authorize") > 0 - ? locationObj.returnPath - : decodeURIComponent(locationObj.returnPath); + // ensure that the returnPath is a valid URL under the current origin (prevents DOM-XSS among other things) + const returnPath = decodeURIComponent(locationObj.returnPath); + const url = new URL(returnPath, window.location.origin); + if (url.origin === window.location.origin) { + path = returnPath; + } } // Ensure path is not absolute