Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyParserBuilder.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,30 @@ public interface CelPolicyParserBuilder<T> {
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> addTagVisitor(TagVisitor<T> tagVisitor);

/**
* Configures the parser to allow for key-value pairs to declare a variable name and expression.
*
* <p>For example:
*
* <pre>{@code
* variables:
* - foo: bar
* - baz: qux
* }</pre>
*
* <p>This is in contrast to the default behavior, which requires the following syntax:
*
* <pre>{@code
* variables:
* - name: foo
* expression: bar
* - name: baz
* expression: qux
* }</pre>
*/
@CanIgnoreReturnValue
CelPolicyParserBuilder<T> enableSimpleVariables(boolean enable);

/** Builds a new instance of {@link CelPolicyParser}. */
@CheckReturnValue
CelPolicyParser build();
Expand Down
65 changes: 59 additions & 6 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;

@Override
public CelPolicy parse(String policySource) throws CelPolicyValidationException {
Expand All@@ -58,13 +59,15 @@ public CelPolicy parse(String policySource) throws CelPolicyValidationException
@Override
public CelPolicy parse(String policySource, String description)
throws CelPolicyValidationException {
ParserImpl parser = new ParserImpl(tagVisitor, policySource, description);
ParserImpl parser =
new ParserImpl(tagVisitor, enableSimpleVariables, policySource, description);
return parser.parseYaml();
}

private static class ParserImpl implements PolicyParserContext<Node> {

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
private final CelPolicySource policySource;
private final ParserContext<Node> ctx;

Expand DownExpand Up@@ -336,9 +339,45 @@ public CelPolicy.Variable parseVariable(
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_VARIABLE;
}

MappingNode variableMap = (MappingNode) node;
Variable.Builder builder = Variable.newBuilder();

if (enableSimpleVariables) {
return parseVariableInline(ctx, id, variableMap, builder);
}
return parseVariableObject(ctx, policyBuilder, id, variableMap, builder);
}

private Variable parseVariableInline(
PolicyParserContext<Node> ctx, long id, MappingNode variableMap, Variable.Builder builder) {
int iterations = 0;
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
builder
.setName(ctx.newValueString(keyNode))
.setExpression(ctx.newValueString(nodeTuple.getValueNode()));
iterations++;

if (iterations > 1) {
ctx.reportError(keyId, "Only one variable may be defined inline");
}
}

if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
return ERROR_VARIABLE;
}

return builder.build();
}

private Variable parseVariableObject(
PolicyParserContext<Node> ctx,
CelPolicy.Builder policyBuilder,
long id,
MappingNode variableMap,
Variable.Builder builder) {
for (NodeTuple nodeTuple : variableMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Expand DownExpand Up@@ -370,8 +409,13 @@ public CelPolicy.Variable parseVariable(
return builder.build();
}

private ParserImpl(TagVisitor<Node> tagVisitor, String source, String description) {
private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
String source,
String description) {
this.tagVisitor = tagVisitor;
this.enableSimpleVariables = enableSimpleVariables;
this.policySource =
CelPolicySource.newBuilder(CelCodePointArray.fromString(source))
.setDescription(description)
Expand DownExpand Up@@ -413,9 +457,11 @@ public ValueString newValueString(Node node) {
static final class Builder implements CelPolicyParserBuilder<Node> {

private TagVisitor<Node> tagVisitor;
private boolean enableSimpleVariables;

private Builder() {
this.tagVisitor = new TagVisitor<Node>() {};
this.enableSimpleVariables = false;
}

@Override
Expand All@@ -424,17 +470,24 @@ public CelPolicyParserBuilder<Node> addTagVisitor(TagVisitor<Node> tagVisitor) {
return this;
}

@Override
public CelPolicyParserBuilder<Node> enableSimpleVariables(boolean enable) {
this.enableSimpleVariables = enable;
return this;
}

@Override
public CelPolicyParser build() {
return new CelPolicyYamlParser(tagVisitor);
return new CelPolicyYamlParser(tagVisitor, enableSimpleVariables);
}
}

static Builder newBuilder() {
return new Builder();
static CelPolicyParserBuilder<Node> newBuilder() {
return new Builder().enableSimpleVariables(false);
}

private CelPolicyYamlParser(TagVisitor<Node> tagVisitor) {
private CelPolicyYamlParser(TagVisitor<Node> tagVisitor, boolean enableSimpleVariables) {
this.tagVisitor = checkNotNull(tagVisitor);
this.enableSimpleVariables = enableSimpleVariables;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -302,6 +302,29 @@ public void evaluateYamlPolicy_lateBoundFunction() throws Exception {
assertThat(evalResult).isEqualTo("foo" + exampleValue);
}

@Test
public void evaluateYamlPolicy_withSimpleVariable() throws Exception {
Cel cel = newCel();
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " - second: 'false'\n"
+ " match:\n"
+ " - output: 'variables.first && variables.second'";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();
CelPolicy policy = parser.parse(policySource);

CelAbstractSyntaxTree compiledPolicyAst =
CelPolicyCompilerFactory.newPolicyCompiler(cel).build().compile(policy);

boolean evalResult = (boolean) cel.createProgram(compiledPolicyAst).eval();

assertThat(evalResult).isFalse();
}

private static final class EvaluablePolicyTestData {
private final TestYamlPolicy yamlPolicy;
private final PolicyTestCase testCase;
Expand Down
24 changes: 24 additions & 0 deletions policy/src/test/java/dev/cel/policy/CelPolicyYamlParserTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,6 +148,30 @@ public void parseYamlPolicy_withImports() throws Exception {
.inOrder();
}

@Test
public void parseYamlPolicy_withSimpleVariable_multipleInlinedVariables() {
String policySource =
"name: shorthand_variables_policy\n"
+ "rule:\n"
+ " variables:\n"
+ " - first: 'true'\n"
+ " second: 'false'\n"
+ " match:\n"
+ " - condition: 'variables.my_var'\n"
+ " output: 'true'\n";
CelPolicyParser parser =
CelPolicyParserFactory.newYamlParserBuilder().enableSimpleVariables(true).build();

CelPolicyValidationException e =
assertThrows(CelPolicyValidationException.class, () -> parser.parse(policySource));
assertThat(e)
.hasMessageThat()
.contains(
"ERROR: <input>:5:7: Only one variable may be defined inline\n"
+ " | second: 'false'\n"
+ " | ......^");
}

@Test
public void parseYamlPolicy_errors(@TestParameter PolicyParseErrorTestCase testCase) {
CelPolicyValidationException e =
Expand Down
Loading