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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
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
144 changes: 144 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicy.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.joining;

import com.google.auto.value.AutoOneOf;
import com.google.auto.value.AutoValue;
Expand DownExpand Up@@ -53,6 +54,10 @@ public abstract class CelPolicy {

public abstract ImmutableList<Import> imports();

public abstract ImmutableList<Invariant> invariants();

public abstract ImmutableList<Variable> verificationVariables();

/** Creates a new builder to construct a {@link CelPolicy} instance. */
public static Builder newBuilder() {
return new AutoValue_CelPolicy.Builder()
Expand All@@ -74,6 +79,8 @@ public abstract static class Builder {

public abstract Builder setDisplayName(ValueString displayName);

public abstract Rule rule();

public abstract Builder setRule(Rule rule);

public abstract Builder setPolicySource(CelPolicySource policySource);
Expand All@@ -90,6 +97,14 @@ public List<Import> imports() {
return Collections.unmodifiableList(importList);
}

abstract ImmutableList<Invariant> invariants();

abstract ImmutableList.Builder<Invariant> invariantsBuilder();

abstract ImmutableList<Variable> verificationVariables();

abstract ImmutableList.Builder<Variable> verificationVariablesBuilder();

public Map<String, Object> metadata() {
return Collections.unmodifiableMap(metadata);
}
Expand All@@ -106,6 +121,24 @@ public Builder addImports(Collection<Import> values) {
return this;
}

@CanIgnoreReturnValue
public Builder addInvariant(Invariant value) {
invariantsBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariable(Variable value) {
verificationVariablesBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addVerificationVariables(Collection<Variable> values) {
verificationVariablesBuilder().addAll(values);
return this;
}

@CanIgnoreReturnValue
public Builder putMetadata(String key, Object value) {
metadata.put(key, value);
Expand DownExpand Up@@ -328,4 +361,115 @@ public static Import create(long id, ValueString name) {
return new AutoValue_CelPolicy_Import(id, name);
}
}

/**
* Invariant declares a required logical property that must hold true under specified
* preconditions.
*/
@AutoValue
public abstract static class Invariant {
public abstract long id();

public abstract ValueString invariantId();

public abstract Optional<ValueString> description();

public abstract ImmutableList<ValueString> assume();

public abstract ImmutableList<ValueString> assertClause();

public String assumeSourceString() {
if (assume().isEmpty()) {
return "true";
}
if (assume().size() == 1) {
return assume().get(0).value();
}
return assume().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

public String assertSourceString() {
if (assertClause().isEmpty()) {
return "true";
}
if (assertClause().size() == 1) {
return assertClause().get(0).value();
}
return assertClause().stream().map(v -> "(" + v.value() + ")").collect(joining(" && "));
}

/** Builder for {@link Invariant}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {
public abstract Builder setId(long value);

abstract Optional<ValueString> invariantId();

abstract ImmutableList<ValueString> assume();

abstract ImmutableList.Builder<ValueString> assumeBuilder();

abstract ImmutableList<ValueString> assertClause();

abstract ImmutableList.Builder<ValueString> assertClauseBuilder();

public abstract Builder setInvariantId(ValueString value);

public abstract Builder setDescription(ValueString value);

public Builder setAssume(ValueString value) {
return setAssume(ImmutableList.of(value));
}

abstract Builder setAssume(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssume(ValueString value) {
assumeBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssume(Iterable<ValueString> values) {
assumeBuilder().addAll(values);
return this;
}

public Builder setAssertClause(ValueString value) {
return setAssertClause(ImmutableList.of(value));
}

abstract Builder setAssertClause(ImmutableList<ValueString> values);

@CanIgnoreReturnValue
public Builder addAssertClause(ValueString value) {
assertClauseBuilder().add(value);
return this;
}

@CanIgnoreReturnValue
public Builder addAssertClause(Iterable<ValueString> values) {
assertClauseBuilder().addAll(values);
return this;
}

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(
RequiredField.of("id", this::invariantId),
RequiredField.of(
"assert",
() ->
assertClause().isEmpty()
? Optional.empty()
: Optional.of(assertClause().get(0))));
}

public abstract Invariant build();
}

public static Builder newBuilder(long id) {
return new AutoValue_CelPolicy_Invariant.Builder().setId(id);
}
}
}
145 changes: 145 additions & 0 deletions policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
package dev.cel.policy;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static dev.cel.common.formats.YamlHelper.ERROR;
import static dev.cel.common.formats.YamlHelper.assertRequiredFields;
import static dev.cel.common.formats.YamlHelper.assertYamlType;
Expand All@@ -28,6 +29,7 @@
import dev.cel.common.formats.YamlParserContextImpl;
import dev.cel.common.internal.CelCodePointArray;
import dev.cel.policy.CelPolicy.Import;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Match.Result;
import dev.cel.policy.CelPolicy.Variable;
Expand All@@ -47,6 +49,8 @@ final class CelPolicyYamlParser implements CelPolicyParser {
Match.newBuilder(0).setCondition(ERROR_VALUE).setResult(Result.ofOutput(ERROR_VALUE)).build();
private static final Variable ERROR_VARIABLE =
Variable.newBuilder().setExpression(ERROR_VALUE).setName(ERROR_VALUE).build();
private static final Invariant ERROR_INVARIANT =
Invariant.newBuilder(0).setInvariantId(ERROR_VALUE).setAssertClause(ERROR_VALUE).build();

private final TagVisitor<Node> tagVisitor;
private final boolean enableSimpleVariables;
Expand DownExpand Up@@ -137,17 +141,82 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
case "rule":
policyBuilder.setRule(parseRule(ctx, policyBuilder, valueNode));
break;
case "verification":
parseVerification(policyBuilder, ctx, valueNode);
break;
default:
tagVisitor.visitPolicyTag(ctx, keyId, fieldName, valueNode, policyBuilder);
break;
}
}

ImmutableSet<String> ruleVarNames =
policyBuilder.rule().variables().stream()
.map(CelPolicy.Variable::name)
.filter(name -> !name.equals(ERROR_VALUE))
.map(ValueString::value)
.collect(toImmutableSet());
for (Variable verVar : policyBuilder.verificationVariables()) {
if (!verVar.name().equals(ERROR_VALUE)
&& ruleVarNames.contains(verVar.name().value())) {
ctx.reportError(
verVar.name().id(),
"Duplicate variable name '"
+ verVar.name().value()
+ "' in verification.variables; already defined in rule.variables");
}
}

return policyBuilder
.setPolicySource(policySource.toBuilder().setPositionsMap(ctx.getIdToOffsetMap()).build())
.build();
}

private void parseVerification(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return;
}
MappingNode mappingNode = (MappingNode) node;
for (NodeTuple nodeTuple : mappingNode.getValue()) {
Node key = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(key);
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
String fieldName = ((ScalarNode) key).getValue();
Node valueNode = nodeTuple.getValueNode();
switch (fieldName) {
case "invariants": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode invariantListNode = (SequenceNode) valueNode;
for (Node invariantNode : invariantListNode.getValue()) {
policyBuilder.addInvariant(parseInvariant(ctx, policyBuilder, invariantNode));
}
break;
}
case "variables": {
long valueId = ctx.collectMetadata(valueNode);
if (!assertYamlType(ctx, valueId, valueNode, YamlNodeType.LIST)) {
continue;
}
SequenceNode variableListNode = (SequenceNode) valueNode;
for (Node varNode : variableListNode.getValue()) {
policyBuilder.addVerificationVariable(parseVariable(ctx, policyBuilder, varNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in verification block: " + fieldName);
break;
}
}
}

private void parseImports(
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
long id = ctx.collectMetadata(node);
Expand DownExpand Up@@ -409,6 +478,82 @@ private Variable parseVariableObject(
return builder.build();
}

@Override
public CelPolicy.Invariant parseInvariant(
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {
long id = ctx.collectMetadata(node);
Invariant.Builder builder = Invariant.newBuilder(id);
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
return ERROR_INVARIANT;
}

MappingNode invariantMap = (MappingNode) node;
for (NodeTuple nodeTuple : invariantMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
if (!assertYamlType(ctx, keyId, keyNode, YamlNodeType.STRING, YamlNodeType.TEXT)) {
continue;
}
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "id":
builder.setInvariantId(ctx.newYamlString(valueNode));
break;
case "description":
builder.setDescription(ctx.newYamlString(valueNode));
break;
case "assume": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssume(ctx.newSourceString(itemNode));
}
} else {
builder.addAssume(ctx.newSourceString(valueNode));
}
break;
}
case "assert": {
if (!assertYamlType(
ctx,
ctx.collectMetadata(valueNode),
valueNode,
YamlNodeType.STRING,
YamlNodeType.TEXT,
YamlNodeType.LIST)) {
break;
}
if (valueNode instanceof SequenceNode) {
for (Node itemNode : ((SequenceNode) valueNode).getValue()) {
builder.addAssertClause(ctx.newSourceString(itemNode));
}
} else {
builder.addAssertClause(ctx.newSourceString(valueNode));
}
break;
}
default:
ctx.reportError(keyId, "Unexpected key in invariant block: " + keyName);
break;
}
}

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

return builder.build();
}

private ParserImpl(
TagVisitor<Node> tagVisitor,
boolean enableSimpleVariables,
Expand Down
3 changes: 3 additions & 0 deletions policy/src/main/java/dev/cel/policy/PolicyParserContext.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@

import com.google.auto.value.AutoValue;
import dev.cel.common.formats.ParserContext;
import dev.cel.policy.CelPolicy.Invariant;
import dev.cel.policy.CelPolicy.Match;
import dev.cel.policy.CelPolicy.Rule;
import dev.cel.policy.CelPolicy.Variable;
Expand DownExpand Up@@ -51,4 +52,6 @@ static NewPolicyMetadata create(CelPolicySource source, long id) {
Match parseMatch(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Variable parseVariable(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);

Invariant parseInvariant(PolicyParserContext<T> ctx, CelPolicy.Builder policyBuilder, T node);
}
Loading
Loading