Skip to content

Commit 0b09059

Browse files
[ES|QL] Validate index name in parser (#112081) (#113985)
* validate index name in parser
1 parent 02a2b98 commit 0b09059

File tree

5 files changed

+284
-12
lines changed

5 files changed

+284
-12
lines changed

docs/changelog/112081.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 112081
2+
summary: "[ES|QL] Validate index name in parser"
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/util/StringUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ private StringUtils() {}
3939
public static final String NEW_LINE = "\n";
4040
public static final String SQL_WILDCARD = "%";
4141
public static final String WILDCARD = "*";
42+
public static final String EXCLUSION = "-";
4243

4344
private static final String[] INTEGER_ORDINALS = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
4445

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/IdentifierBuilder.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@
88
package org.elasticsearch.xpack.esql.parser;
99

1010
import org.antlr.v4.runtime.tree.TerminalNode;
11+
import org.elasticsearch.ElasticsearchParseException;
12+
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
13+
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
1114
import org.elasticsearch.common.Strings;
15+
import org.elasticsearch.indices.InvalidIndexNameException;
16+
import org.elasticsearch.xpack.esql.core.util.Holder;
1217
import org.elasticsearch.xpack.esql.parser.EsqlBaseParser.IdentifierContext;
1318
import org.elasticsearch.xpack.esql.parser.EsqlBaseParser.IndexStringContext;
1419

1520
import java.util.ArrayList;
1621
import java.util.List;
1722

1823
import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR;
24+
import static org.elasticsearch.xpack.esql.core.util.StringUtils.EXCLUSION;
25+
import static org.elasticsearch.xpack.esql.core.util.StringUtils.WILDCARD;
26+
import static org.elasticsearch.xpack.esql.parser.ParserUtils.source;
1927

2028
abstract class IdentifierBuilder extends AbstractBuilder {
2129

@@ -46,12 +54,54 @@ public String visitIndexString(IndexStringContext ctx) {
4654

4755
public String visitIndexPattern(List<EsqlBaseParser.IndexPatternContext> ctx) {
4856
List<String> patterns = new ArrayList<>(ctx.size());
57+
Holder<Boolean> hasSeenStar = new Holder<>(false);
4958
ctx.forEach(c -> {
5059
String indexPattern = visitIndexString(c.indexString());
60+
hasSeenStar.set(indexPattern.contains(WILDCARD) || hasSeenStar.get());
61+
validateIndexPattern(indexPattern, c, hasSeenStar.get());
5162
patterns.add(
5263
c.clusterString() != null ? c.clusterString().getText() + REMOTE_CLUSTER_INDEX_SEPARATOR + indexPattern : indexPattern
5364
);
5465
});
5566
return Strings.collectionToDelimitedString(patterns, ",");
5667
}
68+
69+
private static void validateIndexPattern(String indexPattern, EsqlBaseParser.IndexPatternContext ctx, boolean hasSeenStar) {
70+
// multiple index names can be in the same double quote, e.g. indexPattern = "idx1, *, -idx2"
71+
String[] indices = indexPattern.split(",");
72+
boolean hasExclusion = false;
73+
for (String index : indices) {
74+
hasSeenStar = index.contains(WILDCARD) || hasSeenStar;
75+
index = index.replace(WILDCARD, "").strip();
76+
if (index.isBlank()) {
77+
continue;
78+
}
79+
hasExclusion = index.startsWith(EXCLUSION);
80+
index = removeExclusion(index);
81+
String tempName;
82+
try {
83+
// remove the exclusion outside of <>, from index names with DateMath expression,
84+
// e.g. -<-logstash-{now/d}> becomes <-logstash-{now/d}> before calling resolveDateMathExpression
85+
tempName = IndexNameExpressionResolver.resolveDateMathExpression(index);
86+
} catch (ElasticsearchParseException e) {
87+
// throws exception if the DateMath expression is invalid, resolveDateMathExpression does not complain about exclusions
88+
throw new ParsingException(e, source(ctx), e.getMessage());
89+
}
90+
hasExclusion = tempName.startsWith(EXCLUSION) || hasExclusion;
91+
index = tempName.equals(index) ? index : removeExclusion(tempName);
92+
try {
93+
MetadataCreateIndexService.validateIndexOrAliasName(index, InvalidIndexNameException::new);
94+
} catch (InvalidIndexNameException e) {
95+
// ignore invalid index name if it has exclusions and there is an index with wildcard before it
96+
if (hasSeenStar && hasExclusion) {
97+
continue;
98+
}
99+
throw new ParsingException(e, source(ctx), e.getMessage());
100+
}
101+
}
102+
}
103+
104+
private static String removeExclusion(String indexPattern) {
105+
return indexPattern.charAt(0) == EXCLUSION.charAt(0) ? indexPattern.substring(1) : indexPattern;
106+
}
57107
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/AbstractStatementParserTests.java

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

88
package org.elasticsearch.xpack.esql.parser;
99

10+
import org.elasticsearch.common.logging.LoggerMessageFormat;
1011
import org.elasticsearch.test.ESTestCase;
1112
import org.elasticsearch.xpack.esql.VerificationException;
1213
import org.elasticsearch.xpack.esql.core.expression.Literal;
@@ -38,6 +39,10 @@ void assertStatement(String statement, LogicalPlan expected) {
3839
assertThat(statement, actual, equalTo(expected));
3940
}
4041

42+
LogicalPlan statement(String query, String arg) {
43+
return statement(LoggerMessageFormat.format(null, query, arg), new QueryParams());
44+
}
45+
4146
LogicalPlan statement(String e) {
4247
return statement(e, new QueryParams());
4348
}
@@ -124,4 +129,12 @@ void expectError(String query, List<QueryParam> params, String errorMessage) {
124129
);
125130
assertThat(e.getMessage(), containsString(errorMessage));
126131
}
132+
133+
void expectInvalidIndexNameErrorWithLineNumber(String query, String arg, String lineNumber, String indexString) {
134+
expectError(LoggerMessageFormat.format(null, query, arg), lineNumber + "Invalid index name [" + indexString);
135+
}
136+
137+
void expectDateMathErrorWithLineNumber(String query, String arg, String lineNumber, String error) {
138+
expectError(LoggerMessageFormat.format(null, query, arg), lineNumber + error);
139+
}
127140
}

0 commit comments

Comments
 (0)