From c0d19ce8f96b33291ff632019de181783ddfb9c5 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 3 Mar 2026 12:19:34 +0100 Subject: [PATCH 1/5] Added healthcheck for Imaging:HMACSecretKey configuration setting. --- src/Umbraco.Core/Constants-HealthChecks.cs | 6 ++ .../EmbeddedResources/Lang/en.xml | 5 ++ .../EmbeddedResources/Lang/en_us.xml | 5 ++ .../Security/ImagingHMACSecretKeyCheck.cs | 59 ++++++++++++++ .../ImagingHMACSecretKeyCheckTests.cs | 77 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs diff --git a/src/Umbraco.Core/Constants-HealthChecks.cs b/src/Umbraco.Core/Constants-HealthChecks.cs index 118738bd9e9c..e0b637f39d86 100644 --- a/src/Umbraco.Core/Constants-HealthChecks.cs +++ b/src/Umbraco.Core/Constants-HealthChecks.cs @@ -126,6 +126,12 @@ public static class Security /// public const string CspHeaderCheck = "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP"; + /// + /// The documentation link for the imaging HMAC secret key check. + /// + public const string ImagingHMACSecretKeyCheck = + "https://docs.umbraco.com/umbraco-cms/reference/configuration/imagingsettings"; + /// /// Contains documentation links for HTTPS health checks. /// diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 6ff466663419..d1d6dd817c5f 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -509,6 +509,11 @@ %0%.]]> %0%.]]> + The HMAC secret key for image URL signing is configured. + + Umbraco:CMS:Imaging:HMACSecretKey to prevent unauthorized image manipulation requests.]]> +

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
Umbraco Health Check Status: %0% diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 2514454ba14b..955d2ef7d45c 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -498,6 +498,11 @@ %0%.]]> %0%.]]> + The HMAC secret key for image URL signing is configured. + + Umbraco:CMS:Imaging:HMACSecretKey to prevent unauthorized image manipulation requests.]]> +

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
Umbraco Health Check Status: %0% diff --git a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs new file mode 100644 index 000000000000..e7401c278c80 --- /dev/null +++ b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs @@ -0,0 +1,59 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.HealthChecks.Checks.Security; + +/// +/// Health check for the HMAC secret key used to authenticate image manipulation URLs. +/// +[HealthCheck( + "A3E3A6C8-3D42-4F6E-B285-9F6A5C7E1A14", + "Imaging HMAC Secret Key", + Description = "Verifies that an HMAC secret key is configured to authenticate and protect image manipulation URLs.", + Group = "Security")] +public class ImagingHMACSecretKeyCheck : HealthCheck +{ + private readonly ILocalizedTextService _textService; + private readonly IOptionsMonitor _imagingSettings; + + /// + /// Initializes a new instance of the class. + /// + /// The localized text service. + /// The imaging settings monitor. + public ImagingHMACSecretKeyCheck( + ILocalizedTextService textService, + IOptionsMonitor imagingSettings) + { + _textService = textService; + _imagingSettings = imagingSettings; + } + + /// + public override Task> GetStatusAsync() + { + bool isConfigured = _imagingSettings.CurrentValue.HMACSecretKey.Length > 0; + + HealthCheckStatus status = isConfigured + ? new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckSuccessMessage")) + { + ResultType = StatusResultType.Success, + } + : new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckWarningMessage")) + { + ResultType = StatusResultType.Warning, + ReadMoreLink = Constants.HealthChecks.DocumentationLinks.Security.ImagingHMACSecretKeyCheck, + }; + + return Task.FromResult(status.Yield()); + } + + /// + public override HealthCheckStatus ExecuteAction(HealthCheckAction action) + => throw new NotSupportedException("Configuration cannot be automatically fixed."); +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs new file mode 100644 index 000000000000..39b006d546ac --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Globalization; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.HealthChecks; +using Umbraco.Cms.Core.HealthChecks.Checks.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.HealthChecks; + +[TestFixture] +public class ImagingHMACSecretKeyCheckTests +{ + // ILocalizedTextService.Localize is called via the extension Localize(area, alias) which delegates to + // Localize(area, alias, CultureInfo, IDictionary). We return the alias so tests can assert on which key was used. + private static ILocalizedTextService MockTextService() + { + var mock = new Mock(); + mock.Setup(x => x.Localize( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>())) + .Returns((string? _, string? alias, CultureInfo? _, IDictionary _) => alias ?? string.Empty); + return mock.Object; + } + + [Test] + public async Task GetStatusAsync_WhenHMACKeyIsEmpty_ReturnsWarning() + { + var settings = new ImagingSettings { HMACSecretKey = Array.Empty() }; + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + IEnumerable statuses = await check.GetStatusAsync(); + + HealthCheckStatus status = statuses.Single(); + Assert.Multiple(() => + { + Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Warning)); + Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckWarningMessage")); + Assert.That(status.ReadMoreLink, Is.Not.Null.And.Not.Empty); + }); + } + + [Test] + public async Task GetStatusAsync_WhenHMACKeyIsSet_ReturnsSuccess() + { + var settings = new ImagingSettings { HMACSecretKey = new byte[] { 1, 2, 3, 4, 5 } }; + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + IEnumerable statuses = await check.GetStatusAsync(); + + HealthCheckStatus status = statuses.Single(); + Assert.Multiple(() => + { + Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Success)); + Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckSuccessMessage")); + Assert.That(status.ReadMoreLink, Is.Null.Or.Empty); + }); + } + + [Test] + public void ExecuteAction_AlwaysThrows() + { + var settings = new ImagingSettings(); + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + Assert.Throws(() => check.ExecuteAction(new HealthCheckAction("fix", check.Id))); + } +} From ae461fc03b89151bac3380f5c623f6d12f7ba36f Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 3 Mar 2026 12:19:34 +0100 Subject: [PATCH 2/5] Added healthcheck for Imaging:HMACSecretKey configuration setting. --- src/Umbraco.Core/Constants-HealthChecks.cs | 6 ++ .../EmbeddedResources/Lang/en.xml | 5 ++ .../EmbeddedResources/Lang/en_us.xml | 5 ++ .../Security/ImagingHMACSecretKeyCheck.cs | 59 ++++++++++++++ .../ImagingHMACSecretKeyCheckTests.cs | 77 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs diff --git a/src/Umbraco.Core/Constants-HealthChecks.cs b/src/Umbraco.Core/Constants-HealthChecks.cs index 118738bd9e9c..e0b637f39d86 100644 --- a/src/Umbraco.Core/Constants-HealthChecks.cs +++ b/src/Umbraco.Core/Constants-HealthChecks.cs @@ -126,6 +126,12 @@ public static class Security /// public const string CspHeaderCheck = "https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP"; + /// + /// The documentation link for the imaging HMAC secret key check. + /// + public const string ImagingHMACSecretKeyCheck = + "https://docs.umbraco.com/umbraco-cms/reference/configuration/imagingsettings"; + /// /// Contains documentation links for HTTPS health checks. /// diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 6ff466663419..d1d6dd817c5f 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -509,6 +509,11 @@ %0%.]]> %0%.]]> + The HMAC secret key for image URL signing is configured. + + Umbraco:CMS:Imaging:HMACSecretKey to prevent unauthorized image manipulation requests.]]> +

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
Umbraco Health Check Status: %0% diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 2514454ba14b..955d2ef7d45c 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -498,6 +498,11 @@ %0%.]]> %0%.]]> + The HMAC secret key for image URL signing is configured. + + Umbraco:CMS:Imaging:HMACSecretKey to prevent unauthorized image manipulation requests.]]> +

