Skip to content

Commit 6ed5578

Browse files
authored
Limit the depth of a filter (#133194) (#133200)
This commit limits the maximum depth of a filter expression such as `abc.*.d*.*e.f.*` These expressions are most commonly used in `filter_path` on in a URL, and the limit has been set to 500 - in effect such a filter may only contain 500 `.` separators between field expressions. Backport of: #133113
1 parent 45d8d93 commit 6ed5578

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

docs/changelog/133113.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 133113
2+
summary: Limit the depth of a filter
3+
area: Infra/REST API
4+
type: enhancement
5+
issues: []

libs/x-content/src/main/java/org/elasticsearch/xcontent/support/filtering/FilterPath.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public class FilterPath {
2222
private static final String WILDCARD = "*";
2323
private static final String DOUBLE_WILDCARD = "**";
2424

25+
// This is ridiculously large, but we can be 100% certain that if any filter tries to exceed this depth then it is a mistake
26+
static final int MAX_TREE_DEPTH = 500;
27+
2528
private final Map<String, FilterPath> termsChildren;
2629
private final FilterPath[] wildcardChildren;
2730
private final String pattern;
@@ -132,6 +135,7 @@ private boolean matchFieldNamesWithDots(String name, int dotIndex, List<FilterPa
132135
}
133136

134137
private static class FilterPathBuilder {
138+
135139
private static class BuildNode {
136140
private final Map<String, BuildNode> children;
137141
private final boolean isFinalNode;
@@ -145,14 +149,19 @@ private static class BuildNode {
145149
private final BuildNode root = new BuildNode(false);
146150

147151
void insert(String filter) {
148-
insertNode(filter, root);
152+
insertNode(filter, root, 0);
149153
}
150154

151155
FilterPath build() {
152156
return buildPath("", root);
153157
}
154158

155-
static void insertNode(String filter, BuildNode node) {
159+
static void insertNode(String filter, BuildNode node, int depth) {
160+
if (depth > MAX_TREE_DEPTH) {
161+
throw new IllegalArgumentException(
162+
"Filter exceeds maximum depth at [" + (filter.length() > 100 ? filter.substring(0, 100) : filter) + "]"
163+
);
164+
}
156165
int end = filter.length();
157166
int splitPosition = -1;
158167
boolean findEscapes = false;
@@ -171,7 +180,7 @@ static void insertNode(String filter, BuildNode node) {
171180
String field = findEscapes ? filter.substring(0, splitPosition).replace("\\.", ".") : filter.substring(0, splitPosition);
172181
BuildNode child = node.children.computeIfAbsent(field, f -> new BuildNode(false));
173182
if (false == child.isFinalNode) {
174-
insertNode(filter.substring(splitPosition + 1), child);
183+
insertNode(filter.substring(splitPosition + 1), child, depth + 1);
175184
}
176185
} else {
177186
String field = findEscapes ? filter.replace("\\.", ".") : filter;

libs/x-content/src/test/java/org/elasticsearch/xcontent/support/filtering/FilterPathTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import static java.util.Collections.singleton;
2323
import static org.hamcrest.Matchers.arrayWithSize;
24+
import static org.hamcrest.Matchers.containsString;
2425
import static org.hamcrest.Matchers.is;
2526

2627
public class FilterPathTests extends ESTestCase {
@@ -403,4 +404,16 @@ public void testDotInFieldName() {
403404
assertTrue(filterPaths[0].matches("a.b.c.d", nextFilters, true));
404405
assertEquals(nextFilters.size(), 0);
405406
}
407+
408+
public void testDepthChecking() {
409+
final String atLimit = "x" + (".x").repeat(FilterPath.MAX_TREE_DEPTH);
410+
final String aboveLimit = atLimit + ".y";
411+
412+
var paths = FilterPath.compile(Set.of(atLimit));
413+
assertThat(paths, arrayWithSize(1));
414+
415+
var ex = expectThrows(IllegalArgumentException.class, () -> FilterPath.compile(Set.of(aboveLimit)));
416+
assertThat(ex.getMessage(), containsString("maximum depth"));
417+
assertThat(ex.getMessage(), containsString("[y]"));
418+
}
406419
}

0 commit comments

Comments
 (0)