Repository: cerbos/cerbos (go)

Issue:
Upgrade to CEL 0.17
We need to replace uses of some of the deprecated functions and members but that's pretty trivial.

All tests pass except for partial evaluation of `now() > timestamp("2021-04-20") && R.attr.geo in ["GB", "US"]`. There's a small bug in the CEL library (https://github.com/google/cel-go/issues/785) but even with that bug fixed, we might not get the expected result from the new CEL version. It would return `timestamp("CURRENT_TIMESTAMP") > timestamp("2021-04-20") && R.attr.geo in ["GB", "US"]` rather than the expected result of `R.attr.geo in ["GB", "US"]`.

Pull request:
chore: Upgrade to CEL 0.17
Fixes #1713

Signed-off-by: Charith Ellawala <charith@cerbos.dev>


Patch:
diff --git a/cmd/cerbos/repl/internal/repl.go b/cmd/cerbos/repl/internal/repl.go
index 1f675787..e9bbb5b1 100644
--- a/cmd/cerbos/repl/internal/repl.go
+++ b/cmd/cerbos/repl/internal/repl.go
@@ -97,7 +97,7 @@ func NewREPL(reader *liner.State, output Output) (*REPL, error) {
 		reader:   reader,
 		parser:   parser,
 		output:   output,
-		toRefVal: conditions.StdEnv.TypeAdapter().NativeToValue,
+		toRefVal: conditions.StdEnv.CELTypeAdapter().NativeToValue,
 	}
 
 	return repl, repl.reset()
@@ -410,12 +410,17 @@ func (r *REPL) evalExpr(expr string) (ref.Val, *exprpb.Type, error) {
 		return nil, nil, err
 	}
 
-	tpe := decls.Dyn
-	if t, ok := env.TypeProvider().FindType(val.Type().TypeName()); ok {
+	tpe := types.DynType
+	if t, ok := env.CELTypeProvider().FindStructType(val.Type().TypeName()); ok {
 		tpe = t
 	}
 
-	return val, tpe, nil
+	exprpbTpe, err := types.TypeToExprType(tpe)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return val, exprpbTpe, nil
 }
 
 func (r *REPL) loadPolicy(path string) error {
diff --git a/go.mod b/go.mod
index 6585c945..a69353d0 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
 	github.com/go-sql-driver/mysql v1.7.1
 	github.com/gobwas/glob v0.2.3
 	github.com/golang-migrate/migrate/v4 v4.16.2
-	github.com/google/cel-go v0.15.2
+	github.com/google/cel-go v0.17.1
 	github.com/google/go-cmp v0.5.9
 	github.com/google/gops v0.3.27
 	github.com/google/uuid v1.3.0
diff --git a/go.sum b/go.sum
index 68c057ba..456e041c 100644
--- a/go.sum
+++ b/go.sum
@@ -370,8 +370,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/cel-go v0.15.2 h1:jX8b/sSNoE1JBaxLAVDb+rTuEk9F7+Yd8UVKWjoz7SU=
-github.com/google/cel-go v0.15.2/go.mod h1:YzWEoI07MC/a/wj9in8GeVatqfypkldgBlwXh9bCwqY=
+github.com/google/cel-go v0.17.1 h1:s2151PDGy/eqpCI80/8dl4VL3xTkqI/YubXLXCFw0mw=
+github.com/google/cel-go v0.17.1/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
 github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM=
 github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
diff --git a/internal/conditions/cel.go b/internal/conditions/cel.go
index 39d1fe51..ae8a9636 100644
--- a/internal/conditions/cel.go
+++ b/internal/conditions/cel.go
@@ -78,7 +78,7 @@ func initEnv(options []cel.EnvOption) (*cel.Env, error) {
 		return nil, err
 	}
 
-	cctp := types.NewCamelCaseFieldProvider(env.TypeProvider())
+	cctp := types.NewCamelCaseFieldProvider(env.CELTypeProvider())
 	return env.Extend(cel.CustomTypeProvider(cctp))
 }
 
