diff --git a/data-prepper-expression/src/main/java/org/opensearch/dataprepper/expression/GenericRegexMatchOperator.java b/data-prepper-expression/src/main/java/org/opensearch/dataprepper/expression/GenericRegexMatchOperator.java index 1154978116..b4d34dadc1 100644 --- a/data-prepper-expression/src/main/java/org/opensearch/dataprepper/expression/GenericRegexMatchOperator.java +++ b/data-prepper-expression/src/main/java/org/opensearch/dataprepper/expression/GenericRegexMatchOperator.java @@ -37,6 +37,8 @@ public int getSymbol() { @Override public Boolean evaluate(final Object ... args) { checkArgument(args.length == 2, displayName + " requires operands length needs to be 2."); + if(args[0] == null) + return false; checkArgument(args[0] instanceof String, displayName + " requires left operand to be String."); checkArgument(args[1] instanceof String, displayName + " requires right operand to be String."); try { diff --git a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GenericExpressionEvaluator_ConditionalIT.java b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GenericExpressionEvaluator_ConditionalIT.java index 0bef1a65a0..a8fc7971f3 100644 --- a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GenericExpressionEvaluator_ConditionalIT.java +++ b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/GenericExpressionEvaluator_ConditionalIT.java @@ -35,6 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.params.provider.Arguments.arguments; class GenericExpressionEvaluator_ConditionalIT { /** @@ -145,76 +146,80 @@ private static Stream validExpressionArguments() { int testStringLength = random.nextInt(10); String testString = RandomStringUtils.randomAlphabetic(testStringLength); return Stream.of( - Arguments.of("true", event("{}"), true), - Arguments.of("/status_code == 200", event("{\"status_code\": 200}"), true), - Arguments.of("/status_code == 200", longEvent, true), - Arguments.of("/status_code != 300", event("{\"status_code\": 200}"), true), - Arguments.of("/status_code == 200", event("{}"), false), - Arguments.of("/success == /status_code", event("{\"success\": true, \"status_code\": 200}"), false), - Arguments.of("/success != /status_code", event("{\"success\": true, \"status_code\": 200}"), true), - Arguments.of("/part1@part2.part3 != 111", event("{\"success\": true, \"part1@part2.part3\":111, \"status_code\": 200}"), false), - Arguments.of("/part1.part2@part3 != 111", event("{\"success\": true, \"part1.part2@part3\":222, \"status_code\": 200}"), true), - Arguments.of("/pi == 3.14159", event("{\"pi\": 3.14159}"), true), - Arguments.of("/value == 12345.678", event("{\"value\": 12345.678}"), true), - Arguments.of("/value == 12345.678E12", event("{\"value\": 12345.678E12}"), true), - Arguments.of("/value == 12345.678e-12", event("{\"value\": 12345.678e-12}"), true), - Arguments.of("/value == 12345.0000012", event("{\"value\": 12345.0000012}"), true), - Arguments.of("/value == 12345.00012E6", event("{\"value\": 12345.00012E6}"), true), - Arguments.of("true == (/is_cool == true)", event("{\"is_cool\": true}"), true), - Arguments.of("not /is_cool", event("{\"is_cool\": true}"), false), - Arguments.of("/status_code < 300", event("{\"status_code\": 200}"), true), - Arguments.of("/status_code != null", event("{\"status_code\": 200}"), true), - Arguments.of("null != /status_code", event("{\"status_code\": 200}"), true), - Arguments.of("/status_code == null", event("{\"status_code\": null}"), true), - Arguments.of("/response == null", event("{\"status_code\": 200}"), true), - Arguments.of("null == /response", event("{\"status_code\": 200}"), true), - Arguments.of("/response != null", event("{\"status_code\": 200}"), false), - Arguments.of("/status_code <= 0", event("{\"status_code\": 200}"), false), - Arguments.of("/status_code > 0", event("{\"status_code\": 200}"), true), - Arguments.of("/status_code >= 300", event("{\"status_code\": 200}"), false), - Arguments.of("-/status_code == -200", event("{\"status_code\": 200}"), true), - Arguments.of("/success and /status_code == 200", event("{\"success\": true, \"status_code\": 200}"), true), - Arguments.of("/success or /status_code == 200", event("{\"success\": false, \"status_code\": 200}"), true), - Arguments.of("(/success == true) or (/status_code == 200)", event("{\"success\": false, \"status_code\": 200}"), true), - Arguments.of("/should_drop", event("{\"should_drop\": true}"), true), - Arguments.of("/should_drop", event("{\"should_drop\": false}"), false), - Arguments.of("/logs/2/should_drop", event("{\"logs\": [{}, {}, {\"should_drop\": true}]}"), true), - Arguments.of( + arguments("true", event("{}"), true), + arguments("/status_code == 200", event("{\"status_code\": 200}"), true), + arguments("/status_code == 200", longEvent, true), + arguments("/status_code != 300", event("{\"status_code\": 200}"), true), + arguments("/status_code == 200", event("{}"), false), + arguments("/success == /status_code", event("{\"success\": true, \"status_code\": 200}"), false), + arguments("/success != /status_code", event("{\"success\": true, \"status_code\": 200}"), true), + arguments("/part1@part2.part3 != 111", event("{\"success\": true, \"part1@part2.part3\":111, \"status_code\": 200}"), false), + arguments("/part1.part2@part3 != 111", event("{\"success\": true, \"part1.part2@part3\":222, \"status_code\": 200}"), true), + arguments("/pi == 3.14159", event("{\"pi\": 3.14159}"), true), + arguments("/value == 12345.678", event("{\"value\": 12345.678}"), true), + arguments("/value == 12345.678E12", event("{\"value\": 12345.678E12}"), true), + arguments("/value == 12345.678e-12", event("{\"value\": 12345.678e-12}"), true), + arguments("/value == 12345.0000012", event("{\"value\": 12345.0000012}"), true), + arguments("/value == 12345.00012E6", event("{\"value\": 12345.00012E6}"), true), + arguments("true == (/is_cool == true)", event("{\"is_cool\": true}"), true), + arguments("not /is_cool", event("{\"is_cool\": true}"), false), + arguments("/status_code < 300", event("{\"status_code\": 200}"), true), + arguments("/status_code != null", event("{\"status_code\": 200}"), true), + arguments("null != /status_code", event("{\"status_code\": 200}"), true), + arguments("/status_code == null", event("{\"status_code\": null}"), true), + arguments("/response == null", event("{\"status_code\": 200}"), true), + arguments("null == /response", event("{\"status_code\": 200}"), true), + arguments("/response != null", event("{\"status_code\": 200}"), false), + arguments("/status_code <= 0", event("{\"status_code\": 200}"), false), + arguments("/status_code > 0", event("{\"status_code\": 200}"), true), + arguments("/status_code >= 300", event("{\"status_code\": 200}"), false), + arguments("-/status_code == -200", event("{\"status_code\": 200}"), true), + arguments("/success and /status_code == 200", event("{\"success\": true, \"status_code\": 200}"), true), + arguments("/success or /status_code == 200", event("{\"success\": false, \"status_code\": 200}"), true), + arguments("(/success == true) or (/status_code == 200)", event("{\"success\": false, \"status_code\": 200}"), true), + arguments("/should_drop", event("{\"should_drop\": true}"), true), + arguments("/should_drop", event("{\"should_drop\": false}"), false), + arguments("/logs/2/should_drop", event("{\"logs\": [{}, {}, {\"should_drop\": true}]}"), true), + arguments( escapedJsonPointer(ALL_JACKSON_EVENT_GET_SUPPORTED_CHARACTERS) + " == true", complexEvent(ALL_JACKSON_EVENT_GET_SUPPORTED_CHARACTERS, true), true), - Arguments.of("/durationInNanos > 5000000000", event("{\"durationInNanos\": 6000000000}"), true), - Arguments.of("/response == \"OK\"", event("{\"response\": \"OK\"}"), true), - Arguments.of("length(/response) == "+testStringLength, event("{\"response\": \""+testString+"\"}"), true), - Arguments.of("hasTags(\""+ testTag1+"\")", longEvent, true), - Arguments.of("hasTags(\""+ testTag1+"\",\""+testTag2+"\")", longEvent, true), - Arguments.of("hasTags(\""+ testTag1+"\", \""+testTag2+"\", \""+testTag3+"\")", longEvent, true), - Arguments.of("hasTags(\""+ testTag4+"\")", longEvent, false), - Arguments.of("hasTags(\""+ testTag3+"\",\""+testTag4+"\")", longEvent, false), - Arguments.of("contains(\""+ strValue+"\",\""+strValue.substring(1,5)+"\")", longEvent, true), - Arguments.of("contains(/status,\""+strValue.substring(0,2)+"\")", event("{\"status\":\""+strValue+"\"}"), true), - Arguments.of("contains(\""+strValue+strValue+"\",/status)", event("{\"status\":\""+strValue+"\"}"), true), - Arguments.of("contains(/message,/status)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), true), - Arguments.of("contains(/unknown,/status)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), false), - Arguments.of("contains(/status,/unknown)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), false), - Arguments.of("getMetadata(\"key1\") == \""+strValue+"\"", longEvent, true), - Arguments.of("getMetadata(\"key2\") == "+value4, longEvent, true), - Arguments.of("getMetadata(\"key3\") == "+value5, longEvent, true), - Arguments.of("getMetadata(\"/key1\") == \""+strValue+"\"", longEvent, true), - Arguments.of("getMetadata(\"/key2\") == "+value4, longEvent, true), - Arguments.of("getMetadata(\"key3\") == "+value5, longEvent, true), - Arguments.of("getMetadata(\"/key6\") == \""+value5+"\"", longEvent, false), - Arguments.of("getMetadata(\"key6\") == "+value5, longEvent, false), - Arguments.of("cidrContains(/sourceIp,\"192.0.2.0/24\")", event("{\"sourceIp\": \"192.0.2.3\"}"), true), - Arguments.of("cidrContains(/sourceIp,\"192.0.2.0/24\",\"192.1.1.0/24\")", event("{\"sourceIp\": \"192.0.2.3\"}"), true), - Arguments.of("cidrContains(/sourceIp,\"192.0.2.0/24\",\"192.1.1.0/24\")", event("{\"sourceIp\": \"192.2.2.3\"}"), false), - Arguments.of("cidrContains(/sourceIp,\"2001:0db8::/32\")", event("{\"sourceIp\": \"2001:0db8:aaaa:bbbb::\"}"), true), - Arguments.of("cidrContains(/sourceIp,\"2001:0db8::/32\",\"2001:aaaa::/32\")", event("{\"sourceIp\": \"2001:0db8:aaaa:bbbb::\"}"), true), - Arguments.of("cidrContains(/sourceIp,\"2001:0db8::/32\",\"2001:aaaa::/32\")", event("{\"sourceIp\": \"2001:abcd:aaaa:bbbb::\"}"), false), - Arguments.of("/sourceIp != null", event("{\"sourceIp\": [10, 20]}"), true), - Arguments.of("/sourceIp == null", event("{\"sourceIp\": [\"test\", \"test_two\"]}"), false), - Arguments.of("/sourceIp == null", event("{\"sourceIp\": {\"test\": \"test_two\"}}"), false), - Arguments.of("/sourceIp != null", event("{\"sourceIp\": {\"test\": \"test_two\"}}"), true) + arguments("/durationInNanos > 5000000000", event("{\"durationInNanos\": 6000000000}"), true), + arguments("/response == \"OK\"", event("{\"response\": \"OK\"}"), true), + arguments("length(/response) == "+testStringLength, event("{\"response\": \""+testString+"\"}"), true), + arguments("hasTags(\""+ testTag1+"\")", longEvent, true), + arguments("hasTags(\""+ testTag1+"\",\""+testTag2+"\")", longEvent, true), + arguments("hasTags(\""+ testTag1+"\", \""+testTag2+"\", \""+testTag3+"\")", longEvent, true), + arguments("hasTags(\""+ testTag4+"\")", longEvent, false), + arguments("hasTags(\""+ testTag3+"\",\""+testTag4+"\")", longEvent, false), + arguments("contains(\""+ strValue+"\",\""+strValue.substring(1,5)+"\")", longEvent, true), + arguments("contains(/status,\""+strValue.substring(0,2)+"\")", event("{\"status\":\""+strValue+"\"}"), true), + arguments("contains(\""+strValue+strValue+"\",/status)", event("{\"status\":\""+strValue+"\"}"), true), + arguments("contains(/message,/status)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), true), + arguments("contains(/unknown,/status)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), false), + arguments("contains(/status,/unknown)", event("{\"status\":\""+strValue+"\", \"message\":\""+strValue+strValue+"\"}"), false), + arguments("getMetadata(\"key1\") == \""+strValue+"\"", longEvent, true), + arguments("getMetadata(\"key2\") == "+value4, longEvent, true), + arguments("getMetadata(\"key3\") == "+value5, longEvent, true), + arguments("getMetadata(\"/key1\") == \""+strValue+"\"", longEvent, true), + arguments("getMetadata(\"/key2\") == "+value4, longEvent, true), + arguments("getMetadata(\"key3\") == "+value5, longEvent, true), + arguments("getMetadata(\"/key6\") == \""+value5+"\"", longEvent, false), + arguments("getMetadata(\"key6\") == "+value5, longEvent, false), + arguments("cidrContains(/sourceIp,\"192.0.2.0/24\")", event("{\"sourceIp\": \"192.0.2.3\"}"), true), + arguments("cidrContains(/sourceIp,\"192.0.2.0/24\",\"192.1.1.0/24\")", event("{\"sourceIp\": \"192.0.2.3\"}"), true), + arguments("cidrContains(/sourceIp,\"192.0.2.0/24\",\"192.1.1.0/24\")", event("{\"sourceIp\": \"192.2.2.3\"}"), false), + arguments("cidrContains(/sourceIp,\"2001:0db8::/32\")", event("{\"sourceIp\": \"2001:0db8:aaaa:bbbb::\"}"), true), + arguments("cidrContains(/sourceIp,\"2001:0db8::/32\",\"2001:aaaa::/32\")", event("{\"sourceIp\": \"2001:0db8:aaaa:bbbb::\"}"), true), + arguments("cidrContains(/sourceIp,\"2001:0db8::/32\",\"2001:aaaa::/32\")", event("{\"sourceIp\": \"2001:abcd:aaaa:bbbb::\"}"), false), + arguments("/sourceIp != null", event("{\"sourceIp\": [10, 20]}"), true), + arguments("/sourceIp == null", event("{\"sourceIp\": [\"test\", \"test_two\"]}"), false), + arguments("/sourceIp == null", event("{\"sourceIp\": {\"test\": \"test_two\"}}"), false), + arguments("/sourceIp != null", event("{\"sourceIp\": {\"test\": \"test_two\"}}"), true), + arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"name\": \"dataprepper-0\"}"), true), + arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"name\": \"dataprepper-212\"}"), true), + arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"name\": \"dataprepper-abc\"}"), false), + arguments("/name =~ \".*dataprepper-[0-9]+\"", event("{\"other\": \"dataprepper-abc\"}"), false) ); } @@ -236,43 +241,43 @@ private static Stream invalidExpressionArguments() { int testStringLength = random.nextInt(10); String testString = RandomStringUtils.randomAlphabetic(testStringLength); return Stream.of( - Arguments.of("/missing", event("{}")), - Arguments.of("/success < /status_code", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/success <= /status_code", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/success > /status_code", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/success >= /status_code", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/success > null", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/success >= null", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/status_code < null", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("/status_code <= null", event("{\"success\": true, \"status_code\": 200}")), - Arguments.of("not /status_code", event("{\"status_code\": 200}")), - Arguments.of("/status_code >= 200 and 3", event("{\"status_code\": 200}")), - Arguments.of("", event("{}")), - Arguments.of("-false", event("{}")), - Arguments.of("not 5", event("{}")), - Arguments.of("not null", event("{}")), - Arguments.of("not/status_code", event("{\"status_code\": 200}")), - Arguments.of("trueand/status_code", event("{\"status_code\": 200}")), - Arguments.of("trueor/status_code", event("{\"status_code\": 200}")), - Arguments.of("length(\""+testString+") == "+testStringLength, event("{\"response\": \""+testString+"\"}")), - Arguments.of("length(\""+testString+"\") == "+testStringLength, event("{\"response\": \""+testString+"\"}")), - Arguments.of("hasTags(10)", tagEvent), - Arguments.of("hasTags("+ testTag1+")", tagEvent), - Arguments.of("hasTags(\""+ testTag1+")", tagEvent), - Arguments.of("hasTags(\""+ testTag1+"\","+testTag2+"\")", tagEvent), - Arguments.of("hasTags(,\""+testTag2+"\")", tagEvent), - Arguments.of("hasTags(\""+testTag2+"\",)", tagEvent), - Arguments.of("contains(\""+testTag2+"\",)", tagEvent), - Arguments.of("contains(\""+testTag2+"\")", tagEvent), - Arguments.of("contains(/intField, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), - Arguments.of("contains(1234, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), - Arguments.of("contains(str, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), - Arguments.of("contains(/strField, 1234)", event("{\"intField\":1234,\"strField\":\"string\"}")), - Arguments.of("getMetadata(10)", tagEvent), - Arguments.of("getMetadata("+ testMetadataKey+ ")", tagEvent), - Arguments.of("getMetadata(\""+ testMetadataKey+")", tagEvent), - Arguments.of("cidrContains(/sourceIp)", event("{\"sourceIp\": \"192.0.2.3\"}")), - Arguments.of("cidrContains(/sourceIp,123)", event("{\"sourceIp\": \"192.0.2.3\"}")) + arguments("/missing", event("{}")), + arguments("/success < /status_code", event("{\"success\": true, \"status_code\": 200}")), + arguments("/success <= /status_code", event("{\"success\": true, \"status_code\": 200}")), + arguments("/success > /status_code", event("{\"success\": true, \"status_code\": 200}")), + arguments("/success >= /status_code", event("{\"success\": true, \"status_code\": 200}")), + arguments("/success > null", event("{\"success\": true, \"status_code\": 200}")), + arguments("/success >= null", event("{\"success\": true, \"status_code\": 200}")), + arguments("/status_code < null", event("{\"success\": true, \"status_code\": 200}")), + arguments("/status_code <= null", event("{\"success\": true, \"status_code\": 200}")), + arguments("not /status_code", event("{\"status_code\": 200}")), + arguments("/status_code >= 200 and 3", event("{\"status_code\": 200}")), + arguments("", event("{}")), + arguments("-false", event("{}")), + arguments("not 5", event("{}")), + arguments("not null", event("{}")), + arguments("not/status_code", event("{\"status_code\": 200}")), + arguments("trueand/status_code", event("{\"status_code\": 200}")), + arguments("trueor/status_code", event("{\"status_code\": 200}")), + arguments("length(\""+testString+") == "+testStringLength, event("{\"response\": \""+testString+"\"}")), + arguments("length(\""+testString+"\") == "+testStringLength, event("{\"response\": \""+testString+"\"}")), + arguments("hasTags(10)", tagEvent), + arguments("hasTags("+ testTag1+")", tagEvent), + arguments("hasTags(\""+ testTag1+")", tagEvent), + arguments("hasTags(\""+ testTag1+"\","+testTag2+"\")", tagEvent), + arguments("hasTags(,\""+testTag2+"\")", tagEvent), + arguments("hasTags(\""+testTag2+"\",)", tagEvent), + arguments("contains(\""+testTag2+"\",)", tagEvent), + arguments("contains(\""+testTag2+"\")", tagEvent), + arguments("contains(/intField, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), + arguments("contains(1234, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), + arguments("contains(str, /strField)", event("{\"intField\":1234,\"strField\":\"string\"}")), + arguments("contains(/strField, 1234)", event("{\"intField\":1234,\"strField\":\"string\"}")), + arguments("getMetadata(10)", tagEvent), + arguments("getMetadata("+ testMetadataKey+ ")", tagEvent), + arguments("getMetadata(\""+ testMetadataKey+")", tagEvent), + arguments("cidrContains(/sourceIp)", event("{\"sourceIp\": \"192.0.2.3\"}")), + arguments("cidrContains(/sourceIp,123)", event("{\"sourceIp\": \"192.0.2.3\"}")) ); } diff --git a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexEqualOperatorTest.java b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexEqualOperatorTest.java index 46fcdd9ccf..bb92cd1e49 100644 --- a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexEqualOperatorTest.java +++ b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexEqualOperatorTest.java @@ -12,6 +12,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -64,4 +65,9 @@ void testEvalInValidArgType() { void testEvalInValidPattern() { assertThrows(IllegalArgumentException.class, () -> objectUnderTest.evaluate("a", "*")); } + + @Test + void evaluate_with_null_lhs_returns_false() { + assertThat(objectUnderTest.evaluate(null, "a*"), equalTo(false)); + } } diff --git a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexNotEqualOperatorTest.java b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexNotEqualOperatorTest.java index 221c354eb7..30bc199413 100644 --- a/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexNotEqualOperatorTest.java +++ b/data-prepper-expression/src/test/java/org/opensearch/dataprepper/expression/RegexNotEqualOperatorTest.java @@ -12,6 +12,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.opensearch.dataprepper.expression.antlr.DataPrepperExpressionParser; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -64,4 +65,9 @@ void testEvalInValidArgType() { void testEvalInValidPattern() { assertThrows(IllegalArgumentException.class, () -> objectUnderTest.evaluate("a", "*")); } + + @Test + void evaluate_with_null_lhs_returns_false() { + assertThat(objectUnderTest.evaluate(null, "a*"), equalTo(false)); + } }