This is an automated email from the ASF dual-hosted git repository.
FrankChen021 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new cf98cd7bce2 feat: Add Google RE2/J linear time regular expression as
alternative to Java regex (#19514)
cf98cd7bce2 is described below
commit cf98cd7bce2dd8de3f9b7109ea74586c997b5f5c
Author: Vivek Dhiman <[email protected]>
AuthorDate: Sun Aug 2 09:48:04 2026 +0530
feat: Add Google RE2/J linear time regular expression as alternative to
Java regex (#19514)
* Add Google RE2/J linear time regular expression as alternative to Java
regex
* Fix docs and spellings
* Simplify the regex abstraction by replacing the Java and RE2/J
implementation classes with anonymous implementations.
---
docs/ingestion/data-formats.md | 68 +++++++++++++
extensions-core/druid-kerberos/pom.xml | 4 +
licenses.yaml | 2 +-
processing/pom.xml | 5 +
.../druid/data/input/impl/RegexInputFormat.java | 10 +-
.../apache/druid/data/input/impl/RegexReader.java | 10 +-
.../org/apache/druid/guice/RegexEngineModule.java | 35 +++++++
.../apache/druid/guice/StartupInjectorBuilder.java | 1 +
.../java/org/apache/druid/regex/RegexConfig.java | 54 +++++++++++
.../org/apache/druid/regex/RegexEngineType.java | 26 +++++
.../java/org/apache/druid/regex/RegexMatcher.java | 31 ++++++
.../java/org/apache/druid/regex/RegexPattern.java | 26 +++++
.../apache/druid/regex/RegexPatternFactory.java | 108 +++++++++++++++++++++
.../data/input/impl/RegexInputFormatTest.java | 68 +++++++++++--
.../java/org/apache/druid/cli/CliCoordinator.java | 2 +
.../main/java/org/apache/druid/cli/CliIndexer.java | 4 +-
.../org/apache/druid/cli/CliMiddleManager.java | 4 +-
.../java/org/apache/druid/cli/CliOverlord.java | 4 +-
.../main/java/org/apache/druid/cli/CliPeon.java | 2 +
website/.spelling | 2 +
20 files changed, 446 insertions(+), 20 deletions(-)
diff --git a/docs/ingestion/data-formats.md b/docs/ingestion/data-formats.md
index 303e3f7188b..25615d98157 100644
--- a/docs/ingestion/data-formats.md
+++ b/docs/ingestion/data-formats.md
@@ -73,6 +73,74 @@ Besides text formats, Druid also supports binary formats
such as [Orc](#orc) and
Druid supports custom text data formats and can use the Regex input format to
parse them. However, be aware doing this to
parse data is less efficient than writing a native Java `InputFormat`
extension, or using an external stream processor. We welcome contributions of
new input formats.
+## Regex engine configuration
+
+The `regex` input format supports configurable regex engines using the runtime
property:
+
+```properties
+druid.regex.engine=JAVA
+```
+
+Supported values:
+
+| Value | Description
|
+|----------|--------------------------------------------------------------------------|
+| `JAVA` | Uses Java's built-in `java.util.regex.Pattern` engine.
|
+| `RE2J` | Uses `Google's RE2/J` regex engine with linear-time matching
guarantees. |
+
+Default value:
+
+```properties
+druid.regex.engine=JAVA
+```
+
+### RE2/J engine
+
+Setting:
+
+```properties
+druid.regex.engine=RE2J
+```
+
+enables the RE2/J regex engine for ingestion task `regex` input formats.
+
+RE2/J helps protect against catastrophic backtracking and Regular Expression
Denial of Service (ReDoS) attacks by guaranteeing linear-time regex evaluation.
+
+### Compatibility differences
+
+RE2/J does not support all Java regex features.
+
+Unsupported or partially supported features include:
+- back references
+- look behind assertions
+- some advanced backtracking behavior
+
+Patterns using unsupported constructs will fail during regex compilation.
+
+### Example of catastrophic backtracking
+
+The following Java regex may cause catastrophic backtracking:
+
+```regex
+^(.*a){20}$
+```
+
+against input such as:
+
+```text
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX
+```
+
+Using `RE2J` avoids this issue.
+
+### Performance considerations
+
+- `JAVA` may support more advanced regex syntax and behavior.
+- `RE2J` provides safer and more predictable runtime characteristics.
+- For trusted internal ingestion specs, `JAVA` may be preferred for
compatibility.
+- For externally supplied regex patterns, `RE2J` is recommended.
+
+
## Input format
You can use the `inputFormat` field to specify the data format for your input
data.
diff --git a/extensions-core/druid-kerberos/pom.xml
b/extensions-core/druid-kerberos/pom.xml
index 5c3f0780e84..0a5776c039f 100644
--- a/extensions-core/druid-kerberos/pom.xml
+++ b/extensions-core/druid-kerberos/pom.xml
@@ -248,6 +248,10 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.re2j</groupId>
+ <artifactId>re2j</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
diff --git a/licenses.yaml b/licenses.yaml
index 44ec5ec5eaf..c27dee099e8 100644
--- a/licenses.yaml
+++ b/licenses.yaml
@@ -5366,7 +5366,7 @@ name: RE2/J
license_category: binary
module: java-core
license_name: The Go license
-version: 1.1
+version: 1.7
license_file_path: licenses/bin/re2j.GO
libraries:
- com.google.re2j: re2j
diff --git a/processing/pom.xml b/processing/pom.xml
index aac89544f3d..9adee843a80 100644
--- a/processing/pom.xml
+++ b/processing/pom.xml
@@ -297,6 +297,11 @@
<groupId>io.timeandspace</groupId>
<artifactId>cron-scheduler</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.google.re2j</groupId>
+ <artifactId>re2j</artifactId>
+ <version>1.7</version>
+ </dependency>
<!-- com.lmax.disruptor is optional in log4j-core, so we explicitly
include it here -->
<dependency>
diff --git
a/processing/src/main/java/org/apache/druid/data/input/impl/RegexInputFormat.java
b/processing/src/main/java/org/apache/druid/data/input/impl/RegexInputFormat.java
index a3057fa212b..e807427a686 100644
---
a/processing/src/main/java/org/apache/druid/data/input/impl/RegexInputFormat.java
+++
b/processing/src/main/java/org/apache/druid/data/input/impl/RegexInputFormat.java
@@ -19,6 +19,7 @@
package org.apache.druid.data.input.impl;
+import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -29,12 +30,14 @@ import org.apache.druid.data.input.InputEntity;
import org.apache.druid.data.input.InputEntityReader;
import org.apache.druid.data.input.InputFormat;
import org.apache.druid.data.input.InputRowSchema;
+import org.apache.druid.regex.RegexConfig;
+import org.apache.druid.regex.RegexPattern;
+import org.apache.druid.regex.RegexPatternFactory;
import org.apache.druid.utils.CompressionUtils;
import javax.annotation.Nullable;
import java.io.File;
import java.util.List;
-import java.util.regex.Pattern;
public class RegexInputFormat implements InputFormat
{
@@ -44,10 +47,11 @@ public class RegexInputFormat implements InputFormat
private final String listDelimiter;
private final List<String> columns;
@JsonIgnore
- private final Supplier<Pattern> compiledPatternSupplier;
+ private final Supplier<RegexPattern> compiledPatternSupplier;
@JsonCreator
public RegexInputFormat(
+ @JacksonInject RegexConfig regexConfig,
@JsonProperty("pattern") String pattern,
@JsonProperty("listDelimiter") @Nullable String listDelimiter,
@JsonProperty("columns") @Nullable List<String> columns
@@ -56,7 +60,7 @@ public class RegexInputFormat implements InputFormat
this.pattern = pattern;
this.listDelimiter = listDelimiter;
this.columns = columns;
- this.compiledPatternSupplier = Suppliers.memoize(() ->
Pattern.compile(pattern));
+ this.compiledPatternSupplier = Suppliers.memoize(() ->
RegexPatternFactory.compile(regexConfig.getEngine(), pattern));
}
@JsonProperty
diff --git
a/processing/src/main/java/org/apache/druid/data/input/impl/RegexReader.java
b/processing/src/main/java/org/apache/druid/data/input/impl/RegexReader.java
index 66f4d25b474..5b6dbee4180 100644
--- a/processing/src/main/java/org/apache/druid/data/input/impl/RegexReader.java
+++ b/processing/src/main/java/org/apache/druid/data/input/impl/RegexReader.java
@@ -30,19 +30,19 @@ import org.apache.druid.java.util.common.collect.Utils;
import org.apache.druid.java.util.common.parsers.ParseException;
import org.apache.druid.java.util.common.parsers.ParserUtils;
import org.apache.druid.java.util.common.parsers.Parsers;
+import org.apache.druid.regex.RegexMatcher;
+import org.apache.druid.regex.RegexPattern;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class RegexReader extends TextReader.Strings
{
private final String pattern;
- private final Pattern compiledPattern;
+ private final RegexPattern compiledPattern;
private final Function<String, Object> transformationFunction;
private List<String> columns;
@@ -51,7 +51,7 @@ public class RegexReader extends TextReader.Strings
InputRowSchema inputRowSchema,
InputEntity source,
String pattern,
- Pattern compiledPattern,
+ RegexPattern compiledPattern,
@Nullable String listDelimiter,
@Nullable List<String> columns
)
@@ -83,7 +83,7 @@ public class RegexReader extends TextReader.Strings
private Map<String, Object> parseLine(String line)
{
try {
- final Matcher matcher = compiledPattern.matcher(line);
+ final RegexMatcher matcher = compiledPattern.matcher(line);
if (!matcher.matches()) {
throw new ParseException(line, "Incorrect Regex: %s . No match
found.", pattern);
diff --git
a/processing/src/main/java/org/apache/druid/guice/RegexEngineModule.java
b/processing/src/main/java/org/apache/druid/guice/RegexEngineModule.java
new file mode 100644
index 00000000000..8579078fd6d
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/guice/RegexEngineModule.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.guice;
+
+import com.google.inject.Binder;
+import com.google.inject.Module;
+import org.apache.druid.regex.RegexConfig;
+
+/**
+ */
+public class RegexEngineModule implements Module
+{
+ @Override
+ public void configure(Binder binder)
+ {
+ JsonConfigProvider.bind(binder, "druid.regex", RegexConfig.class);
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/guice/StartupInjectorBuilder.java
b/processing/src/main/java/org/apache/druid/guice/StartupInjectorBuilder.java
index 70dd1cc5151..096de6464f0 100644
---
a/processing/src/main/java/org/apache/druid/guice/StartupInjectorBuilder.java
+++
b/processing/src/main/java/org/apache/druid/guice/StartupInjectorBuilder.java
@@ -65,6 +65,7 @@ public class StartupInjectorBuilder extends
BaseInjectorBuilder<StartupInjectorB
new JacksonModule(),
new ConfigModule(),
new ExpressionProcessingModule(),
+ new RegexEngineModule(),
binder -> binder.bind(DruidSecondaryModule.class),
binder -> binder.bind(PropertiesValidator.class) // this gets
properties injected, later call to validate checks
);
diff --git a/processing/src/main/java/org/apache/druid/regex/RegexConfig.java
b/processing/src/main/java/org/apache/druid/regex/RegexConfig.java
new file mode 100644
index 00000000000..37db9b6f7bb
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/regex/RegexConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.regex;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RegexConfig
+{
+ @JsonProperty
+ private RegexEngineType engine = RegexEngineType.JAVA;
+
+ public RegexEngineType getEngine()
+ {
+ return engine;
+ }
+
+ public void setEngine(RegexEngineType engine)
+ {
+ this.engine = engine;
+ }
+
+ public static RegexConfig with(RegexEngineType engine)
+ {
+ final RegexConfig config = new RegexConfig();
+ config.setEngine(engine);
+
+ return config;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "RegexConfig {" +
+ "engine=" + engine +
+ '}';
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/regex/RegexEngineType.java
b/processing/src/main/java/org/apache/druid/regex/RegexEngineType.java
new file mode 100644
index 00000000000..4226bcb2a75
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/regex/RegexEngineType.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.regex;
+
+public enum RegexEngineType
+{
+ JAVA,
+ RE2J
+}
diff --git a/processing/src/main/java/org/apache/druid/regex/RegexMatcher.java
b/processing/src/main/java/org/apache/druid/regex/RegexMatcher.java
new file mode 100644
index 00000000000..10c1deab6e2
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/regex/RegexMatcher.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.regex;
+
+public interface RegexMatcher
+{
+ boolean matches();
+
+ boolean find();
+
+ String group(int group);
+
+ int groupCount();
+}
diff --git a/processing/src/main/java/org/apache/druid/regex/RegexPattern.java
b/processing/src/main/java/org/apache/druid/regex/RegexPattern.java
new file mode 100644
index 00000000000..ea983231302
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/regex/RegexPattern.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.regex;
+
+@FunctionalInterface
+public interface RegexPattern
+{
+ RegexMatcher matcher(String input);
+}
diff --git
a/processing/src/main/java/org/apache/druid/regex/RegexPatternFactory.java
b/processing/src/main/java/org/apache/druid/regex/RegexPatternFactory.java
new file mode 100644
index 00000000000..38cffd72d97
--- /dev/null
+++ b/processing/src/main/java/org/apache/druid/regex/RegexPatternFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.regex;
+
+
+public class RegexPatternFactory
+{
+ public static RegexPattern compile(RegexEngineType type, String regex)
+ {
+ switch (type) {
+ case RE2J:
+ return compileRe2j(regex);
+
+ case JAVA:
+ default:
+ return compileJava(regex);
+ }
+ }
+
+ private static RegexPattern compileJava(String regex)
+ {
+ final java.util.regex.Pattern pattern =
java.util.regex.Pattern.compile(regex);
+
+ return input -> {
+ final java.util.regex.Matcher matcher = pattern.matcher(input);
+
+ return new RegexMatcher()
+ {
+ @Override
+ public boolean matches()
+ {
+ return matcher.matches();
+ }
+
+ @Override
+ public boolean find()
+ {
+ return matcher.find();
+ }
+
+ @Override
+ public String group(int group)
+ {
+ return matcher.group(group);
+ }
+
+ @Override
+ public int groupCount()
+ {
+ return matcher.groupCount();
+ }
+ };
+ };
+ }
+
+ private static RegexPattern compileRe2j(String regex)
+ {
+ final com.google.re2j.Pattern pattern =
com.google.re2j.Pattern.compile(regex);
+
+ return input -> {
+ final com.google.re2j.Matcher matcher = pattern.matcher(input);
+
+ return new RegexMatcher()
+ {
+ @Override
+ public boolean matches()
+ {
+ return matcher.matches();
+ }
+
+ @Override
+ public boolean find()
+ {
+ return matcher.find();
+ }
+
+ @Override
+ public String group(int group)
+ {
+ return matcher.group(group);
+ }
+
+ @Override
+ public int groupCount()
+ {
+ return matcher.groupCount();
+ }
+ };
+ };
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/data/input/impl/RegexInputFormatTest.java
b/processing/src/test/java/org/apache/druid/data/input/impl/RegexInputFormatTest.java
index 74c4849de31..55c54908b5a 100644
---
a/processing/src/test/java/org/apache/druid/data/input/impl/RegexInputFormatTest.java
+++
b/processing/src/test/java/org/apache/druid/data/input/impl/RegexInputFormatTest.java
@@ -19,15 +19,25 @@
package org.apache.druid.data.input.impl;
+import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.google.common.collect.ImmutableList;
+import org.apache.druid.data.input.InputEntityReader;
import org.apache.druid.data.input.InputFormat;
+import org.apache.druid.java.util.common.parsers.CloseableIterator;
+import org.apache.druid.java.util.common.parsers.ParseException;
+import org.apache.druid.regex.RegexConfig;
+import org.apache.druid.regex.RegexEngineType;
import org.apache.druid.utils.CompressionUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Map;
public class RegexInputFormatTest
@@ -40,10 +50,15 @@ public class RegexInputFormatTest
mapper.registerSubtypes(new NamedType(RegexInputFormat.class, "regex"));
}
- @Test
- public void testSerde() throws IOException
+ @ParameterizedTest
+ @EnumSource(RegexEngineType.class)
+ public void testSerde(RegexEngineType engineType) throws IOException
{
+ final RegexConfig regexConfig = RegexConfig.with(engineType);
+ mapper.setInjectableValues(new
InjectableValues.Std().addValue(RegexConfig.class, regexConfig));
+
final RegexInputFormat expected = new RegexInputFormat(
+ regexConfig,
"//[^\\r\\n]*[\\r\\n]",
"|",
ImmutableList.of("col1", "col2", "col3")
@@ -57,10 +72,12 @@ public class RegexInputFormatTest
Assertions.assertEquals(expected.getColumns(), fromJson.getColumns());
}
- @Test
- public void testIgnoreCompiledPatternInJson() throws IOException
+ @ParameterizedTest
+ @EnumSource(RegexEngineType.class)
+ public void testIgnoreCompiledPatternInJson(RegexEngineType engineType)
throws IOException
{
final RegexInputFormat expected = new RegexInputFormat(
+ RegexConfig.with(engineType),
"//[^\\r\\n]*[\\r\\n]",
"|",
ImmutableList.of("col1", "col2", "col3")
@@ -71,21 +88,27 @@ public class RegexInputFormatTest
Assertions.assertFalse(map.containsKey("compiledPattern"));
}
- @Test
- public void test_getWeightedSize_withoutCompression()
+ @ParameterizedTest
+ @EnumSource(RegexEngineType.class)
+ public void test_getWeightedSize_withoutCompression(RegexEngineType
engineType)
{
final RegexInputFormat format = new RegexInputFormat(
+ RegexConfig.with(engineType),
"//[^\\r\\n]*[\\r\\n]",
"|",
ImmutableList.of("col1", "col2", "col3")
);
final long unweightedSize = 100L;
Assertions.assertEquals(unweightedSize, format.getWeightedSize("file.txt",
unweightedSize));
+
}
- @Test
- public void test_getWeightedSize_withGzCompression()
+
+ @ParameterizedTest
+ @EnumSource(RegexEngineType.class)
+ public void test_getWeightedSize_withGzCompression(RegexEngineType
engineType)
{
final RegexInputFormat format = new RegexInputFormat(
+ RegexConfig.with(engineType),
"//[^\\r\\n]*[\\r\\n]",
"|",
ImmutableList.of("col1", "col2", "col3")
@@ -96,4 +119,33 @@ public class RegexInputFormatTest
format.getWeightedSize("file.txt.gz", unweightedSize)
);
}
+
+ @Test
+ @Timeout(10)
+ public void test_backtracking() throws IOException
+ {
+ final RegexInputFormat inputFormat = new RegexInputFormat(
+ RegexConfig.with(RegexEngineType.RE2J),
+ "^(.*a){20}$",
+ null,
+ ImmutableList.of("value")
+ );
+
+ String maliciousInput = "a".repeat(50) + "X";
+ InputEntityReader reader = inputFormat.createReader(
+ null,
+ new ByteEntity(maliciousInput.getBytes(StandardCharsets.UTF_8)),
+ null
+ );
+
+ try (CloseableIterator<?> iterator = reader.read()) {
+ while (iterator.hasNext()) {
+ iterator.next();
+ }
+ }
+
+ catch (ParseException ignored) {
+ // expected for non-matching input
+ }
+ }
}
diff --git a/services/src/main/java/org/apache/druid/cli/CliCoordinator.java
b/services/src/main/java/org/apache/druid/cli/CliCoordinator.java
index 7fc344d781d..07b8ac6398a 100644
--- a/services/src/main/java/org/apache/druid/cli/CliCoordinator.java
+++ b/services/src/main/java/org/apache/druid/cli/CliCoordinator.java
@@ -50,6 +50,7 @@ import org.apache.druid.guice.ManageLifecycle;
import org.apache.druid.guice.MetadataConfigModule;
import org.apache.druid.guice.MetadataManagerModule;
import org.apache.druid.guice.QueryableModule;
+import org.apache.druid.guice.RegexEngineModule;
import org.apache.druid.guice.SegmentSchemaCacheModule;
import org.apache.druid.guice.SupervisorCleanupModule;
import org.apache.druid.guice.annotations.EscalatedGlobal;
@@ -330,6 +331,7 @@ public class CliCoordinator extends ServerRunnable
modules.add(new MSQIndexingModule());
modules.add(new MSQDurableStorageModule());
modules.add(new MSQExternalDataSourceModule());
+ modules.add(new RegexEngineModule());
}
return modules;
diff --git a/services/src/main/java/org/apache/druid/cli/CliIndexer.java
b/services/src/main/java/org/apache/druid/cli/CliIndexer.java
index 7839ba7e6b4..6ffb17ad637 100644
--- a/services/src/main/java/org/apache/druid/cli/CliIndexer.java
+++ b/services/src/main/java/org/apache/druid/cli/CliIndexer.java
@@ -47,6 +47,7 @@ import org.apache.druid.guice.LifecycleModule;
import org.apache.druid.guice.QueryRunnerFactoryModule;
import org.apache.druid.guice.QueryableModule;
import org.apache.druid.guice.QueryablePeonModule;
+import org.apache.druid.guice.RegexEngineModule;
import org.apache.druid.guice.SegmentWranglerModule;
import org.apache.druid.guice.ServerTypeConfig;
import org.apache.druid.guice.annotations.AttemptId;
@@ -243,7 +244,8 @@ public class CliIndexer extends ServerRunnable
new MSQIndexingModule(),
new MSQDurableStorageModule(),
new MSQExternalDataSourceModule(),
- new IndexerMemoryManagementModule()
+ new IndexerMemoryManagementModule(),
+ new RegexEngineModule()
);
}
}
diff --git a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java
b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java
index 4476a64fbca..e5cebe3adc3 100644
--- a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java
+++ b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java
@@ -45,6 +45,7 @@ import org.apache.druid.guice.LifecycleModule;
import org.apache.druid.guice.ManageLifecycle;
import org.apache.druid.guice.MiddleManagerServiceModule;
import org.apache.druid.guice.PolyBind;
+import org.apache.druid.guice.RegexEngineModule;
import org.apache.druid.guice.annotations.Self;
import org.apache.druid.indexing.common.RetryPolicyFactory;
import org.apache.druid.indexing.common.TaskStorageDirTracker;
@@ -243,7 +244,8 @@ public class CliMiddleManager extends ServerRunnable
new LookupSerdeModule(),
new MSQIndexingModule(),
new MSQDurableStorageModule(),
- new MSQExternalDataSourceModule()
+ new MSQExternalDataSourceModule(),
+ new RegexEngineModule()
);
}
diff --git a/services/src/main/java/org/apache/druid/cli/CliOverlord.java
b/services/src/main/java/org/apache/druid/cli/CliOverlord.java
index 50116cb4968..f7e2d95d9b6 100644
--- a/services/src/main/java/org/apache/druid/cli/CliOverlord.java
+++ b/services/src/main/java/org/apache/druid/cli/CliOverlord.java
@@ -52,6 +52,7 @@ import org.apache.druid.guice.ListProvider;
import org.apache.druid.guice.ManageLifecycle;
import org.apache.druid.guice.MetadataManagerModule;
import org.apache.druid.guice.PolyBind;
+import org.apache.druid.guice.RegexEngineModule;
import org.apache.druid.guice.SupervisorModule;
import org.apache.druid.guice.annotations.Json;
import org.apache.druid.indexing.common.RetryPolicyFactory;
@@ -505,7 +506,8 @@ public class CliOverlord extends ServerRunnable
new SamplerModule(),
new MSQIndexingModule(),
new MSQDurableStorageModule(),
- new MSQExternalDataSourceModule()
+ new MSQExternalDataSourceModule(),
+ new RegexEngineModule()
);
}
diff --git a/services/src/main/java/org/apache/druid/cli/CliPeon.java
b/services/src/main/java/org/apache/druid/cli/CliPeon.java
index 95622409a27..6afc51fae19 100644
--- a/services/src/main/java/org/apache/druid/cli/CliPeon.java
+++ b/services/src/main/java/org/apache/druid/cli/CliPeon.java
@@ -61,6 +61,7 @@ import org.apache.druid.guice.PolyBind;
import org.apache.druid.guice.QueryRunnerFactoryModule;
import org.apache.druid.guice.QueryableModule;
import org.apache.druid.guice.QueryablePeonModule;
+import org.apache.druid.guice.RegexEngineModule;
import org.apache.druid.guice.SegmentWranglerModule;
import org.apache.druid.guice.ServerTypeConfig;
import org.apache.druid.guice.annotations.AttemptId;
@@ -217,6 +218,7 @@ public class CliPeon extends GuiceRunnable
new SegmentWranglerModule(),
new JoinableFactoryModule(),
new IndexingServiceTaskLogsModule(properties),
+ new RegexEngineModule(),
new Module()
{
@SuppressForbidden(reason = "System#out, System#err")
diff --git a/website/.spelling b/website/.spelling
index c99c42107f3..a74372f7dc1 100644
--- a/website/.spelling
+++ b/website/.spelling
@@ -534,9 +534,11 @@ quantile
quantiles
queryable
quickstart
+RE2
realtime
rebalance
redis
+ReDoS
regexes
reimported
reindex
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]