diff --git a/src/AutoMapper/Licensing/LicenseAccessor.cs b/src/AutoMapper/Licensing/LicenseAccessor.cs index ba4bfa152c..8ee939b1e1 100644 --- a/src/AutoMapper/Licensing/LicenseAccessor.cs +++ b/src/AutoMapper/Licensing/LicenseAccessor.cs @@ -52,11 +52,22 @@ private License Initialize() /// Resolves the license key from, in order of precedence: the explicitly configured value, /// the product-specific AUTOMAPPER_LICENSE_KEY environment variable, then the shared /// LUCKYPENNY_LICENSE_KEY environment variable (usable across Lucky Penny products). + /// Whitespace is removed from each value, and empty values are treated as unset. /// internal static string ResolveLicenseKey(string explicitKey) => - explicitKey - ?? Environment.GetEnvironmentVariable(AutoMapperLicenseKeyEnvVariable) - ?? Environment.GetEnvironmentVariable(SharedLicenseKeyEnvVariable); + NormalizeLicenseKey(explicitKey) + ?? NormalizeLicenseKey(Environment.GetEnvironmentVariable(AutoMapperLicenseKeyEnvVariable)) + ?? NormalizeLicenseKey(Environment.GetEnvironmentVariable(SharedLicenseKeyEnvVariable)); + + private static string NormalizeLicenseKey(string key) + { + if (string.IsNullOrWhiteSpace(key)) + { + return null; + } + + return new string(key.Where(c => !char.IsWhiteSpace(c)).ToArray()); + } private Claim[] ValidateKey(string licenseKey) { @@ -105,4 +116,4 @@ private Claim[] ValidateKey(string licenseKey) } } -} \ No newline at end of file +} diff --git a/src/UnitTests/Licensing/LicenseKeyEnvironmentVariableTests.cs b/src/UnitTests/Licensing/LicenseKeyEnvironmentVariableTests.cs index 4d1bbad3f3..5d93ef86ac 100644 --- a/src/UnitTests/Licensing/LicenseKeyEnvironmentVariableTests.cs +++ b/src/UnitTests/Licensing/LicenseKeyEnvironmentVariableTests.cs @@ -10,6 +10,76 @@ public class LicenseKeyEnvironmentVariableTests { private const string AutoMapperEnvVar = LicenseAccessor.AutoMapperLicenseKeyEnvVariable; private const string SharedEnvVar = LicenseAccessor.SharedLicenseKeyEnvVariable; + private const string CleanKey = "header.pay-load.sign_ature"; + + public static TheoryData KeysWithWhitespace => new() + { + CleanKey + "\n", + CleanKey + "\r\n", + CleanKey + " ", + CleanKey + "\t", + " \t" + CleanKey, + "header.\r\npay-\nload.sign_\tature", + "\u00a0header.\u2003pay-load.sign_ature\u2028" + }; + + [Theory] + [MemberData(nameof(KeysWithWhitespace))] + public void ExplicitKey_StripsWhitespace_AndTakesPrecedence(string key) + { + WithEnvironment(autoMapper: "env-automapper-key", shared: "env-shared-key", () => + LicenseAccessor.ResolveLicenseKey(key).ShouldBe(CleanKey)); + } + + [Theory] + [MemberData(nameof(KeysWithWhitespace))] + public void AutoMapperEnvironmentVariable_StripsWhitespace_AndTakesPrecedence(string key) + { + WithEnvironment(autoMapper: key, shared: "env-shared-key", () => + LicenseAccessor.ResolveLicenseKey(null).ShouldBe(CleanKey)); + } + + [Theory] + [MemberData(nameof(KeysWithWhitespace))] + public void SharedEnvironmentVariable_StripsWhitespace(string key) + { + WithEnvironment(autoMapper: null, shared: key, () => + LicenseAccessor.ResolveLicenseKey(null).ShouldBe(CleanKey)); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("\t\r\n")] + [InlineData("\u00a0\u2003\u2028")] + public void BlankExplicitKey_FallsBackToAutoMapperEnvironmentVariable(string key) + { + WithEnvironment(autoMapper: " env-automapper-key\n", shared: "env-shared-key", () => + LicenseAccessor.ResolveLicenseKey(key).ShouldBe("env-automapper-key")); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData("\t\r\n")] + [InlineData("\u00a0\u2003\u2028")] + public void BlankHigherPrecedenceKeys_FallBackToSharedEnvironmentVariable(string key) + { + WithEnvironment(autoMapper: key, shared: " env-shared-key\n", () => + LicenseAccessor.ResolveLicenseKey(key).ShouldBe("env-shared-key")); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("\t\r\n")] + [InlineData("\u00a0\u2003\u2028")] + public void ReturnsNull_WhenAllKeysAreBlank(string key) + { + WithEnvironment(autoMapper: key, shared: key, () => + LicenseAccessor.ResolveLicenseKey(key).ShouldBeNull()); + } [Fact] public void ExplicitKey_TakesPrecedence_OverBothEnvironmentVariables()