diff --git a/internal/conditions/types/hierarchy.go b/internal/conditions/types/hierarchy.go
index ecd1e7d1..2a839551 100644
--- a/internal/conditions/types/hierarchy.go
+++ b/internal/conditions/types/hierarchy.go
@@ -32,7 +32,7 @@ const (
 )
 
 var (
-	HierarchyType = types.NewTypeValue(hierarchyTypeName,
+	HierarchyType = cel.ObjectType(hierarchyTypeName,
 		traits.IndexerType,
 		traits.SizerType,
 		traits.ReceiverType)
diff --git a/internal/conditions/types/jsonfield.go b/internal/conditions/types/jsonfield.go
index 24123518..8d90eabc 100644
--- a/internal/conditions/types/jsonfield.go
+++ b/internal/conditions/types/jsonfield.go
@@ -4,24 +4,24 @@
 package types
 
 import (
-	"github.com/google/cel-go/common/types/ref"
+	"github.com/google/cel-go/common/types"
 	"github.com/iancoleman/strcase"
 )
 
 // JSONFieldProvider is a custom type provider that allows protobuf fields to be accessed by their JSON name (camel case).
 type JSONFieldProvider struct {
-	ref.TypeProvider
+	types.Provider
 }
 
-func NewCamelCaseFieldProvider(tp ref.TypeProvider) *JSONFieldProvider {
-	return &JSONFieldProvider{TypeProvider: tp}
+func NewCamelCaseFieldProvider(tp types.Provider) *JSONFieldProvider {
+	return &JSONFieldProvider{Provider: tp}
 }
 
-func (ccfp *JSONFieldProvider) FindFieldType(msgType, fieldName string) (*ref.FieldType, bool) {
-	if ft, ok := ccfp.TypeProvider.FindFieldType(msgType, fieldName); ok {
+func (ccfp *JSONFieldProvider) FindStructFieldType(msgType, fieldName string) (*types.FieldType, bool) {
+	if ft, ok := ccfp.Provider.FindStructFieldType(msgType, fieldName); ok {
 		return ft, ok
 	}
 
 	sc := strcase.ToSnake(fieldName)
-	return ccfp.TypeProvider.FindFieldType(msgType, sc)
+	return ccfp.Provider.FindStructFieldType(msgType, sc)
 }
diff --git a/internal/engine/planner/planner.go b/internal/engine/planner/planner.go
index 9997da7a..bd79faa4 100644
--- a/internal/engine/planner/planner.go
+++ b/internal/engine/planner/planner.go
@@ -14,6 +14,9 @@ import (
 	"github.com/google/cel-go/common/types"
 	exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
 
+	"github.com/google/cel-go/common/types/ref"
+	"github.com/google/cel-go/interpreter"
+
 	effectv1 "github.com/cerbos/cerbos/api/genpb/cerbos/effect/v1"
 	enginev1 "github.com/cerbos/cerbos/api/genpb/cerbos/engine/v1"
 	runtimev1 "github.com/cerbos/cerbos/api/genpb/cerbos/runtime/v1"
@@ -25,8 +28,6 @@ import (
 	"github.com/cerbos/cerbos/internal/observability/tracing"
 	"github.com/cerbos/cerbos/internal/schema"
 	"github.com/cerbos/cerbos/internal/util"
-	"github.com/google/cel-go/common/types/ref"
-	"github.com/google/cel-go/interpreter"
 )
 
 type (
diff --git a/internal/policy/validate.go b/internal/policy/validate.go
index 25f284fa..3ee5dd59 100644
--- a/internal/policy/validate.go
+++ b/internal/policy/validate.go
@@ -107,7 +107,8 @@ func validateDerivedRoles(dr *policyv1.DerivedRoles) (err error) {
 }
 
 func validateExportVariables(p *policyv1.Policy) error {
-	if len(p.Variables) > 0 { //nolint:staticcheck
+	//nolint:staticcheck
+	if len(p.Variables) > 0 {
 		return fmt.Errorf("export variables policies do not support the deprecated top-level variables field")
 	}
 


Test patch:
diff --git a/cmd/cerbos/repl/internal/repl_test.go b/cmd/cerbos/repl/internal/repl_test.go
index 9add4e43..e5f4cfc3 100644
--- a/cmd/cerbos/repl/internal/repl_test.go
+++ b/cmd/cerbos/repl/internal/repl_test.go
@@ -31,7 +31,7 @@ type DirectiveTest struct {
 }
 
 func TestREPL(t *testing.T) {
-	toRefVal := conditions.StdEnv.TypeAdapter().NativeToValue
+	toRefVal := conditions.StdEnv.CELTypeAdapter().NativeToValue
 	drPath := filepath.Join(test.PathToDir(t, "store"), "derived_roles", "derived_roles_01.yaml")
 	rpPath := filepath.Join(test.PathToDir(t, "store"), "resource_policies", "policy_01.yaml")
 	ppPath := filepath.Join(test.PathToDir(t, "store"), "principal_policies", "policy_01.yaml")
diff --git a/internal/engine/planner/planner_test.go b/internal/engine/planner/planner_test.go
index 6a60dd52..0cbadc64 100644
--- a/internal/engine/planner/planner_test.go
+++ b/internal/engine/planner/planner_test.go
@@ -244,7 +244,7 @@ func TestResidualExpr(t *testing.T) {
 		`V.info.language + "_" + V.info.country == gbLoc`,
 		`has(R.attr.geo) && R.attr.geo in ["GB", "US"]`,
 		"has(V.info.language)",
-		`now() > timestamp("2021-04-20") && R.attr.geo in ["GB", "US"]`,
+		`now() > timestamp("2021-04-20T00:00:00Z") && R.attr.geo in ["GB", "US"]`,
 		`timestamp(R.attr.lastAccessed) > now()`,
 	}
 
@@ -320,7 +320,7 @@ func TestPartialEvaluationWithGlobalVars(t *testing.T) {
 			want: "R.attr.items.filter(x, x.price > 100)",
 		},
 		{
-			expr: `now() > timestamp("2021-04-20") && R.attr.geo in ["GB", "US"]`,
+			expr: `now() > timestamp("2021-04-20T00:00:00Z") && R.attr.geo in ["GB", "US"]`,
 			want: `R.attr.geo in ["GB", "US"]`,
 		},
 		{