From 4cc4a491e20a3b0f997887f14983672a0e32abd0 Mon Sep 17 00:00:00 2001 From: "maksim.nabokikh" Date: Thu, 5 Mar 2026 08:27:37 +0100 Subject: [PATCH] Add CEL integration with cost estimation and error handling Signed-off-by: maksim.nabokikh --- pkg/cel/cost.go | 8 +++++--- pkg/cel/cost_test.go | 3 ++- pkg/cel/library/groups.go | 8 ++++++-- pkg/cel/library/groups_test.go | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/cel/cost.go b/pkg/cel/cost.go index 2f26ea82..b3e3610d 100644 --- a/pkg/cel/cost.go +++ b/pkg/cel/cost.go @@ -1,6 +1,8 @@ package cel import ( + "fmt" + "github.com/google/cel-go/checker" ) @@ -28,13 +30,13 @@ type CostEstimate struct { // EstimateCost returns the estimated cost range for a compiled expression. // This is computed statically at compile time without evaluating the expression. -func (c *Compiler) EstimateCost(result *CompilationResult) CostEstimate { +func (c *Compiler) EstimateCost(result *CompilationResult) (CostEstimate, error) { costEst, err := c.env.EstimateCost(result.ast, &defaultCostEstimator{}) if err != nil { - return CostEstimate{} + return CostEstimate{}, fmt.Errorf("CEL cost estimation failed: %w", err) } - return CostEstimate{Min: costEst.Min, Max: costEst.Max} + return CostEstimate{Min: costEst.Min, Max: costEst.Max}, nil } // defaultCostEstimator provides size hints for compile-time cost estimation. diff --git a/pkg/cel/cost_test.go b/pkg/cel/cost_test.go index 4737aea4..9a068be4 100644 --- a/pkg/cel/cost_test.go +++ b/pkg/cel/cost_test.go @@ -33,7 +33,8 @@ func TestEstimateCost(t *testing.T) { prog, err := compiler.Compile(tc.expr) require.NoError(t, err) - est := compiler.EstimateCost(prog) + est, err := compiler.EstimateCost(prog) + require.NoError(t, err) assert.True(t, est.Max >= est.Min, "max cost should be >= min cost") assert.True(t, est.Max <= dexcel.DefaultCostBudget, "estimated max cost %d should be within default budget %d", est.Max, dexcel.DefaultCostBudget) diff --git a/pkg/cel/library/groups.go b/pkg/cel/library/groups.go index 6f58ce59..fd7f3603 100644 --- a/pkg/cel/library/groups.go +++ b/pkg/cel/library/groups.go @@ -1,7 +1,7 @@ package library import ( - "path/filepath" + "path" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" @@ -67,7 +67,11 @@ func groupMatchesImpl(lhs, rhs ref.Val) ref.Val { continue } - if ok, _ := filepath.Match(pattern, group); ok { + ok, err := path.Match(pattern, group) + if err != nil { + return types.NewErr("dex.groupMatches: invalid pattern %q: %v", pattern, err) + } + if ok { matched = append(matched, types.String(group)) } } diff --git a/pkg/cel/library/groups_test.go b/pkg/cel/library/groups_test.go index 53c9ba30..99663bd0 100644 --- a/pkg/cel/library/groups_test.go +++ b/pkg/cel/library/groups_test.go @@ -70,6 +70,23 @@ func TestGroupMatches(t *testing.T) { } } +func TestGroupMatchesInvalidPattern(t *testing.T) { + vars := dexcel.IdentityVariables() + compiler, err := dexcel.NewCompiler(vars) + require.NoError(t, err) + + prog, err := compiler.CompileStringList(`dex.groupMatches(identity.groups, "[invalid")`) + require.NoError(t, err) + + _, err = dexcel.Eval(context.Background(), prog, map[string]any{ + "identity": map[string]any{ + "groups": []string{"admin"}, + }, + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid pattern") +} + func TestGroupFilter(t *testing.T) { vars := dexcel.IdentityVariables() compiler, err := dexcel.NewCompiler(vars)