diff --git a/conformance/expected-failures.yaml b/conformance/expected-failures.yaml index c69ddf5a..e69de29b 100644 --- a/conformance/expected-failures.yaml +++ b/conformance/expected-failures.yaml @@ -1,9 +0,0 @@ -standard_rules/bytes: - - pattern/invalid/not_utf8 -# input: [ type.googleapis.com/buf.validate.conformance.cases.BytesPattern ]:{val:"\x99"} -# want: runtime error: value must be valid UTF-8 to apply regexp -# got: validation error (1 violation) -# 1. rule_id: "bytes.pattern" -# message: "value must match regex pattern `^[\\x00-\\x7F]+$`" -# field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_BYTES} -# rule: "bytes.pattern" elements:{field_number:15 field_name:"bytes" field_type:TYPE_MESSAGE} elements:{field_number:4 field_name:"pattern" field_type:TYPE_STRING} \ No newline at end of file diff --git a/src/main/java/build/buf/protovalidate/CustomOverload.java b/src/main/java/build/buf/protovalidate/CustomOverload.java index 2950d89b..28d468a9 100644 --- a/src/main/java/build/buf/protovalidate/CustomOverload.java +++ b/src/main/java/build/buf/protovalidate/CustomOverload.java @@ -17,6 +17,8 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; import com.google.protobuf.Message; +import dev.cel.common.CelErrorCode; +import dev.cel.common.CelRuntimeException; import dev.cel.common.types.CelType; import dev.cel.common.types.SimpleType; import dev.cel.runtime.CelEvaluationException; @@ -47,6 +49,7 @@ static List create() { ArrayList bindings = new ArrayList<>(); bindings.addAll( Arrays.asList( + celBytesToString(), celGetField(), celFormat(), celStartsWithBytes(), @@ -70,6 +73,26 @@ static List create() { return Collections.unmodifiableList(bindings); } + /** + * This implementes that standard {@code bytes_to_string} function. We override it because the CEL + * library doesn't validate that the bytes are valid utf-8. + * + *

Workround until https://github.com/google/cel-java/pull/717 lands. + */ + private static CelFunctionBinding celBytesToString() { + return CelFunctionBinding.from( + "bytes_to_string", + ByteString.class, + v -> { + if (!v.isValidUtf8()) { + throw new CelRuntimeException( + new IllegalArgumentException("invalid UTF-8 in bytes, cannot convert to string"), + CelErrorCode.BAD_FORMAT); + } + return v.toStringUtf8(); + }); + } + /** * Creates a custom function overload for the "getField" operation. * diff --git a/src/main/java/build/buf/protovalidate/ValidateLibrary.java b/src/main/java/build/buf/protovalidate/ValidateLibrary.java index 71e66706..0e7467a3 100644 --- a/src/main/java/build/buf/protovalidate/ValidateLibrary.java +++ b/src/main/java/build/buf/protovalidate/ValidateLibrary.java @@ -22,6 +22,9 @@ import dev.cel.parser.CelStandardMacro; import dev.cel.runtime.CelRuntimeBuilder; import dev.cel.runtime.CelRuntimeLibrary; +import dev.cel.runtime.CelStandardFunctions; +import dev.cel.runtime.CelStandardFunctions.StandardFunction; +import dev.cel.runtime.CelStandardFunctions.StandardFunction.Overload.Conversions; /** * Custom {@link CelCompilerLibrary} and {@link CelRuntimeLibrary}. Provides all the custom @@ -54,6 +57,17 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) { @Override public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) { - runtimeBuilder.addFunctionBindings(CustomOverload.create()); + runtimeBuilder + .addFunctionBindings(CustomOverload.create()) + .setStandardEnvironmentEnabled(false) + .setStandardFunctions( + CelStandardFunctions.newBuilder() + .filterFunctions( + // CEL doesn't validate, that the bytes are valid utf-8, so we provide out own + // implementation. + (function, overload) -> + function != StandardFunction.STRING + || !overload.equals(Conversions.BYTES_TO_STRING)) + .build()); } }