diff --git a/README.md b/README.md index 539ff92..27e7006 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,13 @@ A Go SDK for Switcher API The **Switcher Client SDK for Go** provides integration with [Switcher-API](https://github.com/switcherapi/switcher-api), enabling feature flag management in Go applications. -> Features marked as **Under development** are part of the current SDK roadmap and may not be available in the repository yet. - ### Key Features - **Clean & Maintainable**: Simple package-level access with an instance-based core - **Local Mode**: Offline execution using snapshot files from your Switcher-API domain - **Silent Mode**: Hybrid configuration with automatic fallback for connectivity issues -- **Built-in Testing Helpers (Under development)**: Test-oriented mocking support adapted for Go +- **Built-in Testing Helpers**: Test-oriented mocking support adapted for Go - **Zero Latency**: Local snapshot execution for high-performance scenarios -- **Secure**: Regex protections and configurable remote transport settings - **Monitoring**: Execution logging, caching, and error notification hooks ## Quick Start @@ -184,8 +181,6 @@ func main() { SilentMode: 5 * time.Minute, RestrictRelay: true, ThrottleMaxWorkers: 2, - RegexMaxBlacklist: 10, - RegexMaxTimeLimit: 100 * time.Millisecond, Remote: client.RemoteOptions{ CertPath: "./certs/client.pem", AutoRenewToken: true, @@ -212,8 +207,6 @@ func main() { | `SilentMode` | `time.Duration` | Silent mode retry time before returning to remote mode | `0` | | `RestrictRelay` | `bool` | Enable relay restrictions in local mode | `true` | | `ThrottleMaxWorkers` | `int` | Max workers for throttling refresh tasks | runtime-defined | -| `RegexMaxBlacklist` | `int` | Max cached entries for failed regex | `100` | -| `RegexMaxTimeLimit` | `time.Duration` | Regex execution time limit | `3s` | | `Remote` | `RemoteOptions` | Remote transport settings | `RemoteOptions{}` | `RemoteOptions` fields: @@ -227,12 +220,6 @@ func main() { **Note:** lower remote connect timeouts help silent mode fall back faster when the upstream is unavailable. -#### Security Features - -- **ReDoS Protection (Under development)**: Regex safety features with bounded execution time -- **Time Limits**: Configurable timeouts for regex and remote operations -- **Certificate Support**: PEM client-certificate bundles for secure API connections - ## Usage Examples ### Basic Feature Flag Checking diff --git a/context.go b/context.go index ddd431e..b22020e 100644 --- a/context.go +++ b/context.go @@ -9,8 +9,6 @@ const ( DefaultLogger = false DefaultFreeze = false DefaultRestrictRelay = true - DefaultRegexMaxBlacklist = 100 - DefaultRegexMaxTimeLimit = 3 * time.Second DefaultRemoteConnectTimeout = 300 * time.Millisecond DefaultRemoteTimeout = 5 * time.Second ) @@ -24,7 +22,7 @@ type RemoteOptions struct { } // ContextOptions exposes advanced SDK behaviors such as local mode, snapshot management, -// throttling and regex safety configuration. See README advanced configuration for details. +// throttling. See README advanced configuration for details. type ContextOptions struct { Local bool Logger bool @@ -34,8 +32,6 @@ type ContextOptions struct { SilentMode time.Duration RestrictRelay bool ThrottleMaxWorkers int - RegexMaxBlacklist int - RegexMaxTimeLimit time.Duration Remote RemoteOptions } @@ -64,14 +60,6 @@ func (o ContextOptions) withDefaults() ContextOptions { o.RestrictRelay = DefaultRestrictRelay } - if o.RegexMaxBlacklist == 0 { - o.RegexMaxBlacklist = DefaultRegexMaxBlacklist - } - - if o.RegexMaxTimeLimit == 0 { - o.RegexMaxTimeLimit = DefaultRegexMaxTimeLimit - } - o.Remote = o.Remote.withDefaults() return o } diff --git a/context_test.go b/context_test.go index df66390..0113165 100644 --- a/context_test.go +++ b/context_test.go @@ -48,8 +48,6 @@ func TestBuildContext(t *testing.T) { assert.Equal(t, DefaultEnvironment, ctx.Environment) assert.True(t, ctx.Options.RestrictRelay) - assert.Equal(t, DefaultRegexMaxBlacklist, ctx.Options.RegexMaxBlacklist) - assert.Equal(t, DefaultRegexMaxTimeLimit, ctx.Options.RegexMaxTimeLimit) assert.Equal(t, DefaultRemoteConnectTimeout, ctx.Options.Remote.ConnectTimeout) assert.Equal(t, DefaultRemoteTimeout, ctx.Options.Remote.Timeout) }) diff --git a/local_strategies_test.go b/local_strategies_test.go index f36781f..5ed2873 100644 --- a/local_strategies_test.go +++ b/local_strategies_test.go @@ -112,6 +112,14 @@ func TestLocalStrategyOperations(t *testing.T) { assert.False(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "INVALID_OP", []string{`USER_[0-9]{1,2}`}), "USER_11")) }) + t.Run("should avoid catastrophic backtracking for regex strategy", func(t *testing.T) { + assert.False(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "EXIST", []string{`^(([a-z])+.)+[A-Z]([a-z])+$`}), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + assert.False(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "EXIST", []string{`^(a+)+$`}), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")) + assert.False(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "EQUAL", []string{`^(a+)+$`}), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")) + assert.True(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "NOT_EXIST", []string{`^(a+)+$`}), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")) + assert.True(t, processLocalStrategy(makeStrategy("REGEX_VALIDATION", "NOT_EQUAL", []string{`^(a+)+$`}), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")) + }) + t.Run("should return false for unknown strategy", func(t *testing.T) { assert.False(t, processLocalStrategy(makeStrategy("UNKNOWN", "EQUAL", []string{"USER_1"}), "USER_1")) })