From c01c093f8dc47bb2871e8b91c2cf7d1886d16d69 Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Sat, 28 Mar 2020 15:02:54 -0400 Subject: [PATCH 1/5] EQL: implement stringContains function --- .../resources/test_queries_unsupported.toml | 7 - .../function/EqlFunctionRegistry.java | 2 + .../function/scalar/string/Substring.java | 2 +- .../scalar/stringcontains/StringContains.java | 135 ++++++++++++++++++ .../StringContainsFunctionPipe.java | 113 +++++++++++++++ .../StringContainsFunctionProcessor.java | 113 +++++++++++++++ .../stringcontains/StringContainsUtils.java | 40 ++++++ .../whitelist/InternalEqlScriptUtils.java | 6 + .../xpack/eql/plugin/eql_whitelist.txt | 1 + .../xpack/eql/analysis/VerifierTests.java | 4 +- .../StringContainsFunctionProcessorTests.java | 54 +++++++ .../StringContainsUtilsTests.java | 44 ++++++ .../eql/planner/QueryFolderFailTests.java | 35 +++++ .../src/test/resources/queryfolder_tests.txt | 6 + 14 files changed, 551 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java create mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java create mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java create mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java create mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java create mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java diff --git a/x-pack/plugin/eql/qa/common/src/main/resources/test_queries_unsupported.toml b/x-pack/plugin/eql/qa/common/src/main/resources/test_queries_unsupported.toml index f9a869f9e25f3..c0f41cf7cb655 100644 --- a/x-pack/plugin/eql/qa/common/src/main/resources/test_queries_unsupported.toml +++ b/x-pack/plugin/eql/qa/common/src/main/resources/test_queries_unsupported.toml @@ -824,13 +824,6 @@ file where opcode=0 and serial_event_id = 88 and startsWith('explorer.exeaAAAA', expected_event_ids = [88] description = "check built-in string functions" -[[queries]] -query = ''' -file where opcode=0 and stringContains('ABCDEFGHIexplorer.exeJKLMNOP', file_name) -''' -expected_event_ids = [88] -description = "check built-in string functions" - [[queries]] query = ''' file where opcode=0 and indexOf(file_name, 'plore') == 2 and not indexOf(file_name, '.pf') diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java index 119e12fa9f39c..3b755dfe464ff 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.eql.expression.function; import org.elasticsearch.xpack.eql.expression.function.scalar.string.Substring; +import org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContains; import org.elasticsearch.xpack.ql.expression.function.FunctionDefinition; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; @@ -23,6 +24,7 @@ private static FunctionDefinition[][] functions() { // Scalar functions // String new FunctionDefinition[] { + def(StringContains.class, StringContains::new, "stringcontains"), def(Substring.class, Substring::new, "substring"), }, }; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java index 5aeec120218c3..8f785a34377d4 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/Substring.java @@ -127,4 +127,4 @@ public Expression replaceChildren(List newChildren) { return new Substring(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2)); } -} \ No newline at end of file +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java new file mode 100644 index 0000000000000..a064d7d1c99f9 --- /dev/null +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import org.elasticsearch.xpack.ql.expression.Expression; +import org.elasticsearch.xpack.ql.expression.Expressions; +import org.elasticsearch.xpack.ql.expression.FieldAttribute; +import org.elasticsearch.xpack.ql.expression.Literal; +import org.elasticsearch.xpack.ql.expression.function.OptionalArgument; +import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction; +import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe; +import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate; +import org.elasticsearch.xpack.ql.expression.gen.script.Scripts; +import org.elasticsearch.xpack.ql.tree.NodeInfo; +import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; +import org.elasticsearch.xpack.ql.type.DataTypes; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import static java.lang.String.format; +import static org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsFunctionProcessor.doProcess; +import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isBoolean; +import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isStringAndExact; +import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.paramsBuilder; + +/** + * EQL specific stringContains function. + * https://eql.readthedocs.io/en/latest/query-guide/functions.html#stringContains + * stringContains(a, b) + * Returns true if b is a substring of a + */ +public class StringContains extends ScalarFunction implements OptionalArgument { + + private final Expression haystack, needle, caseSensitive; + + public StringContains(Source source, Expression haystack, Expression needle, Expression caseSensitive) { + super(source, Arrays.asList(haystack, needle, toDefault(caseSensitive))); + this.haystack = haystack; + this.needle = needle; + this.caseSensitive = arguments().get(2); + } + + private static Expression toDefault(Expression exp) { + return exp != null ? exp : Literal.FALSE; + } + + @Override + protected TypeResolution resolveType() { + if (!childrenResolved()) { + return new TypeResolution("Unresolved children"); + } + + TypeResolution resolution = isStringAndExact(haystack, sourceText(), Expressions.ParamOrdinal.FIRST); + if (resolution.unresolved()) { + return resolution; + } + + resolution = isStringAndExact(needle, sourceText(), Expressions.ParamOrdinal.SECOND); + if (resolution.unresolved()) { + return resolution; + } + return isBoolean(caseSensitive, sourceText(), Expressions.ParamOrdinal.THIRD); + } + + @Override + protected Pipe makePipe() { + return new StringContainsFunctionPipe(source(), this, + Expressions.pipe(haystack), Expressions.pipe(needle), Expressions.pipe(caseSensitive)); + } + + @Override + public boolean foldable() { + return haystack.foldable() && needle.foldable() && caseSensitive.foldable(); + } + + @Override + public Object fold() { + return doProcess(haystack.fold(), needle.fold(), caseSensitive.fold()); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, StringContains::new, haystack, needle, caseSensitive); + } + + @Override + public ScriptTemplate asScript() { + ScriptTemplate haystackScript = asScript(haystack); + ScriptTemplate needleScript = asScript(needle); + ScriptTemplate caseSensitiveScript = asScript(caseSensitive); + + return asScriptFrom(haystackScript, needleScript, caseSensitiveScript); + } + + protected ScriptTemplate asScriptFrom(ScriptTemplate haystackScript, ScriptTemplate needleScript, ScriptTemplate caseSensitiveScript) { + return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s,%s)"), + "stringContains", + haystackScript.template(), + needleScript.template(), + caseSensitiveScript.template()), + paramsBuilder() + .script(haystackScript.params()) + .script(needleScript.params()) + .script(caseSensitiveScript.params()) + .build(), dataType()); + } + + @Override + public ScriptTemplate scriptWithField(FieldAttribute field) { + return new ScriptTemplate(processScript(Scripts.DOC_VALUE), + paramsBuilder().variable(field.exactAttribute().name()).build(), + dataType()); + } + + @Override + public DataType dataType() { + return DataTypes.BOOLEAN; + } + + @Override + public Expression replaceChildren(List newChildren) { + if (newChildren.size() != 3) { + throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]"); + } + + return new StringContains(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2)); + } +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java new file mode 100644 index 0000000000000..5b98d0610e4d9 --- /dev/null +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import org.elasticsearch.xpack.ql.execution.search.QlSourceBuilder; +import org.elasticsearch.xpack.ql.expression.Expression; +import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe; +import org.elasticsearch.xpack.ql.tree.NodeInfo; +import org.elasticsearch.xpack.ql.tree.Source; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class StringContainsFunctionPipe extends Pipe { + + private final Pipe haystack, needle, caseSensitive; + + public StringContainsFunctionPipe(Source source, Expression expression, Pipe haystack, Pipe needle, Pipe caseSensitive) { + super(source, expression, Arrays.asList(haystack, needle)); + this.haystack = haystack; + this.needle = needle; + this.caseSensitive = caseSensitive; + } + + @Override + public final Pipe replaceChildren(List newChildren) { + if (newChildren.size() != 3) { + throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]"); + } + return replaceChildren(newChildren.get(0), newChildren.get(1), newChildren.get(2)); + } + + @Override + public final Pipe resolveAttributes(AttributeResolver resolver) { + Pipe newHaystack = haystack.resolveAttributes(resolver); + Pipe newNeedle = needle.resolveAttributes(resolver); + Pipe newCaseSensitive = caseSensitive.resolveAttributes(resolver); + if (newHaystack == haystack && newNeedle == needle && newCaseSensitive == caseSensitive) { + return this; + } + return replaceChildren(newHaystack, newNeedle, newCaseSensitive); + } + + @Override + public boolean supportedByAggsOnlyQuery() { + return haystack.supportedByAggsOnlyQuery() && needle.supportedByAggsOnlyQuery() && caseSensitive.supportedByAggsOnlyQuery(); + } + + @Override + public boolean resolved() { + return haystack.resolved() && needle.resolved() && caseSensitive.resolved(); + } + + protected Pipe replaceChildren(Pipe haystack, Pipe needle, Pipe caseSensitive) { + return new StringContainsFunctionPipe(source(), expression(), haystack, needle, caseSensitive); + } + + @Override + public final void collectFields(QlSourceBuilder sourceBuilder) { + haystack.collectFields(sourceBuilder); + needle.collectFields(sourceBuilder); + caseSensitive.collectFields(sourceBuilder); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, StringContainsFunctionPipe::new, expression(), haystack, needle, caseSensitive); + } + + @Override + public StringContainsFunctionProcessor asProcessor() { + return new StringContainsFunctionProcessor(haystack.asProcessor(), needle.asProcessor(), caseSensitive.asProcessor()); + } + + public Pipe haystack() { + return haystack; + } + + public Pipe needle() { + return needle; + } + + public Pipe caseSensitive() { + return caseSensitive; + } + + @Override + public int hashCode() { + return Objects.hash(source(), haystack(), needle(), caseSensitive()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + StringContainsFunctionPipe other = (StringContainsFunctionPipe) obj; + return Objects.equals(source(), other.source()) + && Objects.equals(haystack(), other.haystack()) + && Objects.equals(needle(), other.needle()) + && Objects.equals(caseSensitive(), other.caseSensitive()); + } +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java new file mode 100644 index 0000000000000..88072d3d8a148 --- /dev/null +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xpack.eql.EqlIllegalArgumentException; +import org.elasticsearch.xpack.ql.expression.gen.processor.Processor; + +import java.io.IOException; +import java.util.Objects; + +public class StringContainsFunctionProcessor implements Processor { + + public static final String NAME = "sstringcontains"; + + private final Processor haystack, needle, caseSensitive; + + public StringContainsFunctionProcessor(Processor haystack, Processor needle, Processor caseSensitive) { + this.haystack = haystack; + this.needle = needle; + this.caseSensitive = caseSensitive; + } + + public StringContainsFunctionProcessor(StreamInput in) throws IOException { + haystack = in.readNamedWriteable(Processor.class); + needle = in.readNamedWriteable(Processor.class); + caseSensitive = in.readNamedWriteable(Processor.class); + } + + @Override + public final void writeTo(StreamOutput out) throws IOException { + out.writeNamedWriteable(haystack); + out.writeNamedWriteable(needle); + out.writeNamedWriteable(caseSensitive); + } + + @Override + public Object process(Object input) { + return doProcess(haystack.process(input), needle.process(input), caseSensitive.process(input)); + } + + public static Object doProcess(Object haystack, Object needle, Object caseSensitive) { + if (haystack == null) { + return null; + } + + throwIfNotString(haystack); + throwIfNotString(needle); + + throwIfNotBoolean(caseSensitive); + + String strHaystack = haystack.toString(); + String strNeedle = needle.toString(); + boolean bCaseSensitive = ((Boolean) caseSensitive).booleanValue(); + return StringContainsUtils.stringContains(strHaystack, strNeedle, bCaseSensitive); + } + + private static void throwIfNotString(Object obj) { + if (!(obj instanceof String || obj instanceof Character)) { + throw new EqlIllegalArgumentException("A string/char is required; received [{}]", obj); + } + } + + private static void throwIfNotBoolean(Object obj) { + if (!(obj instanceof Boolean)) { + throw new EqlIllegalArgumentException("A boolean is required; received [{}]", obj); + } + } + + protected Processor haystack() { + return haystack; + } + + public Processor needle() { + return needle; + } + + public Processor caseSensitive() { + return caseSensitive; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + StringContainsFunctionProcessor other = (StringContainsFunctionProcessor) obj; + return Objects.equals(haystack(), other.haystack()) + && Objects.equals(needle(), other.needle()) + && Objects.equals(caseSensitive(), other.caseSensitive()); + } + + @Override + public int hashCode() { + return Objects.hash(haystack(), needle(), caseSensitive()); + } + + + @Override + public String getWriteableName() { + return NAME; + } +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java new file mode 100644 index 0000000000000..d048da54a0851 --- /dev/null +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import java.util.Locale; + +import static org.elasticsearch.common.Strings.hasLength; + +final class StringContainsUtils { + + private StringContainsUtils() { + } + + /** + * Extracts a substring from string between left and right strings. + * Port of "between" function from the original EQL python implementation. + * + * @param haystack string to search through. + * @param needle string to search for. + * @param caseSensitive flag for case-sensitive matching. + * @return {@code true} if {@code search} string contains {@code find} string. + */ + static boolean stringContains(String haystack, String needle, boolean caseSensitive) { + if (hasLength(haystack) == false || hasLength(needle) == false) { + return false; + } + + String search = haystack; + String find = needle; + if (caseSensitive == false) { + search = search.toLowerCase(Locale.ROOT); + find = find.toLowerCase(Locale.ROOT); + } + return search.contains(find); + } +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java index 959334a73d8b0..b3b9ee8dc097b 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.eql.expression.function.scalar.whitelist; import org.elasticsearch.xpack.eql.expression.function.scalar.string.SubstringFunctionProcessor; +import org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsFunctionProcessor; import org.elasticsearch.xpack.ql.expression.function.scalar.whitelist.InternalQlScriptUtils; /* @@ -21,4 +22,9 @@ public class InternalEqlScriptUtils extends InternalQlScriptUtils { public static String substring(String s, Number start, Number end) { return (String) SubstringFunctionProcessor.doProcess(s, start, end); } + + public static Boolean stringContains(String haystack, String needle, Boolean caseSensitive) { + return (Boolean) StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive); + } + } diff --git a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt index 6839583d14c1e..c1116fdf505f0 100644 --- a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt +++ b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt @@ -56,4 +56,5 @@ class org.elasticsearch.xpack.eql.expression.function.scalar.whitelist.InternalE # ASCII Functions # String substring(String, Number, Number) + Boolean stringContains(String, String, Boolean) } diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java index 23763bec6ec7e..1febdc72c0044 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java @@ -113,7 +113,7 @@ public void testJoinUnsupported() { // Some functions fail with "Unsupported" message at the parse stage public void testArrayFunctionsUnsupported() { - assertEquals("1:16: Unknown function [arrayContains]", + assertEquals("1:16: Unknown function [arrayContains], did you mean [stringcontains]?", error("registry where arrayContains(bytes_written_string_list, 'En')")); assertEquals("1:16: Unknown function [arraySearch]", error("registry where arraySearch(bytes_written_string_list, bytes_written_string, true)")); @@ -135,8 +135,6 @@ public void testFunctionVerificationUnknown() { error("file where opcode=0 and endsWith(file_name, 'loREr.exe')")); assertEquals("1:25: Unknown function [startsWith]", error("file where opcode=0 and startsWith(file_name, 'explORER.EXE')")); - assertEquals("1:25: Unknown function [stringContains]", - error("file where opcode=0 and stringContains('ABCDEFGHIexplorer.exeJKLMNOP', file_name)")); assertEquals("1:25: Unknown function [indexOf]", error("file where opcode=0 and indexOf(file_name, 'plore') == 2")); assertEquals("1:15: Unknown function [add]", diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java new file mode 100644 index 0000000000000..6286cb9733fc5 --- /dev/null +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.eql.EqlIllegalArgumentException; + +import java.util.concurrent.Callable; + +import static org.hamcrest.Matchers.equalTo; + +public class StringContainsFunctionProcessorTests extends ESTestCase { + + // TODO (AM): consolidate with other functions tests in previous PRs where I already used this pattern once they are merged + protected static final int NUMBER_OF_TEST_RUNS = 20; + + protected static void run(Callable callable) throws Exception { + for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { + callable.call(); + } + } + + public void testNullOrEmptyParameters() throws Exception { + run(() -> { + String needle = randomBoolean() ? null : randomAlphaOfLength(10); + String str = randomBoolean() ? null : randomAlphaOfLength(10); + if (str != null && needle != null) { + str += needle; + str += randomAlphaOfLength(10); + } + final String haystack = str; + Boolean caseSensitive = randomBoolean() ? null : randomBoolean(); + + // The haystack parameter can be null. Expect exception if any of other parameters is null. + if ((haystack != null) && (needle == null || caseSensitive == null)) { + EqlIllegalArgumentException e = expectThrows(EqlIllegalArgumentException.class, + () -> StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive)); + if (needle == null) { + assertThat(e.getMessage(), equalTo("A string/char is required; received [null]")); + } else { + assertThat(e.getMessage(), equalTo("A boolean is required; received [null]")); + } + } else { + assertThat(StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive), + equalTo(haystack == null? null : true)); + } + return null; + }); + } +} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java new file mode 100644 index 0000000000000..c47ee3ad440fb --- /dev/null +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; + +import org.elasticsearch.test.ESTestCase; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; + +import static org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsUtils.stringContains; + +public class StringContainsUtilsTests extends ESTestCase { + + protected static final int NUMBER_OF_TEST_RUNS = 20; + + private static void run(Callable callable) throws Exception { + for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { + callable.call(); + } + } + + public void testStringContainsWithNullOrEmpty() { + List caseSensList = Arrays.asList(true, false); + for (boolean caseSensitive: caseSensList) { + assertFalse(stringContains(null, null, caseSensitive)); + assertFalse(stringContains(null, "", caseSensitive)); + assertFalse(stringContains("", null, caseSensitive)); + } + } + + public void testStringContainsWithRandom() throws Exception { + run(() -> { + String needle = randomAlphaOfLength(10); + String haystack = randomAlphaOfLength(10) + needle + randomAlphaOfLength(10); + assertTrue(stringContains(haystack, needle, true)); + return null; + }); + } +} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java index 471ee4c56943a..b2bb78c2e8a35 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java @@ -6,9 +6,28 @@ package org.elasticsearch.xpack.eql.planner; +import org.elasticsearch.xpack.eql.analysis.VerificationException; +import org.elasticsearch.xpack.ql.ParsingException; import org.elasticsearch.xpack.ql.QlIllegalArgumentException; public class QueryFolderFailTests extends AbstractQueryFolderTestCase { + + private String error(String query) { + VerificationException e = expectThrows(VerificationException.class, + () -> plan(query)); + + assertTrue(e.getMessage().startsWith("Found ")); + final String header = "Found 1 problem\nline "; + return e.getMessage().substring(header.length()); + } + + private String errorParsing(String eql) { + ParsingException e = expectThrows(ParsingException.class, () -> plan(eql)); + final String header = "line "; + assertTrue(e.getMessage().startsWith(header)); + return e.getMessage().substring(header.length()); + } + public void testPropertyEquationFilterUnsupported() { QlIllegalArgumentException e = expectThrows(QlIllegalArgumentException.class, () -> plan("process where (serial_event_id<9 and serial_event_id >= 7) or (opcode == pid)")); @@ -22,4 +41,20 @@ public void testPropertyEquationInClauseFilterUnsupported() { String msg = e.getMessage(); assertEquals("Line 1:52: Comparisons against variables are not (currently) supported; offender [parent_process_name] in [==]", msg); } + + + public void testStringContainsWrongParams() { + assertEquals("1:16: error building [stringcontains]: expects two or three arguments", + errorParsing("process where stringContains()")); + + assertEquals("1:16: error building [stringcontains]: expects two or three arguments", + errorParsing("process where stringContains(process_name)")); + + assertEquals("1:15: second argument of [stringContains(process_name, 1)] must be [string], found value [1] type [integer]", + error("process where stringContains(process_name, 1)")); + + assertEquals("1:15: third argument of [stringContains(process_name, \"foo\", 2)] must be [boolean], found value [2] type [integer]", + error("process where stringContains(process_name, \"foo\", 2)")); + + } } diff --git a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt index a4f73f1b19602..e86181b119006 100644 --- a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt +++ b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt @@ -72,3 +72,9 @@ process where substring(file_name, -4) == '.exe' InternalEqlScriptUtils.substring(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2),params.v3))", "params":{"v0":"file_name.keyword","v1":-4,"v2":null,"v3":".exe"} + +stringContains +process where stringContains(process_name, "foo") +"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.stringContains( +InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))" +"params":{"v0":"process_name","v1":"foo","v2":false} From 8472a00078de62d3402eef0790cfbeb1716ad5eb Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Sat, 4 Apr 2020 10:49:53 -0400 Subject: [PATCH 2/5] Address code review comments --- .../function/EqlFunctionRegistry.java | 2 +- .../StringContains.java | 66 ++++------ .../StringContainsFunctionPipe.java | 63 +++++----- .../StringContainsFunctionProcessor.java | 96 +++++++++++++++ .../function/scalar/string/StringUtils.java | 22 +++- .../StringContainsFunctionProcessor.java | 113 ------------------ .../stringcontains/StringContainsUtils.java | 40 ------- .../whitelist/InternalEqlScriptUtils.java | 6 +- .../xpack/eql/plugin/eql_whitelist.txt | 2 +- .../StringContainsFunctionProcessorTests.java | 26 ++-- .../scalar/string/StringUtilsTests.java | 26 ++++ .../StringContainsUtilsTests.java | 44 ------- .../eql/planner/QueryFolderFailTests.java | 8 +- .../src/test/resources/queryfolder_tests.txt | 4 +- 14 files changed, 215 insertions(+), 303 deletions(-) rename x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/{stringcontains => string}/StringContains.java (53%) rename x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/{stringcontains => string}/StringContainsFunctionPipe.java (51%) create mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessor.java delete mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java delete mode 100644 x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java rename x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/{stringcontains => string}/StringContainsFunctionProcessorTests.java (66%) delete mode 100644 x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java index ca94f2bd54f6c..cafd787df8e8c 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java @@ -10,7 +10,7 @@ import org.elasticsearch.xpack.eql.expression.function.scalar.string.Length; import org.elasticsearch.xpack.eql.expression.function.scalar.string.StartsWith; import org.elasticsearch.xpack.eql.expression.function.scalar.string.Substring; -import org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContains; +import org.elasticsearch.xpack.eql.expression.function.scalar.string.StringContains; import org.elasticsearch.xpack.eql.expression.function.scalar.string.Wildcard; import org.elasticsearch.xpack.ql.expression.function.FunctionDefinition; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java similarity index 53% rename from x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java rename to x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java index a064d7d1c99f9..9755db4d0563d 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContains.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; +package org.elasticsearch.xpack.eql.expression.function.scalar.string; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.Expressions; import org.elasticsearch.xpack.ql.expression.FieldAttribute; -import org.elasticsearch.xpack.ql.expression.Literal; -import org.elasticsearch.xpack.ql.expression.function.OptionalArgument; import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction; import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe; import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate; @@ -25,8 +23,7 @@ import java.util.Locale; import static java.lang.String.format; -import static org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsFunctionProcessor.doProcess; -import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isBoolean; +import static org.elasticsearch.xpack.eql.expression.function.scalar.string.StringContainsFunctionProcessor.doProcess; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isStringAndExact; import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.paramsBuilder; @@ -36,19 +33,14 @@ * stringContains(a, b) * Returns true if b is a substring of a */ -public class StringContains extends ScalarFunction implements OptionalArgument { +public class StringContains extends ScalarFunction { - private final Expression haystack, needle, caseSensitive; + private final Expression string, substring; - public StringContains(Source source, Expression haystack, Expression needle, Expression caseSensitive) { - super(source, Arrays.asList(haystack, needle, toDefault(caseSensitive))); - this.haystack = haystack; - this.needle = needle; - this.caseSensitive = arguments().get(2); - } - - private static Expression toDefault(Expression exp) { - return exp != null ? exp : Literal.FALSE; + public StringContains(Source source, Expression string, Expression substring) { + super(source, Arrays.asList(string, substring)); + this.string = string; + this.substring = substring; } @Override @@ -57,58 +49,48 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = isStringAndExact(haystack, sourceText(), Expressions.ParamOrdinal.FIRST); + TypeResolution resolution = isStringAndExact(string, sourceText(), Expressions.ParamOrdinal.FIRST); if (resolution.unresolved()) { return resolution; } - resolution = isStringAndExact(needle, sourceText(), Expressions.ParamOrdinal.SECOND); - if (resolution.unresolved()) { - return resolution; - } - return isBoolean(caseSensitive, sourceText(), Expressions.ParamOrdinal.THIRD); + return isStringAndExact(substring, sourceText(), Expressions.ParamOrdinal.SECOND); } @Override protected Pipe makePipe() { return new StringContainsFunctionPipe(source(), this, - Expressions.pipe(haystack), Expressions.pipe(needle), Expressions.pipe(caseSensitive)); + Expressions.pipe(string), Expressions.pipe(substring)); } @Override public boolean foldable() { - return haystack.foldable() && needle.foldable() && caseSensitive.foldable(); + return string.foldable() && substring.foldable(); } @Override public Object fold() { - return doProcess(haystack.fold(), needle.fold(), caseSensitive.fold()); + return doProcess(string.fold(), substring.fold()); } @Override protected NodeInfo info() { - return NodeInfo.create(this, StringContains::new, haystack, needle, caseSensitive); + return NodeInfo.create(this, StringContains::new, string, substring); } @Override public ScriptTemplate asScript() { - ScriptTemplate haystackScript = asScript(haystack); - ScriptTemplate needleScript = asScript(needle); - ScriptTemplate caseSensitiveScript = asScript(caseSensitive); - - return asScriptFrom(haystackScript, needleScript, caseSensitiveScript); + return asScriptFrom(asScript(string), asScript(substring)); } - protected ScriptTemplate asScriptFrom(ScriptTemplate haystackScript, ScriptTemplate needleScript, ScriptTemplate caseSensitiveScript) { - return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s,%s)"), + protected ScriptTemplate asScriptFrom(ScriptTemplate stringScript, ScriptTemplate substringScript) { + return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s)"), "stringContains", - haystackScript.template(), - needleScript.template(), - caseSensitiveScript.template()), + stringScript.template(), + substringScript.template()), paramsBuilder() - .script(haystackScript.params()) - .script(needleScript.params()) - .script(caseSensitiveScript.params()) + .script(stringScript.params()) + .script(substringScript.params()) .build(), dataType()); } @@ -126,10 +108,10 @@ public DataType dataType() { @Override public Expression replaceChildren(List newChildren) { - if (newChildren.size() != 3) { - throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]"); + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); } - return new StringContains(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2)); + return new StringContains(source(), newChildren.get(0), newChildren.get(1)); } } diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionPipe.java similarity index 51% rename from x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java rename to x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionPipe.java index 5b98d0610e4d9..54e91bc4d392b 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionPipe.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionPipe.java @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; +package org.elasticsearch.xpack.eql.expression.function.scalar.string; import org.elasticsearch.xpack.ql.execution.search.QlSourceBuilder; import org.elasticsearch.xpack.ql.expression.Expression; @@ -18,80 +18,74 @@ public class StringContainsFunctionPipe extends Pipe { - private final Pipe haystack, needle, caseSensitive; + private final Pipe string, substring; - public StringContainsFunctionPipe(Source source, Expression expression, Pipe haystack, Pipe needle, Pipe caseSensitive) { - super(source, expression, Arrays.asList(haystack, needle)); - this.haystack = haystack; - this.needle = needle; - this.caseSensitive = caseSensitive; + public StringContainsFunctionPipe(Source source, Expression expression, Pipe string, Pipe substring) { + super(source, expression, Arrays.asList(string, substring)); + this.string = string; + this.substring = substring; } @Override public final Pipe replaceChildren(List newChildren) { - if (newChildren.size() != 3) { - throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]"); + if (newChildren.size() != 2) { + throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]"); } - return replaceChildren(newChildren.get(0), newChildren.get(1), newChildren.get(2)); + return replaceChildren(newChildren.get(0), newChildren.get(1)); } @Override public final Pipe resolveAttributes(AttributeResolver resolver) { - Pipe newHaystack = haystack.resolveAttributes(resolver); - Pipe newNeedle = needle.resolveAttributes(resolver); - Pipe newCaseSensitive = caseSensitive.resolveAttributes(resolver); - if (newHaystack == haystack && newNeedle == needle && newCaseSensitive == caseSensitive) { + Pipe newString = string.resolveAttributes(resolver); + Pipe newSubstring = substring.resolveAttributes(resolver); + if (newString == string && newSubstring == substring) { return this; } - return replaceChildren(newHaystack, newNeedle, newCaseSensitive); + return replaceChildren(newString, newSubstring); } @Override public boolean supportedByAggsOnlyQuery() { - return haystack.supportedByAggsOnlyQuery() && needle.supportedByAggsOnlyQuery() && caseSensitive.supportedByAggsOnlyQuery(); + return string.supportedByAggsOnlyQuery() && substring.supportedByAggsOnlyQuery(); } @Override public boolean resolved() { - return haystack.resolved() && needle.resolved() && caseSensitive.resolved(); + return string.resolved() && substring.resolved(); } - protected Pipe replaceChildren(Pipe haystack, Pipe needle, Pipe caseSensitive) { - return new StringContainsFunctionPipe(source(), expression(), haystack, needle, caseSensitive); + protected Pipe replaceChildren(Pipe string, Pipe substring) { + return new StringContainsFunctionPipe(source(), expression(), string, substring); } @Override public final void collectFields(QlSourceBuilder sourceBuilder) { - haystack.collectFields(sourceBuilder); - needle.collectFields(sourceBuilder); - caseSensitive.collectFields(sourceBuilder); + string.collectFields(sourceBuilder); + substring.collectFields(sourceBuilder); } @Override protected NodeInfo info() { - return NodeInfo.create(this, StringContainsFunctionPipe::new, expression(), haystack, needle, caseSensitive); + return NodeInfo.create(this, StringContainsFunctionPipe::new, expression(), string, substring); } @Override public StringContainsFunctionProcessor asProcessor() { - return new StringContainsFunctionProcessor(haystack.asProcessor(), needle.asProcessor(), caseSensitive.asProcessor()); + return new StringContainsFunctionProcessor(string.asProcessor(), substring.asProcessor()); } - public Pipe haystack() { - return haystack; + public Pipe string() { + return string; } - public Pipe needle() { - return needle; + public Pipe substring() { + return substring; } - public Pipe caseSensitive() { - return caseSensitive; - } @Override public int hashCode() { - return Objects.hash(source(), haystack(), needle(), caseSensitive()); + return Objects.hash(source(), string(), substring()); } @Override @@ -106,8 +100,7 @@ public boolean equals(Object obj) { StringContainsFunctionPipe other = (StringContainsFunctionPipe) obj; return Objects.equals(source(), other.source()) - && Objects.equals(haystack(), other.haystack()) - && Objects.equals(needle(), other.needle()) - && Objects.equals(caseSensitive(), other.caseSensitive()); + && Objects.equals(string(), other.string()) + && Objects.equals(substring(), other.substring()); } } diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessor.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessor.java new file mode 100644 index 0000000000000..c4b479d2811e6 --- /dev/null +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessor.java @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.eql.expression.function.scalar.string; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xpack.eql.EqlIllegalArgumentException; +import org.elasticsearch.xpack.ql.expression.gen.processor.Processor; + +import java.io.IOException; +import java.util.Objects; + +public class StringContainsFunctionProcessor implements Processor { + + public static final String NAME = "sstc"; + + private final Processor string, substring; + + public StringContainsFunctionProcessor(Processor string, Processor substring) { + this.string = string; + this.substring = substring; + } + + public StringContainsFunctionProcessor(StreamInput in) throws IOException { + string = in.readNamedWriteable(Processor.class); + substring = in.readNamedWriteable(Processor.class); + } + + @Override + public final void writeTo(StreamOutput out) throws IOException { + out.writeNamedWriteable(string); + out.writeNamedWriteable(substring); + } + + @Override + public Object process(Object input) { + return doProcess(string.process(input), substring.process(input)); + } + + public static Object doProcess(Object string, Object substring) { + if (string == null) { + return null; + } + + throwIfNotString(string); + throwIfNotString(substring); + + String strString = string.toString(); + String strSubstring = substring.toString(); + return StringUtils.stringContains(strString, strSubstring); + } + + private static void throwIfNotString(Object obj) { + if (!(obj instanceof String || obj instanceof Character)) { + throw new EqlIllegalArgumentException("A string/char is required; received [{}]", obj); + } + } + + protected Processor string() { + return string; + } + + public Processor substring() { + return substring; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + StringContainsFunctionProcessor other = (StringContainsFunctionProcessor) obj; + return Objects.equals(string(), other.string()) + && Objects.equals(substring(), other.substring()); + } + + @Override + public int hashCode() { + return Objects.hash(string(), substring()); + } + + + @Override + public String getWriteableName() { + return NAME; + } +} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java index 11657872c7f69..f60959d09d337 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java @@ -8,12 +8,32 @@ import org.elasticsearch.common.Strings; +import java.util.Locale; + import static org.elasticsearch.common.Strings.hasLength; final class StringUtils { private StringUtils() {} + /** + * Extracts a substring from string between left and right strings. + * Port of "between" function from the original EQL python implementation. + * + * @param string string to search through. + * @param substring string to search for. + * @return {@code true} if {@code string} string contains {@code substring} string. + */ + static boolean stringContains(String string, String substring) { + if (hasLength(string) == false || hasLength(substring) == false) { + return false; + } + + string = string.toLowerCase(Locale.ROOT); + substring = substring.toLowerCase(Locale.ROOT); + return string.contains(substring); + } + /** * Returns a substring using the Python slice semantics, meaning * start and end can be negative @@ -24,7 +44,7 @@ static String substringSlice(String string, int start, int end) { } int length = string.length(); - + // handle first negative values if (start < 0) { start += length; diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java deleted file mode 100644 index 88072d3d8a148..0000000000000 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessor.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; - -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xpack.eql.EqlIllegalArgumentException; -import org.elasticsearch.xpack.ql.expression.gen.processor.Processor; - -import java.io.IOException; -import java.util.Objects; - -public class StringContainsFunctionProcessor implements Processor { - - public static final String NAME = "sstringcontains"; - - private final Processor haystack, needle, caseSensitive; - - public StringContainsFunctionProcessor(Processor haystack, Processor needle, Processor caseSensitive) { - this.haystack = haystack; - this.needle = needle; - this.caseSensitive = caseSensitive; - } - - public StringContainsFunctionProcessor(StreamInput in) throws IOException { - haystack = in.readNamedWriteable(Processor.class); - needle = in.readNamedWriteable(Processor.class); - caseSensitive = in.readNamedWriteable(Processor.class); - } - - @Override - public final void writeTo(StreamOutput out) throws IOException { - out.writeNamedWriteable(haystack); - out.writeNamedWriteable(needle); - out.writeNamedWriteable(caseSensitive); - } - - @Override - public Object process(Object input) { - return doProcess(haystack.process(input), needle.process(input), caseSensitive.process(input)); - } - - public static Object doProcess(Object haystack, Object needle, Object caseSensitive) { - if (haystack == null) { - return null; - } - - throwIfNotString(haystack); - throwIfNotString(needle); - - throwIfNotBoolean(caseSensitive); - - String strHaystack = haystack.toString(); - String strNeedle = needle.toString(); - boolean bCaseSensitive = ((Boolean) caseSensitive).booleanValue(); - return StringContainsUtils.stringContains(strHaystack, strNeedle, bCaseSensitive); - } - - private static void throwIfNotString(Object obj) { - if (!(obj instanceof String || obj instanceof Character)) { - throw new EqlIllegalArgumentException("A string/char is required; received [{}]", obj); - } - } - - private static void throwIfNotBoolean(Object obj) { - if (!(obj instanceof Boolean)) { - throw new EqlIllegalArgumentException("A boolean is required; received [{}]", obj); - } - } - - protected Processor haystack() { - return haystack; - } - - public Processor needle() { - return needle; - } - - public Processor caseSensitive() { - return caseSensitive; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - StringContainsFunctionProcessor other = (StringContainsFunctionProcessor) obj; - return Objects.equals(haystack(), other.haystack()) - && Objects.equals(needle(), other.needle()) - && Objects.equals(caseSensitive(), other.caseSensitive()); - } - - @Override - public int hashCode() { - return Objects.hash(haystack(), needle(), caseSensitive()); - } - - - @Override - public String getWriteableName() { - return NAME; - } -} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java deleted file mode 100644 index d048da54a0851..0000000000000 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; - -import java.util.Locale; - -import static org.elasticsearch.common.Strings.hasLength; - -final class StringContainsUtils { - - private StringContainsUtils() { - } - - /** - * Extracts a substring from string between left and right strings. - * Port of "between" function from the original EQL python implementation. - * - * @param haystack string to search through. - * @param needle string to search for. - * @param caseSensitive flag for case-sensitive matching. - * @return {@code true} if {@code search} string contains {@code find} string. - */ - static boolean stringContains(String haystack, String needle, boolean caseSensitive) { - if (hasLength(haystack) == false || hasLength(needle) == false) { - return false; - } - - String search = haystack; - String find = needle; - if (caseSensitive == false) { - search = search.toLowerCase(Locale.ROOT); - find = find.toLowerCase(Locale.ROOT); - } - return search.contains(find); - } -} diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java index 0ec03f1c7e02f..0319fd91d49b8 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java @@ -10,7 +10,7 @@ import org.elasticsearch.xpack.eql.expression.function.scalar.string.LengthFunctionProcessor; import org.elasticsearch.xpack.eql.expression.function.scalar.string.StartsWithFunctionProcessor; import org.elasticsearch.xpack.eql.expression.function.scalar.string.SubstringFunctionProcessor; -import org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsFunctionProcessor; +import org.elasticsearch.xpack.eql.expression.function.scalar.string.StringContainsFunctionProcessor; import org.elasticsearch.xpack.ql.expression.function.scalar.whitelist.InternalQlScriptUtils; /* @@ -38,8 +38,8 @@ public static String substring(String s, Number start, Number end) { return (String) SubstringFunctionProcessor.doProcess(s, start, end); } - public static Boolean stringContains(String haystack, String needle, Boolean caseSensitive) { - return (Boolean) StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive); + public static Boolean stringContains(String string, String substring) { + return (Boolean) StringContainsFunctionProcessor.doProcess(string, substring); } } diff --git a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt index 098fe45b512ab..26ef2a64c66e5 100644 --- a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt +++ b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt @@ -59,5 +59,5 @@ class org.elasticsearch.xpack.eql.expression.function.scalar.whitelist.InternalE Integer length(String) Boolean startsWith(String, String) String substring(String, Number, Number) - Boolean stringContains(String, String, Boolean) + Boolean stringContains(String, String) } diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessorTests.java similarity index 66% rename from x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java rename to x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessorTests.java index 6286cb9733fc5..d59ff397cc5bd 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsFunctionProcessorTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContainsFunctionProcessorTests.java @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; +package org.elasticsearch.xpack.eql.expression.function.scalar.string; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.eql.EqlIllegalArgumentException; +import org.elasticsearch.xpack.eql.expression.function.scalar.string.StringContainsFunctionProcessor; import java.util.concurrent.Callable; @@ -26,27 +27,22 @@ protected static void run(Callable callable) throws Exception { public void testNullOrEmptyParameters() throws Exception { run(() -> { - String needle = randomBoolean() ? null : randomAlphaOfLength(10); + String substring = randomBoolean() ? null : randomAlphaOfLength(10); String str = randomBoolean() ? null : randomAlphaOfLength(10); - if (str != null && needle != null) { - str += needle; + if (str != null && substring != null) { + str += substring; str += randomAlphaOfLength(10); } - final String haystack = str; - Boolean caseSensitive = randomBoolean() ? null : randomBoolean(); + final String string = str; - // The haystack parameter can be null. Expect exception if any of other parameters is null. - if ((haystack != null) && (needle == null || caseSensitive == null)) { + // The string parameter can be null. Expect exception if any of other parameters is null. + if ((string != null) && (substring == null)) { EqlIllegalArgumentException e = expectThrows(EqlIllegalArgumentException.class, - () -> StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive)); - if (needle == null) { + () -> StringContainsFunctionProcessor.doProcess(string, substring)); assertThat(e.getMessage(), equalTo("A string/char is required; received [null]")); - } else { - assertThat(e.getMessage(), equalTo("A boolean is required; received [null]")); - } } else { - assertThat(StringContainsFunctionProcessor.doProcess(haystack, needle, caseSensitive), - equalTo(haystack == null? null : true)); + assertThat(StringContainsFunctionProcessor.doProcess(string, substring), + equalTo(string == null? null : true)); } return null; }); diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtilsTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtilsTests.java index 0abf9c5eb755d..169e9abf25efc 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtilsTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtilsTests.java @@ -8,10 +8,21 @@ import org.elasticsearch.test.ESTestCase; +import java.util.concurrent.Callable; + +import static org.elasticsearch.xpack.eql.expression.function.scalar.string.StringUtils.stringContains; import static org.elasticsearch.xpack.eql.expression.function.scalar.string.StringUtils.substringSlice; public class StringUtilsTests extends ESTestCase { + protected static final int NUMBER_OF_TEST_RUNS = 20; + + private static void run(Callable callable) throws Exception { + for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { + callable.call(); + } + } + public void testSubstringSlicePositive() { String str = randomAlphaOfLength(10); assertEquals(str.substring(1, 7), substringSlice(str, 1, 7)); @@ -72,4 +83,19 @@ public void testSubstringRandomSliceSameStartEnd() { public void testNullValue() { assertNull(substringSlice(null, 0, 0)); } + + public void testStringContainsWithNullOrEmpty() { + assertFalse(stringContains(null, null)); + assertFalse(stringContains(null, "")); + assertFalse(stringContains("", null)); + } + + public void testStringContainsWithRandom() throws Exception { + run(() -> { + String substring = randomAlphaOfLength(10); + String string = randomAlphaOfLength(10) + substring + randomAlphaOfLength(10); + assertTrue(stringContains(string, substring)); + return null; + }); + } } diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java deleted file mode 100644 index c47ee3ad440fb..0000000000000 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/expression/function/scalar/stringcontains/StringContainsUtilsTests.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains; - -import org.elasticsearch.test.ESTestCase; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Callable; - -import static org.elasticsearch.xpack.eql.expression.function.scalar.stringcontains.StringContainsUtils.stringContains; - -public class StringContainsUtilsTests extends ESTestCase { - - protected static final int NUMBER_OF_TEST_RUNS = 20; - - private static void run(Callable callable) throws Exception { - for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { - callable.call(); - } - } - - public void testStringContainsWithNullOrEmpty() { - List caseSensList = Arrays.asList(true, false); - for (boolean caseSensitive: caseSensList) { - assertFalse(stringContains(null, null, caseSensitive)); - assertFalse(stringContains(null, "", caseSensitive)); - assertFalse(stringContains("", null, caseSensitive)); - } - } - - public void testStringContainsWithRandom() throws Exception { - run(() -> { - String needle = randomAlphaOfLength(10); - String haystack = randomAlphaOfLength(10) + needle + randomAlphaOfLength(10); - assertTrue(stringContains(haystack, needle, true)); - return null; - }); - } -} diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java index 988e9001a78d3..55d40dd394215 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/planner/QueryFolderFailTests.java @@ -100,17 +100,13 @@ public void testWildcardWithNumericField() { public void testStringContainsWrongParams() { - assertEquals("1:16: error building [stringcontains]: expects two or three arguments", + assertEquals("1:16: error building [stringcontains]: expects exactly two arguments", errorParsing("process where stringContains()")); - assertEquals("1:16: error building [stringcontains]: expects two or three arguments", + assertEquals("1:16: error building [stringcontains]: expects exactly two arguments", errorParsing("process where stringContains(process_name)")); assertEquals("1:15: second argument of [stringContains(process_name, 1)] must be [string], found value [1] type [integer]", error("process where stringContains(process_name, 1)")); - - assertEquals("1:15: third argument of [stringContains(process_name, \"foo\", 2)] must be [boolean], found value [2] type [integer]", - error("process where stringContains(process_name, \"foo\", 2)")); - } } diff --git a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt index 3af7159c0e403..9be13f5da0b9c 100644 --- a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt +++ b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt @@ -106,8 +106,8 @@ InternalEqlScriptUtils.substring(InternalQlScriptUtils.docValue(doc,params.v0),p stringContains process where stringContains(process_name, "foo") "script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.stringContains( -InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))" -"params":{"v0":"process_name","v1":"foo","v2":false} +InternalQlScriptUtils.docValue(doc,params.v0),params.v1))" +"params":{"v0":"process_name","v1":"foo"} wildcardFunctionSingleArgument From 4e6adef70e5a82270a9b281dc25e3e54717b99c2 Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Sat, 4 Apr 2020 10:53:23 -0400 Subject: [PATCH 3/5] Fix javadoc comment for stringContains function --- .../eql/expression/function/scalar/string/StringUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java index f60959d09d337..34270edbad06a 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringUtils.java @@ -18,7 +18,7 @@ private StringUtils() {} /** * Extracts a substring from string between left and right strings. - * Port of "between" function from the original EQL python implementation. + * Port of "stringContains" function from the original EQL python implementation. * * @param string string to search through. * @param substring string to search for. From e19ba6736a86e1e42be059fd2e9747f87734ac00 Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Mon, 6 Apr 2020 13:42:48 -0400 Subject: [PATCH 4/5] Address code review comments --- .../function/scalar/string/StringContains.java | 1 - .../scalar/whitelist/InternalEqlScriptUtils.java | 7 +++---- .../eql/src/test/resources/queryfolder_tests.txt | 14 +++++++------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java index 9755db4d0563d..48f5856c955ab 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/string/StringContains.java @@ -29,7 +29,6 @@ /** * EQL specific stringContains function. - * https://eql.readthedocs.io/en/latest/query-guide/functions.html#stringContains * stringContains(a, b) * Returns true if b is a substring of a */ diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java index 0319fd91d49b8..61e109d2f6e4f 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/scalar/whitelist/InternalEqlScriptUtils.java @@ -34,12 +34,11 @@ public static Boolean startsWith(String s, String pattern) { return (Boolean) StartsWithFunctionProcessor.doProcess(s, pattern); } - public static String substring(String s, Number start, Number end) { - return (String) SubstringFunctionProcessor.doProcess(s, start, end); - } - public static Boolean stringContains(String string, String substring) { return (Boolean) StringContainsFunctionProcessor.doProcess(string, substring); } + public static String substring(String s, Number start, Number end) { + return (String) SubstringFunctionProcessor.doProcess(s, start, end); + } } diff --git a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt index 9be13f5da0b9c..4978885450688 100644 --- a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt +++ b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt @@ -96,13 +96,6 @@ InternalQlScriptUtils.docValue(doc,params.v0),params.v1))", "params":{"v0":"user_name","v1":"A"} -substringFunction -process where substring(file_name, -4) == '.exe' -"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.eq( -InternalEqlScriptUtils.substring(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2),params.v3))", -"params":{"v0":"file_name.keyword","v1":-4,"v2":null,"v3":".exe"} - - stringContains process where stringContains(process_name, "foo") "script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.stringContains( @@ -110,6 +103,13 @@ InternalQlScriptUtils.docValue(doc,params.v0),params.v1))" "params":{"v0":"process_name","v1":"foo"} +substringFunction +process where substring(file_name, -4) == '.exe' +"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.eq( +InternalEqlScriptUtils.substring(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2),params.v3))", +"params":{"v0":"file_name.keyword","v1":-4,"v2":null,"v3":".exe"} + + wildcardFunctionSingleArgument process where wildcard(process_path, "*\\red_ttp\\wininit.*") "wildcard":{"process_path":{"wildcard":"*\\\\red_ttp\\\\wininit.*" From 65765cc1316489776c75d6d8316b864e395aea9c Mon Sep 17 00:00:00 2001 From: Aleksandr Maus Date: Tue, 7 Apr 2020 15:29:03 -0400 Subject: [PATCH 5/5] Adjust queryfolder_tests.txt to the new format --- x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt index 49fce1941c366..896fb28c573de 100644 --- a/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt +++ b/x-pack/plugin/eql/src/test/resources/queryfolder_tests.txt @@ -107,10 +107,11 @@ InternalQlScriptUtils.docValue(doc,params.v0),params.v1))", stringContains process where stringContains(process_name, "foo") +; "script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.stringContains( InternalQlScriptUtils.docValue(doc,params.v0),params.v1))" "params":{"v0":"process_name","v1":"foo"} - +; substringFunction process where substring(file_name, -4) == '.exe' @@ -147,4 +148,4 @@ process where wildcard(process_path, "*\\red_ttp\\wininit.*", "*\\abc\\*", "*def "wildcard":{"process_path":{"wildcard":"*\\\\red_ttp\\\\wininit.*" "wildcard":{"process_path":{"wildcard":"*\\\\abc\\\\*" "wildcard":{"process_path":{"wildcard":"*def*" -; \ No newline at end of file +;