Results of the scheduled Umbraco Health Checks run on %0% at %1% are as follows:

%2%]]>
Umbraco Health Check Status: %0% diff --git a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs new file mode 100644 index 000000000000..e7401c278c80 --- /dev/null +++ b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs @@ -0,0 +1,59 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.HealthChecks.Checks.Security; + +/// +/// Health check for the HMAC secret key used to authenticate image manipulation URLs. +/// +[HealthCheck( + "A3E3A6C8-3D42-4F6E-B285-9F6A5C7E1A14", + "Imaging HMAC Secret Key", + Description = "Verifies that an HMAC secret key is configured to authenticate and protect image manipulation URLs.", + Group = "Security")] +public class ImagingHMACSecretKeyCheck : HealthCheck +{ + private readonly ILocalizedTextService _textService; + private readonly IOptionsMonitor _imagingSettings; + + /// + /// Initializes a new instance of the class. + /// + /// The localized text service. + /// The imaging settings monitor. + public ImagingHMACSecretKeyCheck( + ILocalizedTextService textService, + IOptionsMonitor imagingSettings) + { + _textService = textService; + _imagingSettings = imagingSettings; + } + + /// + public override Task> GetStatusAsync() + { + bool isConfigured = _imagingSettings.CurrentValue.HMACSecretKey.Length > 0; + + HealthCheckStatus status = isConfigured + ? new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckSuccessMessage")) + { + ResultType = StatusResultType.Success, + } + : new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckWarningMessage")) + { + ResultType = StatusResultType.Warning, + ReadMoreLink = Constants.HealthChecks.DocumentationLinks.Security.ImagingHMACSecretKeyCheck, + }; + + return Task.FromResult(status.Yield()); + } + + /// + public override HealthCheckStatus ExecuteAction(HealthCheckAction action) + => throw new NotSupportedException("Configuration cannot be automatically fixed."); +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs new file mode 100644 index 000000000000..7a1fb88bbd63 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Globalization; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.HealthChecks; +using Umbraco.Cms.Core.HealthChecks.Checks.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.HealthChecks; + +[TestFixture] +public class ImagingHMACSecretKeyCheckTests +{ + // ILocalizedTextService.Localize is called via the extension Localize(area, alias) which delegates to + // Localize(area, alias, CultureInfo, IDictionary). We return the alias so tests can assert on which key was used. + private static ILocalizedTextService MockTextService() + { + var mock = new Mock(); + mock.Setup(x => x.Localize( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>())) + .Returns((string? _, string? alias, CultureInfo? _, IDictionary _) => alias ?? string.Empty); + return mock.Object; + } + + [Test] + public async Task GetStatusAsync_WhenHMACKeyIsEmpty_ReturnsWarning() + { + var settings = new ImagingSettings { HMACSecretKey = [] }; + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + IEnumerable statuses = await check.GetStatusAsync(); + + HealthCheckStatus status = statuses.Single(); + Assert.Multiple(() => + { + Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Warning)); + Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckWarningMessage")); + Assert.That(status.ReadMoreLink, Is.Not.Null.And.Not.Empty); + }); + } + + [Test] + public async Task GetStatusAsync_WhenHMACKeyIsSet_ReturnsSuccess() + { + var settings = new ImagingSettings { HMACSecretKey = [1, 2, 3, 4, 5] }; + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + IEnumerable statuses = await check.GetStatusAsync(); + + HealthCheckStatus status = statuses.Single(); + Assert.Multiple(() => + { + Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Success)); + Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckSuccessMessage")); + Assert.That(status.ReadMoreLink, Is.Null.Or.Empty); + }); + } + + [Test] + public void ExecuteAction_AlwaysThrows() + { + var settings = new ImagingSettings(); + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + Assert.Throws(() => check.ExecuteAction(new HealthCheckAction("fix", check.Id))); + } +} From 093483b1d9284cfe4ac1fecaba1d8abb627532ee Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 3 Mar 2026 13:22:32 +0100 Subject: [PATCH 3/5] Address code review feedback. --- .../Security/ImagingHMACSecretKeyCheck.cs | 2 +- .../ImagingHMACSecretKeyCheckTests.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs index e7401c278c80..19087bde88b9 100644 --- a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs +++ b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs @@ -37,7 +37,7 @@ public ImagingHMACSecretKeyCheck( /// public override Task> GetStatusAsync() { - bool isConfigured = _imagingSettings.CurrentValue.HMACSecretKey.Length > 0; + bool isConfigured = _imagingSettings.CurrentValue.HMACSecretKey?.Length > 0; HealthCheckStatus status = isConfigured ? new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckSuccessMessage")) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs index 7a1fb88bbd63..6ce0fedf70b2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs @@ -29,6 +29,24 @@ private static ILocalizedTextService MockTextService() return mock.Object; } + [Test] + public async Task GetStatusAsync_WhenHMACKeyIsNull_ReturnsWarning() + { + var settings = new ImagingSettings { HMACSecretKey = null! }; + var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + + IEnumerable statuses = await check.GetStatusAsync(); + + HealthCheckStatus status = statuses.Single(); + Assert.Multiple(() => + { + Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Warning)); + Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckWarningMessage")); + Assert.That(status.ReadMoreLink, Is.Not.Null.And.Not.Empty); + }); + } + [Test] public async Task GetStatusAsync_WhenHMACKeyIsEmpty_ReturnsWarning() { From 003f8ebbae3710f86fcc6491d54cdbd3248e80d6 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 3 Mar 2026 13:26:20 +0100 Subject: [PATCH 4/5] Removes unnecessary test. --- .../HealthChecks/ImagingHMACSecretKeyCheckTests.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs index 6ce0fedf70b2..181ae58c7579 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs @@ -82,14 +82,4 @@ public async Task GetStatusAsync_WhenHMACKeyIsSet_ReturnsSuccess() Assert.That(status.ReadMoreLink, Is.Null.Or.Empty); }); } - - [Test] - public void ExecuteAction_AlwaysThrows() - { - var settings = new ImagingSettings(); - var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); - var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); - - Assert.Throws(() => check.ExecuteAction(new HealthCheckAction("fix", check.Id))); - } } From 2301cf7aea04850a8760429d82b412c6003cb980 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 3 Mar 2026 17:35:59 +0100 Subject: [PATCH 5/5] Use service in healthcheck. --- .../Security/ImagingHMACSecretKeyCheck.cs | 13 ++++--- .../ImagingHMACSecretKeyCheckTests.cs | 35 ++++--------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs index 19087bde88b9..4c8de6a235bd 100644 --- a/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs +++ b/src/Umbraco.Core/HealthChecks/Checks/Security/ImagingHMACSecretKeyCheck.cs @@ -1,8 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; @@ -19,25 +18,25 @@ namespace Umbraco.Cms.Core.HealthChecks.Checks.Security; public class ImagingHMACSecretKeyCheck : HealthCheck { private readonly ILocalizedTextService _textService; - private readonly IOptionsMonitor _imagingSettings; + private readonly IHmacSecretKeyService _hmacSecretKeyService; /// /// Initializes a new instance of the class. /// /// The localized text service. - /// The imaging settings monitor. + /// The HMAC secret key service. public ImagingHMACSecretKeyCheck( ILocalizedTextService textService, - IOptionsMonitor imagingSettings) + IHmacSecretKeyService hmacSecretKeyService) { _textService = textService; - _imagingSettings = imagingSettings; + _hmacSecretKeyService = hmacSecretKeyService; } /// public override Task> GetStatusAsync() { - bool isConfigured = _imagingSettings.CurrentValue.HMACSecretKey?.Length > 0; + bool isConfigured = _hmacSecretKeyService.HasHmacSecretKey(); HealthCheckStatus status = isConfigured ? new HealthCheckStatus(_textService.Localize("healthcheck", "imagingHMACSecretKeyCheckSuccessMessage")) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs index 181ae58c7579..cefdebe1a3c0 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/HealthChecks/ImagingHMACSecretKeyCheckTests.cs @@ -2,12 +2,11 @@ // See LICENSE for more details. using System.Globalization; -using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.HealthChecks; using Umbraco.Cms.Core.HealthChecks.Checks.Security; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.HealthChecks; @@ -30,11 +29,10 @@ private static ILocalizedTextService MockTextService() } [Test] - public async Task GetStatusAsync_WhenHMACKeyIsNull_ReturnsWarning() + public async Task GetStatusAsync_WhenHMACKeyIsNotConfigured_ReturnsWarning() { - var settings = new ImagingSettings { HMACSecretKey = null! }; - var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); - var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + var hmacSecretKeyService = Mock.Of(x => x.HasHmacSecretKey() == false); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), hmacSecretKeyService); IEnumerable statuses = await check.GetStatusAsync(); @@ -48,29 +46,10 @@ public async Task GetStatusAsync_WhenHMACKeyIsNull_ReturnsWarning() } [Test] - public async Task GetStatusAsync_WhenHMACKeyIsEmpty_ReturnsWarning() + public async Task GetStatusAsync_WhenHMACKeyIsConfigured_ReturnsSuccess() { - var settings = new ImagingSettings { HMACSecretKey = [] }; - var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); - var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); - - IEnumerable statuses = await check.GetStatusAsync(); - - HealthCheckStatus status = statuses.Single(); - Assert.Multiple(() => - { - Assert.That(status.ResultType, Is.EqualTo(StatusResultType.Warning)); - Assert.That(status.Message, Is.EqualTo("imagingHMACSecretKeyCheckWarningMessage")); - Assert.That(status.ReadMoreLink, Is.Not.Null.And.Not.Empty); - }); - } - - [Test] - public async Task GetStatusAsync_WhenHMACKeyIsSet_ReturnsSuccess() - { - var settings = new ImagingSettings { HMACSecretKey = [1, 2, 3, 4, 5] }; - var optionsMonitor = Mock.Of>(x => x.CurrentValue == settings); - var check = new ImagingHMACSecretKeyCheck(MockTextService(), optionsMonitor); + var hmacSecretKeyService = Mock.Of(x => x.HasHmacSecretKey() == true); + var check = new ImagingHMACSecretKeyCheck(MockTextService(), hmacSecretKeyService); IEnumerable statuses = await check.GetStatusAsync();