diff --git a/policy/src/main/java/dev/cel/policy/tools/CelPolicyCompilerTool.java b/policy/src/main/java/dev/cel/policy/tools/CelPolicyCompilerTool.java index 2eb0c247e..e1e16ba6b 100644 --- a/policy/src/main/java/dev/cel/policy/tools/CelPolicyCompilerTool.java +++ b/policy/src/main/java/dev/cel/policy/tools/CelPolicyCompilerTool.java @@ -120,6 +120,11 @@ public final class CelPolicyCompilerTool implements Callable { description = "Optimize field selection for version skew mitigation") private boolean optimizeFieldSelection = false; + @Option( + names = {"--simple_variables"}, + description = "Enable inline variable definitions (e.g., '- var_name: expr') in the policy") + private boolean simpleVariables = false; + private static final CelOptions CEL_OPTIONS = CelOptions.current() .populateMacroCalls(true) @@ -195,7 +200,9 @@ public Integer call() { CelPolicy policy; try { CelPolicyParser policyParser = - CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build(); + CelPolicyParserFactory.newYamlParserBuilder() + .enableSimpleVariables(simpleVariables) + .build(); String policyYaml = new String(readFileBytes(effectivePolicyPath), UTF_8); policy = policyParser.parse(policyYaml, effectivePolicyPath); } catch (Exception e) { diff --git a/policy/src/test/java/dev/cel/policy/tools/BUILD.bazel b/policy/src/test/java/dev/cel/policy/tools/BUILD.bazel index 21f9f92f0..8a3c55c40 100644 --- a/policy/src/test/java/dev/cel/policy/tools/BUILD.bazel +++ b/policy/src/test/java/dev/cel/policy/tools/BUILD.bazel @@ -19,6 +19,7 @@ compile_cel_policy( optimize_field_selection = True, policy = ":test_policy.yaml", proto_srcs = ["@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_proto"], + simple_variables = True, ) java_library( @@ -28,7 +29,6 @@ java_library( data = [ ":compiled_test_policy.binarypb", ":test_all_types_fds", - ":test_policy.yaml", "//testing/environment:proto3_message_variables", ], deps = [ diff --git a/policy/src/test/java/dev/cel/policy/tools/CelPolicyCompilerToolTest.java b/policy/src/test/java/dev/cel/policy/tools/CelPolicyCompilerToolTest.java index 5257bfa0b..a14e72402 100644 --- a/policy/src/test/java/dev/cel/policy/tools/CelPolicyCompilerToolTest.java +++ b/policy/src/test/java/dev/cel/policy/tools/CelPolicyCompilerToolTest.java @@ -233,6 +233,39 @@ public void compile_withBaseConfig_success() throws Exception { assertThat(result).isEqualTo(Optional.of(true)); } + @Test + public void compile_withVariables_success() throws Exception { + String configPath = createFile("config.yaml", "name: test-env\n"); + String policyPath = + createFile( + "policy.yaml", + "name: p\n" + + "rule:\n" + + " variables:\n" + + " - name: my_sum\n" + + " expression: 10 + 20\n" + + " match:\n" + + " - condition: variables.my_sum == 30\n" + + " output: 'true'\n"); + File outputFile = tempFolder.newFile("output.binarypb"); + CommandLine cmd = new CommandLine(new CelPolicyCompilerTool()); + + int exitCode = + cmd.execute( + "--policy", + policyPath, + "--config", + configPath, + "--output", + outputFile.getAbsolutePath()); + + assertThat(exitCode).isEqualTo(0); + CheckedExpr checkedExpr = + CheckedExpr.parseFrom(Files.toByteArray(outputFile), ExtensionRegistry.getEmptyRegistry()); + CelAbstractSyntaxTree ast = CelProtoAbstractSyntaxTree.fromCheckedExpr(checkedExpr).getAst(); + assertThat(celRuntime.createProgram(ast).eval()).isEqualTo(Optional.of(true)); + } + @Test public void compile_withSimpleVariables_success() throws Exception { String configPath = createFile("config.yaml", "name: test-env\n"); @@ -246,26 +279,24 @@ public void compile_withSimpleVariables_success() throws Exception { + " match:\n" + " - condition: variables.my_sum == 30\n" + " output: 'true'\n"); - File outputFile = tempFolder.newFile("output.binarypb"); - CommandLine cmd = new CommandLine(new CelPolicyCompilerTool()); + int exitCode = cmd.execute( "--policy", policyPath, "--config", configPath, + "--simple_variables", "--output", outputFile.getAbsolutePath()); assertThat(exitCode).isEqualTo(0); - CheckedExpr checkedExpr = CheckedExpr.parseFrom(Files.toByteArray(outputFile), ExtensionRegistry.getEmptyRegistry()); CelAbstractSyntaxTree ast = CelProtoAbstractSyntaxTree.fromCheckedExpr(checkedExpr).getAst(); - Object result = celRuntime.createProgram(ast).eval(); - assertThat(result).isEqualTo(Optional.of(true)); + assertThat(celRuntime.createProgram(ast).eval()).isEqualTo(Optional.of(true)); } @Test diff --git a/policy/src/test/java/dev/cel/policy/tools/test_policy.yaml b/policy/src/test/java/dev/cel/policy/tools/test_policy.yaml index afed6485d..da60a0694 100644 --- a/policy/src/test/java/dev/cel/policy/tools/test_policy.yaml +++ b/policy/src/test/java/dev/cel/policy/tools/test_policy.yaml @@ -1,7 +1,9 @@ # Copyright 2026 Google LLC name: "test_policy" rule: + variables: + - is_valid_int: proto3.single_int32 == 100 match: - - condition: proto3.single_int32 == 100 && has(proto3.single_nested_message) + - condition: variables.is_valid_int && has(proto3.single_nested_message) output: '"ALLOW"' - output: '"DENY"' diff --git a/policy/tools/BUILD.bazel b/policy/tools/BUILD.bazel index 3596d7c23..2f320b1f4 100644 --- a/policy/tools/BUILD.bazel +++ b/policy/tools/BUILD.bazel @@ -22,6 +22,5 @@ alias( bzl_library( name = "compile_cel_policy_bzl", srcs = ["compile_cel_policy.bzl"], - visibility = ["//visibility:private"], deps = ["@rules_proto//proto:defs"], ) diff --git a/policy/tools/compile_cel_policy.bzl b/policy/tools/compile_cel_policy.bzl index 2f8b42301..97214059f 100644 --- a/policy/tools/compile_cel_policy.bzl +++ b/policy/tools/compile_cel_policy.bzl @@ -26,6 +26,7 @@ def compile_cel_policy( output_format = "binarypb", output_version = "canonical", optimize_field_selection = False, + simple_variables = False, visibility = None): """Compiles a CEL policy into a CheckedExpr binarypb or textpb with optional select optimization. @@ -43,7 +44,13 @@ def compile_cel_policy( output: (optional) str file name for the output checked expression (default derived from label name and format) output_format: (optional) str either "binarypb", "textpb", or "textproto" (default "binarypb") output_version: (optional) str either "canonical" or "v1alpha1" (default "canonical") - optimize_field_selection: (optional) bool whether to enable AST select optimization (default False) + optimize_field_selection: (optional) bool whether to enable AST select optimization (default False). + When True, embeds field numbers, wire types, and default values directly into the compiled + `CheckedExpr` AST. This enables version-skew and field-rename resilience (evaluating unknown + fields from raw wire bytes on older clients) and faster field selections, at the cost of a + larger serialized AST payload. + simple_variables: (optional) bool whether to enable inline variable definitions (e.g., + `- var_name: expr` instead of `- name: var_name` / `expression: expr`) in the policy (default False) visibility: (optional) visibility to use on the genrule macro (default None) """ if output_format not in ("binarypb", "textpb", "textproto"): @@ -87,6 +94,9 @@ def compile_cel_policy( if optimize_field_selection: args.append("--optimize_field_selection") + if simple_variables: + args.append("--simple_variables") + cmd = ( "$(location //policy/tools:cel_policy_compiler_tool) " + " ".join(args)