Skip to content

Commit

Permalink
Add duration and regex literal validators
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 561101227
  • Loading branch information
l46kok authored and copybara-github committed Aug 29, 2023
1 parent 5593808 commit 149f12d
Show file tree
Hide file tree
Showing 10 changed files with 686 additions and 53 deletions.
44 changes: 43 additions & 1 deletion validator/src/main/java/dev/cel/validator/validators/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,55 @@ java_library(
],
tags = [
],
deps = [
":literal_validator",
"@maven//:com_google_protobuf_protobuf_java",
],
)

java_library(
name = "duration",
srcs = [
"DurationLiteralValidator.java",
],
tags = [
],
deps = [
":literal_validator",
"@maven//:com_google_protobuf_protobuf_java",
],
)

java_library(
name = "regex",
srcs = [
"RegexLiteralValidator.java",
],
tags = [
],
deps = [
"//bundle:cel",
"//common/ast",
"//common/navigation",
"//validator:ast_validator",
"@maven//:com_google_guava_guava",
],
)

java_library(
name = "literal_validator",
srcs = [
"LiteralValidator.java",
],
tags = [
],
visibility = ["//visibility:private"],
deps = [
"//bundle:cel",
"//common",
"//common/ast",
"//common/navigation",
"//validator:ast_validator",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.validator.validators;

import com.google.protobuf.Duration;

/** DurationLiteralValidator ensures that duration literal arguments are valid. */
public class DurationLiteralValidator extends LiteralValidator {
public static final DurationLiteralValidator INSTANCE =
new DurationLiteralValidator("duration", Duration.class);

private DurationLiteralValidator(String functionName, Class<?> expectedResultType) {
super(functionName, expectedResultType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.validator.validators;

import com.google.common.collect.ImmutableList;
import dev.cel.bundle.Cel;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelSource;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
import dev.cel.common.navigation.CelNavigableAst;
import dev.cel.common.navigation.CelNavigableExpr;
import dev.cel.validator.CelAstValidator;
import java.util.Optional;

/**
* LiteralValidator defines the common logic to handle simple validation of a literal in a function
* call by evaluating it and ensuring that no errors are thrown (example: duration / timestamp
* literals).
*/
abstract class LiteralValidator implements CelAstValidator {
private final String functionName;
private final Class<?> expectedResultType;

protected LiteralValidator(String functionName, Class<?> expectedResultType) {
this.functionName = functionName;
this.expectedResultType = expectedResultType;
}

@Override
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
navigableAst
.getRoot()
.descendants()
.filter(
node ->
node.getKind().equals(Kind.CONSTANT)
&& node.parent()
.map(
parent -> parent.expr().callOrDefault().function().equals(functionName))
.orElse(false))
.map(CelNavigableExpr::expr)
.forEach(
expr -> {
CelExpr callExpr =
CelExpr.ofCallExpr(
1,
Optional.empty(),
functionName,
ImmutableList.of(CelExpr.ofConstantExpr(2, expr.constant())));

CelAbstractSyntaxTree ast =
CelAbstractSyntaxTree.newParsedAst(callExpr, CelSource.newBuilder().build());
try {
ast = cel.check(ast).getAst();
Object result = cel.createProgram(ast).eval();

if (!expectedResultType.isInstance(result)) {
throw new IllegalStateException(
String.format(
"Expected %s type but got %s instead",
expectedResultType.getName(), result.getClass().getName()));
}

} catch (Exception e) {
issuesFactory.addError(
expr.id(),
String.format(
"%s validation failed. Reason: %s", functionName, e.getMessage()));
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dev.cel.validator.validators;

import com.google.common.collect.ImmutableList;
import dev.cel.bundle.Cel;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
import dev.cel.common.navigation.CelNavigableAst;
import dev.cel.validator.CelAstValidator;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/** RegexLiteralValidator ensures that regex patterns are valid. */
public class RegexLiteralValidator implements CelAstValidator {
public static final RegexLiteralValidator INSTANCE = new RegexLiteralValidator();

@Override
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
navigableAst
.getRoot()
.descendants()
.filter(node -> node.expr().callOrDefault().function().equals("matches"))
.filter(node -> ImmutableList.of(1, 2).contains(node.expr().call().args().size()))
.map(node -> node.expr().call())
.forEach(
matchesCallExpr -> {
CelExpr regexArg =
matchesCallExpr.target().isPresent()
? matchesCallExpr.args().get(0)
: matchesCallExpr.args().get(1);
if (!regexArg.exprKind().getKind().equals(Kind.CONSTANT)) {
return;
}

String regexPattern = regexArg.constant().stringValue();
try {
Pattern.compile(regexPattern);
} catch (PatternSyntaxException e) {
issuesFactory.addError(
regexArg.id(), "Regex validation failed. Reason: " + e.getMessage());
}
});
}

private RegexLiteralValidator() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,14 @@

package dev.cel.validator.validators;

import com.google.common.collect.ImmutableList;
import com.google.protobuf.Timestamp;
import dev.cel.bundle.Cel;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelSource;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
import dev.cel.common.navigation.CelNavigableAst;
import dev.cel.common.navigation.CelNavigableExpr;
import dev.cel.validator.CelAstValidator;
import java.util.Optional;

/** TimestampValidator ensures that timestamp literal arguments are valid. */
public class TimestampLiteralValidator implements CelAstValidator {
public static final TimestampLiteralValidator INSTANCE = new TimestampLiteralValidator();
/** TimestampLiteralValidator ensures that timestamp literal arguments are valid. */
public class TimestampLiteralValidator extends LiteralValidator {
public static final TimestampLiteralValidator INSTANCE =
new TimestampLiteralValidator("timestamp", Timestamp.class);

@Override
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
navigableAst
.getRoot()
.descendants()
.filter(
node ->
node.getKind().equals(Kind.CONSTANT)
&& node.parent().isPresent()
&& node.parent().get().expr().call().function().equals("timestamp"))
.map(CelNavigableExpr::expr)
.forEach(
timestampExpr -> {
try {
CelExpr timestampCall =
CelExpr.ofCallExpr(
1,
Optional.empty(),
"timestamp",
ImmutableList.of(CelExpr.ofConstantExpr(2, timestampExpr.constant())));

CelAbstractSyntaxTree ast =
CelAbstractSyntaxTree.newParsedAst(
timestampCall, CelSource.newBuilder().build());
ast = cel.check(ast).getAst();
Object result = cel.createProgram(ast).eval();

if (!(result instanceof Timestamp)) {
throw new IllegalStateException(
"Expected timestamp type but got " + result.getClass());
}
} catch (Exception e) {
issuesFactory.addError(
timestampExpr.id(), "Timestamp validation failed. Reason: " + e.getMessage());
}
});
private TimestampLiteralValidator(String functionName, Class<?> expectedResultType) {
super(functionName, expectedResultType);
}

private TimestampLiteralValidator() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ java_library(
"//runtime",
"//validator",
"//validator:validator_builder",
"//validator/validators:duration",
"//validator/validators:regex",
"//validator/validators:timestamp",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
Expand Down
Loading

0 comments on commit 149f12d

Please sign in to comment.