diff --git a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java index 7b367707..703f6770 100644 --- a/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java +++ b/core/src/main/java/org/projectnessie/cel/interpreter/Interpretable.java @@ -396,6 +396,13 @@ final class EvalEq extends AbstractEvalLhsRhs implements InterpretableCall { public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { Val lVal = lhs.eval(ctx); Val rVal = rhs.eval(ctx); + // Early return if any argument to the function is unknown or error. + if (isUnknownOrError(lVal)) { + return lVal; + } + if (isUnknownOrError(rVal)) { + return rVal; + } return lVal.equal(rVal); } @@ -439,6 +446,13 @@ final class EvalNe extends AbstractEvalLhsRhs implements InterpretableCall { public Val eval(org.projectnessie.cel.interpreter.Activation ctx) { Val lVal = lhs.eval(ctx); Val rVal = rhs.eval(ctx); + // Early return if any argument to the function is unknown or error. + if (isUnknownOrError(lVal)) { + return lVal; + } + if (isUnknownOrError(rVal)) { + return rVal; + } Val eqVal = lVal.equal(rVal); switch (eqVal.type().typeEnum()) { case Err: diff --git a/core/src/test/java/org/projectnessie/cel/CELTest.java b/core/src/test/java/org/projectnessie/cel/CELTest.java index 97cd7d22..a34644b1 100644 --- a/core/src/test/java/org/projectnessie/cel/CELTest.java +++ b/core/src/test/java/org/projectnessie/cel/CELTest.java @@ -49,7 +49,6 @@ import static org.projectnessie.cel.ProgramOption.functions; import static org.projectnessie.cel.ProgramOption.globals; import static org.projectnessie.cel.Util.mapOf; -import static org.projectnessie.cel.common.types.BoolT.False; import static org.projectnessie.cel.common.types.BoolT.True; import static org.projectnessie.cel.common.types.Err.isError; import static org.projectnessie.cel.common.types.Err.newErr; @@ -657,10 +656,10 @@ void ResidualAst_Complex() { assertThat(astIss.hasIssues()).isFalse(); Program prg = e.program(astIss.getAst(), evalOptions(OptTrackState, OptPartialEval)); EvalResult outDet = prg.eval(unkVars); - assertThat(outDet.getVal()).isSameAs(False); + assertThat(outDet.getVal()).isInstanceOf(UnknownT.class); Ast residual = e.residualAst(astIss.getAst(), outDet.getEvalDetails()); String expr = astToString(residual); - assertThat(expr).isEqualTo("false"); + assertThat(expr).isEqualTo("request.auth.claims.email == \"wiley@acme.co\""); } @Test diff --git a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java index ce58f0ad..125fbd95 100644 --- a/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java +++ b/core/src/test/java/org/projectnessie/cel/interpreter/InterpreterTest.java @@ -1505,6 +1505,32 @@ void missingIdentInSelect() { assertThat(result).isInstanceOf(Err.class); } + @Test + void equalityWithUnknownOperandStaysUnknown() { + assertThat(evalWithUnknownStringX("x == 'foo'")).isInstanceOf(UnknownT.class); + assertThat(evalWithUnknownStringX("x != 'foo'")).isInstanceOf(UnknownT.class); + assertThat(evalWithUnknownStringX("false || x == 'foo'")).isInstanceOf(UnknownT.class); + assertThat(evalWithUnknownStringX("false && x == 'foo'")).isSameAs(False); + } + + private static Val evalWithUnknownStringX(String expr) { + Source src = newTextSource(expr); + ParseResult parsed = Parser.parseAllMacros(src); + assertThat(parsed.hasErrors()).withFailMessage(parsed.getErrors()::toDisplayString).isFalse(); + + Container cont = testContainer("test"); + TypeRegistry reg = newRegistry(); + CheckerEnv env = newStandardCheckerEnv(cont, reg); + env.add(Decls.newVar("x", Decls.String)); + CheckResult checkResult = Checker.Check(parsed, src, env); + + AttributeFactory attrs = newPartialAttributeFactory(cont, reg, reg); + Interpreter interp = newStandardInterpreter(cont, reg, reg, attrs); + Interpretable i = interp.newInterpretable(checkResult.getCheckedExpr()); + Activation vars = newPartialActivation(mapOf(), newAttributePattern("x")); + return i.eval(vars); + } + static class ConvTestCase { final String in; Val out;