From e1ccf92a888d841137627333a6b3ebbc152bb0ce Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 17:11:09 +0000 Subject: [PATCH 1/9] fix: Validate string format of SDK key --- pkgs/sdk/server/src/Configuration.cs | 2 +- pkgs/sdk/server/src/ConfigurationBuilder.cs | 21 +++++++++++++++++---- pkgs/sdk/server/src/LdClient.cs | 6 ++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pkgs/sdk/server/src/Configuration.cs b/pkgs/sdk/server/src/Configuration.cs index 092217ed..95a9b480 100644 --- a/pkgs/sdk/server/src/Configuration.cs +++ b/pkgs/sdk/server/src/Configuration.cs @@ -187,7 +187,7 @@ internal Configuration(ConfigurationBuilder builder) Events = builder._events; Http = builder._http; Logging = builder._logging; - Offline = builder._offline; + Offline = builder._sdkKey == null || builder._offline; SdkKey = builder._sdkKey; ServiceEndpoints = (builder._serviceEndpointsBuilder ?? Components.ServiceEndpoints()).Build(); StartWaitTime = builder._startWaitTime; diff --git a/pkgs/sdk/server/src/ConfigurationBuilder.cs b/pkgs/sdk/server/src/ConfigurationBuilder.cs index 1e45d98a..91b729ca 100644 --- a/pkgs/sdk/server/src/ConfigurationBuilder.cs +++ b/pkgs/sdk/server/src/ConfigurationBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using LaunchDarkly.Logging; +using LaunchDarkly.Sdk.Helpers; using LaunchDarkly.Sdk.Server.Hooks; using LaunchDarkly.Sdk.Server.Integrations; using LaunchDarkly.Sdk.Server.Interfaces; @@ -53,9 +54,20 @@ public sealed class ConfigurationBuilder #region Internal constructors + /// + /// Sets the SDK key only if it passes validation rules. + /// + private void SetSdkKeyIfValid(string sdkKey) + { + if (ValidationUtils.ValidateStringValue(sdkKey) == null) + { + _sdkKey = sdkKey; + } + } + internal ConfigurationBuilder(string sdkKey) { - _sdkKey = sdkKey; + SetSdkKeyIfValid(sdkKey); } internal ConfigurationBuilder(Configuration copyFrom) @@ -70,7 +82,8 @@ internal ConfigurationBuilder(Configuration copyFrom) _http = copyFrom.Http; _logging = copyFrom.Logging; _offline = copyFrom.Offline; - _sdkKey = copyFrom.SdkKey; + // The SDK key from Configuration should already be valid, but we validate just in case + SetSdkKeyIfValid(copyFrom.SdkKey); _serviceEndpointsBuilder = new ServiceEndpointsBuilder(copyFrom.ServiceEndpoints); _startWaitTime = copyFrom.StartWaitTime; _applicationInfo = copyFrom.ApplicationInfo; @@ -317,15 +330,15 @@ public ConfigurationBuilder Offline(bool offline) /// /// Sets the SDK key for your LaunchDarkly environment. + /// They key will not be updated if the provided key contains invalid characters. /// /// the SDK key /// the same builder public ConfigurationBuilder SdkKey(string sdkKey) { - _sdkKey = sdkKey; + SetSdkKeyIfValid(sdkKey); return this; } - /// /// Sets the SDK's service URIs, using a configuration builder obtained from /// . diff --git a/pkgs/sdk/server/src/LdClient.cs b/pkgs/sdk/server/src/LdClient.cs index 49bd37dc..7a38ffbb 100644 --- a/pkgs/sdk/server/src/LdClient.cs +++ b/pkgs/sdk/server/src/LdClient.cs @@ -136,6 +136,12 @@ public LdClient(Configuration config) _log = logConfig.LogAdapter.Logger(logConfig.BaseLoggerName ?? LogNames.DefaultBase); _log.Info("Starting LaunchDarkly client {0}", AssemblyVersions.GetAssemblyVersionStringForType(typeof(LdClient))); + + if (_configuration.SdkKey == null) + { + _log.Error("The SDK key provided is invalid."); + } + _evalLog = _log.SubLogger(LogNames.EvaluationSubLog); var taskExecutor = new TaskExecutor(this, _log); From 0f63f7d0d4ec06a98f772a8d09526441101bf340 Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 17:42:39 +0000 Subject: [PATCH 2/9] add sdk-key to test --- pkgs/sdk/server/test/LdClientListenersTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/sdk/server/test/LdClientListenersTest.cs b/pkgs/sdk/server/test/LdClientListenersTest.cs index 06e21500..7ff243f4 100644 --- a/pkgs/sdk/server/test/LdClientListenersTest.cs +++ b/pkgs/sdk/server/test/LdClientListenersTest.cs @@ -62,7 +62,7 @@ public void ClientSendsFlagValueChangeEvents() var testData = TestData.DataSource(); testData.Update(testData.Flag(flagKey).On(false)); - var config = Configuration.Builder("").DataSource(testData) + var config = Configuration.Builder("sdk-key").DataSource(testData) .Events(Components.NoEvents).Build(); using (var client = new LdClient(config)) @@ -136,7 +136,7 @@ public void DataSourceStatusProviderSendsStatusUpdates() var config = BasicConfig() .DataSource(testData) .Build(); - + using (var client = new LdClient(config)) { var statuses = new EventSink(); From 7f04fc9c8a74eea23026679b6910e6bd25c0c897 Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 20:25:40 +0000 Subject: [PATCH 3/9] Address feedback to make validation more specific. Removed forcing offline mode. --- pkgs/sdk/server/src/Configuration.cs | 2 +- pkgs/sdk/server/src/ConfigurationBuilder.cs | 2 +- .../common/src/Helpers/ValidationUtils.cs | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pkgs/sdk/server/src/Configuration.cs b/pkgs/sdk/server/src/Configuration.cs index 95a9b480..092217ed 100644 --- a/pkgs/sdk/server/src/Configuration.cs +++ b/pkgs/sdk/server/src/Configuration.cs @@ -187,7 +187,7 @@ internal Configuration(ConfigurationBuilder builder) Events = builder._events; Http = builder._http; Logging = builder._logging; - Offline = builder._sdkKey == null || builder._offline; + Offline = builder._offline; SdkKey = builder._sdkKey; ServiceEndpoints = (builder._serviceEndpointsBuilder ?? Components.ServiceEndpoints()).Build(); StartWaitTime = builder._startWaitTime; diff --git a/pkgs/sdk/server/src/ConfigurationBuilder.cs b/pkgs/sdk/server/src/ConfigurationBuilder.cs index 91b729ca..5214549f 100644 --- a/pkgs/sdk/server/src/ConfigurationBuilder.cs +++ b/pkgs/sdk/server/src/ConfigurationBuilder.cs @@ -59,7 +59,7 @@ public sealed class ConfigurationBuilder /// private void SetSdkKeyIfValid(string sdkKey) { - if (ValidationUtils.ValidateStringValue(sdkKey) == null) + if (ValidationUtils.ValidateSdkKeyFormat(sdkKey) == null) { _sdkKey = sdkKey; } diff --git a/pkgs/shared/common/src/Helpers/ValidationUtils.cs b/pkgs/shared/common/src/Helpers/ValidationUtils.cs index 7a11780d..d76d1b91 100644 --- a/pkgs/shared/common/src/Helpers/ValidationUtils.cs +++ b/pkgs/shared/common/src/Helpers/ValidationUtils.cs @@ -10,6 +10,33 @@ public static class ValidationUtils { private static readonly Regex ValidCharsRegex = new Regex("^[-a-zA-Z0-9._]+\\z"); + /// + /// Validates that a string does not contain invalid characters or exceed the max length of 8192 characters. + /// + /// the SDK key to validate. + /// Null if the input is valid, otherwise an error string describing the issue. + public static string ValidateSdkKeyFormat(string sdkKey) + { + + // For offline mode, we allow a null or empty SDK key and it is not invalid. + if (string.IsNullOrEmpty(sdkKey)) + { + return null; + } + + if (sdkKey.Length > 8192) + { + return "SDK key cannot be longer than 1024 characters."; + } + + if (!ValidCharsRegex.IsMatch(sdkKey)) + { + return "SDK key contains invalid characters."; + } + + return null; + } + /// /// Validates that a string is non-empty, not too longer for our systems, and only contains /// alphanumeric characters, hyphens, periods, and underscores. From 61de2417f2d20843a096633e571ebf2faa60d75e Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 20:27:19 +0000 Subject: [PATCH 4/9] dont log an error if they are offline. --- pkgs/sdk/server/src/LdClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/sdk/server/src/LdClient.cs b/pkgs/sdk/server/src/LdClient.cs index 7a38ffbb..2968952e 100644 --- a/pkgs/sdk/server/src/LdClient.cs +++ b/pkgs/sdk/server/src/LdClient.cs @@ -137,7 +137,7 @@ public LdClient(Configuration config) _log.Info("Starting LaunchDarkly client {0}", AssemblyVersions.GetAssemblyVersionStringForType(typeof(LdClient))); - if (_configuration.SdkKey == null) + if (_configuration.SdkKey == null && !_configuration.offline) { _log.Error("The SDK key provided is invalid."); } From 450b852600dc0e343b6429bed64f577924bd91e2 Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 20:37:25 +0000 Subject: [PATCH 5/9] fix casing --- pkgs/sdk/server/src/LdClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/sdk/server/src/LdClient.cs b/pkgs/sdk/server/src/LdClient.cs index 2968952e..dc2b300b 100644 --- a/pkgs/sdk/server/src/LdClient.cs +++ b/pkgs/sdk/server/src/LdClient.cs @@ -137,7 +137,7 @@ public LdClient(Configuration config) _log.Info("Starting LaunchDarkly client {0}", AssemblyVersions.GetAssemblyVersionStringForType(typeof(LdClient))); - if (_configuration.SdkKey == null && !_configuration.offline) + if (_configuration.SdkKey == null && !_configuration.Offline) { _log.Error("The SDK key provided is invalid."); } From 427dfaacbc1ac5f513e24d97730dc28f5f490c2f Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Fri, 26 Sep 2025 21:04:10 +0000 Subject: [PATCH 6/9] adjust validation function --- pkgs/sdk/server/src/ConfigurationBuilder.cs | 2 +- .../common/src/Helpers/ValidationUtils.cs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pkgs/sdk/server/src/ConfigurationBuilder.cs b/pkgs/sdk/server/src/ConfigurationBuilder.cs index 5214549f..42f71b71 100644 --- a/pkgs/sdk/server/src/ConfigurationBuilder.cs +++ b/pkgs/sdk/server/src/ConfigurationBuilder.cs @@ -59,7 +59,7 @@ public sealed class ConfigurationBuilder /// private void SetSdkKeyIfValid(string sdkKey) { - if (ValidationUtils.ValidateSdkKeyFormat(sdkKey) == null) + if (ValidationUtils.IsValidSdkKeyFormat(sdkKey)) { _sdkKey = sdkKey; } diff --git a/pkgs/shared/common/src/Helpers/ValidationUtils.cs b/pkgs/shared/common/src/Helpers/ValidationUtils.cs index d76d1b91..f50f8338 100644 --- a/pkgs/shared/common/src/Helpers/ValidationUtils.cs +++ b/pkgs/shared/common/src/Helpers/ValidationUtils.cs @@ -9,36 +9,39 @@ namespace LaunchDarkly.Sdk.Helpers public static class ValidationUtils { private static readonly Regex ValidCharsRegex = new Regex("^[-a-zA-Z0-9._]+\\z"); + private const int MaxSdkKeyLength = 8192; /// - /// Validates that a string does not contain invalid characters or exceed the max length of 8192 characters. + /// Validates that a string does not contain invalid characters and is not too long for our systems. /// /// the SDK key to validate. /// Null if the input is valid, otherwise an error string describing the issue. - public static string ValidateSdkKeyFormat(string sdkKey) + public static bool IsValidSdkKeyFormat(string sdkKey, out string errorMessage) { // For offline mode, we allow a null or empty SDK key and it is not invalid. if (string.IsNullOrEmpty(sdkKey)) { - return null; + return true; } - if (sdkKey.Length > 8192) + if (sdkKey.Length > MaxSdkKeyLength) { - return "SDK key cannot be longer than 1024 characters."; + errorMessage = $"SDK key cannot be longer than {MaxSdkKeyLength} characters."; + return false; } if (!ValidCharsRegex.IsMatch(sdkKey)) { - return "SDK key contains invalid characters."; + errorMessage = "SDK key contains invalid characters."; + return false; } - return null; + return true; } /// - /// Validates that a string is non-empty, not too longer for our systems, and only contains + /// Validates that a string is non-empty, not too long for our systems, and only contains /// alphanumeric characters, hyphens, periods, and underscores. /// /// the string to validate. From dfa801e0f1ae4d0d7c9385d19653da3ab66906ed Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Sat, 27 Sep 2025 00:44:23 +0000 Subject: [PATCH 7/9] undo common changes to prevent merge conflict --- .../server/src/LaunchDarkly.ServerSdk.csproj | 4 +-- .../common/src/Helpers/ValidationUtils.cs | 32 +------------------ 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/pkgs/sdk/server/src/LaunchDarkly.ServerSdk.csproj b/pkgs/sdk/server/src/LaunchDarkly.ServerSdk.csproj index 3dcef758..81ca499b 100644 --- a/pkgs/sdk/server/src/LaunchDarkly.ServerSdk.csproj +++ b/pkgs/sdk/server/src/LaunchDarkly.ServerSdk.csproj @@ -39,9 +39,9 @@ - + - + diff --git a/pkgs/shared/common/src/Helpers/ValidationUtils.cs b/pkgs/shared/common/src/Helpers/ValidationUtils.cs index f50f8338..7a11780d 100644 --- a/pkgs/shared/common/src/Helpers/ValidationUtils.cs +++ b/pkgs/shared/common/src/Helpers/ValidationUtils.cs @@ -9,39 +9,9 @@ namespace LaunchDarkly.Sdk.Helpers public static class ValidationUtils { private static readonly Regex ValidCharsRegex = new Regex("^[-a-zA-Z0-9._]+\\z"); - private const int MaxSdkKeyLength = 8192; /// - /// Validates that a string does not contain invalid characters and is not too long for our systems. - /// - /// the SDK key to validate. - /// Null if the input is valid, otherwise an error string describing the issue. - public static bool IsValidSdkKeyFormat(string sdkKey, out string errorMessage) - { - - // For offline mode, we allow a null or empty SDK key and it is not invalid. - if (string.IsNullOrEmpty(sdkKey)) - { - return true; - } - - if (sdkKey.Length > MaxSdkKeyLength) - { - errorMessage = $"SDK key cannot be longer than {MaxSdkKeyLength} characters."; - return false; - } - - if (!ValidCharsRegex.IsMatch(sdkKey)) - { - errorMessage = "SDK key contains invalid characters."; - return false; - } - - return true; - } - - /// - /// Validates that a string is non-empty, not too long for our systems, and only contains + /// Validates that a string is non-empty, not too longer for our systems, and only contains /// alphanumeric characters, hyphens, periods, and underscores. /// /// the string to validate. From 70a646a043d844339e689d34c0df1ef4df2a1c49 Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Mon, 29 Sep 2025 13:42:02 +0000 Subject: [PATCH 8/9] Update the client to validate the key --- pkgs/sdk/client/src/ConfigurationBuilder.cs | 20 ++++++++++++++++--- .../client/src/LaunchDarkly.ClientSdk.csproj | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pkgs/sdk/client/src/ConfigurationBuilder.cs b/pkgs/sdk/client/src/ConfigurationBuilder.cs index 7a3ed36e..04662825 100644 --- a/pkgs/sdk/client/src/ConfigurationBuilder.cs +++ b/pkgs/sdk/client/src/ConfigurationBuilder.cs @@ -3,6 +3,7 @@ using LaunchDarkly.Sdk.Client.Integrations; using LaunchDarkly.Sdk.Client.Internal.Interfaces; using LaunchDarkly.Sdk.Client.Subsystems; +using LaunchDarkly.Sdk.Helpers; namespace LaunchDarkly.Sdk.Client { @@ -75,9 +76,20 @@ public enum AutoEnvAttributes internal IBackgroundModeManager _backgroundModeManager; internal IConnectivityStateManager _connectivityStateManager; + /// + /// Sets the mobile key only if it passes validation rules. + /// + private void SetMobileKeyIfValid(string mobileKey) + { + if (ValidationUtils.ValidateStringValue(mobileKey) == null) + { + _mobileKey = mobileKey; + } + } + internal ConfigurationBuilder(string mobileKey, AutoEnvAttributes autoEnvAttributes) { - _mobileKey = mobileKey; + SetMobileKeyIfValid(mobileKey); _autoEnvAttributes = autoEnvAttributes == AutoEnvAttributes.Enabled; // map enum to boolean } @@ -92,7 +104,8 @@ internal ConfigurationBuilder(Configuration copyFrom) _events = copyFrom.Events; _httpConfigurationBuilder = copyFrom.HttpConfigurationBuilder; _loggingConfigurationBuilder = copyFrom.LoggingConfigurationBuilder; - _mobileKey = copyFrom.MobileKey; + // The mobile key from Configuration should already be valid, but we validate just in case + SetMobileKeyIfValid(copyFrom.MobileKey); _offline = copyFrom.Offline; _persistenceConfigurationBuilder = copyFrom.PersistenceConfigurationBuilder; _serviceEndpointsBuilder = new ServiceEndpointsBuilder(copyFrom.ServiceEndpoints); @@ -362,6 +375,7 @@ public ConfigurationBuilder Logging(LoggingConfigurationBuilder loggingConfigura /// /// Sets the key for your LaunchDarkly environment. + /// The key will not be updated if the provided key contains invalid characters. /// /// /// This should be the "mobile key" field for the environment on your LaunchDarkly dashboard. @@ -370,7 +384,7 @@ public ConfigurationBuilder Logging(LoggingConfigurationBuilder loggingConfigura /// the same builder public ConfigurationBuilder MobileKey(string mobileKey) { - _mobileKey = mobileKey; + SetMobileKeyIfValid(mobileKey); return this; } diff --git a/pkgs/sdk/client/src/LaunchDarkly.ClientSdk.csproj b/pkgs/sdk/client/src/LaunchDarkly.ClientSdk.csproj index 2bcebbdc..50b37a93 100644 --- a/pkgs/sdk/client/src/LaunchDarkly.ClientSdk.csproj +++ b/pkgs/sdk/client/src/LaunchDarkly.ClientSdk.csproj @@ -41,9 +41,9 @@ - + - + From fb778621a7464d0b2866ce4b40e2690c3bcbb8fe Mon Sep 17 00:00:00 2001 From: jsonbailey Date: Mon, 29 Sep 2025 14:56:27 +0000 Subject: [PATCH 9/9] Use the proper validation method --- pkgs/sdk/client/src/ConfigurationBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/sdk/client/src/ConfigurationBuilder.cs b/pkgs/sdk/client/src/ConfigurationBuilder.cs index 04662825..76e3fd7f 100644 --- a/pkgs/sdk/client/src/ConfigurationBuilder.cs +++ b/pkgs/sdk/client/src/ConfigurationBuilder.cs @@ -81,7 +81,7 @@ public enum AutoEnvAttributes /// private void SetMobileKeyIfValid(string mobileKey) { - if (ValidationUtils.ValidateStringValue(mobileKey) == null) + if (ValidationUtils.IsValidSdkKeyFormat(mobileKey)) { _mobileKey = mobileKey; }