package expressions import ( "math/rand/v2" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/ext" ) // NewEnvironment creates a new CEL environment, this is the set of // variables and functions that are passed into the CEL scope so that // Anubis can fail loudly and early when something is invalid instead // of blowing up at runtime. func NewEnvironment() (*cel.Env, error) { return cel.NewEnv( ext.Strings( ext.StringsLocale("en_US"), ext.StringsValidateFormatCalls(true), ), // default all timestamps to UTC cel.DefaultUTCTimeZone(true), // Variables exposed to CEL programs: cel.Variable("remoteAddress", cel.StringType), cel.Variable("host", cel.StringType), cel.Variable("method", cel.StringType), cel.Variable("userAgent", cel.StringType), cel.Variable("path", cel.StringType), cel.Variable("query", cel.MapType(cel.StringType, cel.StringType)), cel.Variable("headers", cel.MapType(cel.StringType, cel.StringType)), // Functions exposed to CEL programs: cel.Function("randInt", cel.Overload("randInt_int", []*cel.Type{cel.IntType}, cel.IntType, cel.UnaryBinding(func(val ref.Val) ref.Val { n, ok := val.(types.Int) if !ok { return types.ValOrErr(val, "value is not an integer, but is %T", val) } return types.Int(rand.IntN(int(n))) }), ), ), ) } // Compile takes CEL environment and syntax tree then emits an optimized // Program for execution. func Compile(env *cel.Env, ast *cel.Ast) (cel.Program, error) { return env.Program( ast, cel.EvalOptions( // optimize regular expressions right now instead of on the fly cel.OptOptimize, ), ) }