From b5544aa52052de6a783e85ff6ef89dfc3e98b613 Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Tue, 21 Nov 2023 09:49:04 +0100 Subject: [PATCH 1/5] 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 a1c70d0ec3..ced539ebc0 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 { @@ -93,12 +103,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 13cc320f1911d3175453dc925635930f6d4712fa 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 2/5] Merge pull request from GHSA-6324-52pr-h4p5 * Bump version * Fix https://github.com/umbraco/Umbraco-CMS/security/advisories/GHSA-6324-52pr-h4p5 * Fix https://github.com/umbraco/Umbraco-CMS/security/advisories/GHSA-6324-52pr-h4p5 --------- Co-authored-by: Bjarke Berg 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 9f921266ca..093fadd8fe 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs @@ -266,7 +266,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 be5a740c960a39b33d3506a52a23d6e1d4474b93 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 3/5] Merge pull request from GHSA-8qp8-9rpw-j46c * 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: jey Co-authored-by: Jey --- .../Controllers/AuthenticationController.cs | 15 ++++++++++++--- .../src/common/resources/auth.resource.js | 4 ++-- .../views/components/application/umb-login.html | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 4892f53012..4ec726ae09 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -402,6 +402,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); @@ -422,14 +425,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 cdd4d2a00078f2bdec8959038f57ea6fbe7a8cf1 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 4/5] Merge pull request from GHSA-cfr5-7p54-4qg8 * Bump version * Apply authorization policies to controllers * Return bad request if we urltracking is disabled * Apply authorization policies to controllers * Return bad request if we urltracking is disabled --------- Co-authored-by: Bjarke Berg 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 bb515b61fc..aaf067022c 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 c4d7d47d87..08057d82fa 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 4a7ad4a562e7f07641d4a33cd246509f4ab59cd8 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 11 Dec 2023 14:00:23 +0100 Subject: [PATCH 5/5] Merge pull request from GHSA-v98m-398x-269r --- .../src/views/common/login.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 5827f7e530..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,7 +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) { - path = 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