diff --git a/openfeature/memprovider/in_memory_provider.go b/openfeature/memprovider/in_memory_provider.go index 7fc95853..8ef60e34 100644 --- a/openfeature/memprovider/in_memory_provider.go +++ b/openfeature/memprovider/in_memory_provider.go @@ -226,7 +226,9 @@ func (flag *InMemoryFlag) Resolve(defaultValue any, flatCtx openfeature.Flattene } // first resolve from context callback - if flag.ContextEvaluator != nil { + // ContextEvaluator is a pointer to a func, so guard against both a nil + // pointer and a non-nil pointer to a nil func before dereferencing. + if flag.ContextEvaluator != nil && *flag.ContextEvaluator != nil { return (*flag.ContextEvaluator)(*flag, flatCtx) } diff --git a/openfeature/memprovider/in_memory_provider_test.go b/openfeature/memprovider/in_memory_provider_test.go index 13d79648..ec915d53 100644 --- a/openfeature/memprovider/in_memory_provider_test.go +++ b/openfeature/memprovider/in_memory_provider_test.go @@ -225,6 +225,40 @@ func TestInMemoryProvider_WithContext(t *testing.T) { }) } +func TestInMemoryProvider_NilContextEvaluatorFunc(t *testing.T) { + // ContextEvaluator is a non-nil pointer to a nil func. The pointer passes a + // plain != nil guard, so a naive dereference would panic. The flag must fall + // back to the default variant instead. + var nilFn func(callerFlag InMemoryFlag, flatCtx openfeature.FlattenedContext) (any, openfeature.ProviderResolutionDetail) + + memoryProvider := NewInMemoryProvider(map[string]InMemoryFlag{ + "nilFnFlag": { + Key: "nilFnFlag", + State: Enabled, + DefaultVariant: "true", + Variants: map[string]any{ + "true": true, + "false": false, + }, + ContextEvaluator: &nilFn, + }, + }) + + ctx := t.Context() + + evaluation := memoryProvider.BooleanEvaluation(ctx, "nilFnFlag", false, nil) + + if evaluation.Value != true { + t.Errorf("incorrect evaluation, expected %v, got %v", true, evaluation.Value) + } + if evaluation.Variant != "true" { + t.Errorf("incorrect variant, expected %q, got %q", "true", evaluation.Variant) + } + if evaluation.Reason != openfeature.StaticReason { + t.Errorf("incorrect reason, expected %q, got %q", openfeature.StaticReason, evaluation.Reason) + } +} + func TestInMemoryProvider_MissingFlag(t *testing.T) { memoryProvider := NewInMemoryProvider(map[string]InMemoryFlag{})