Skip to content

Commit

Permalink
Mark all scripted field queries as expensive (#59658)
Browse files Browse the repository at this point in the history
This will cause them to fail if you don't have permission to execute
expensive queries.
  • Loading branch information
nik9000 authored Jul 16, 2020
1 parent 30861e6 commit 4ba86b1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.apache.lucene.search.MultiTermQuery.RewriteMethod;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.time.DateMathParser;
Expand Down Expand Up @@ -38,6 +39,7 @@
import java.util.Set;

import static java.util.stream.Collectors.toSet;
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;

public final class RuntimeKeywordMappedFieldType extends MappedFieldType {

Expand Down Expand Up @@ -77,8 +79,21 @@ private StringScriptFieldScript.LeafFactory leafFactory(QueryShardContext contex
return scriptFactory.newFactory(script.getParams(), context.lookup());
}

private void checkAllowExpensiveQueries(QueryShardContext context) {
if (context.allowExpensiveQueries() == false) {
throw new ElasticsearchException(
"queries cannot be executed against ["
+ ScriptFieldMapper.CONTENT_TYPE
+ "] fields while ["
+ ALLOW_EXPENSIVE_QUERIES.getKey()
+ "] is set to [false]."
);
}
}

@Override
public Query existsQuery(QueryShardContext context) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldExistsQuery(script, leafFactory(context), name());
}

Expand All @@ -91,6 +106,7 @@ public Query fuzzyQuery(
boolean transpositions,
QueryShardContext context
) {
checkAllowExpensiveQueries(context);
return StringScriptFieldFuzzyQuery.build(
script,
leafFactory(context),
Expand All @@ -104,6 +120,7 @@ public Query fuzzyQuery(

@Override
public Query prefixQuery(String value, RewriteMethod method, org.elasticsearch.index.query.QueryShardContext context) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldPrefixQuery(script, leafFactory(context), name(), value);
}

Expand All @@ -118,6 +135,7 @@ public Query rangeQuery(
DateMathParser parser,
QueryShardContext context
) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldRangeQuery(
script,
leafFactory(context),
Expand All @@ -131,22 +149,26 @@ public Query rangeQuery(

@Override
public Query regexpQuery(String value, int flags, int maxDeterminizedStates, RewriteMethod method, QueryShardContext context) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldRegexpQuery(script, leafFactory(context), name(), value, flags, maxDeterminizedStates);
}

@Override
public Query termQuery(Object value, QueryShardContext context) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldTermQuery(script, leafFactory(context), name(), BytesRefs.toString(Objects.requireNonNull(value)));
}

@Override
public Query termsQuery(List<?> values, QueryShardContext context) {
checkAllowExpensiveQueries(context);
Set<String> terms = values.stream().map(v -> BytesRefs.toString(Objects.requireNonNull(v))).collect(toSet());
return new StringScriptFieldTermsQuery(script, leafFactory(context), name(), terms);
}

@Override
public Query wildcardQuery(String value, RewriteMethod method, QueryShardContext context) {
checkAllowExpensiveQueries(context);
return new StringScriptFieldWildcardQuery(script, leafFactory(context), name(), value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
Expand All @@ -42,6 +43,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

import static java.util.Collections.emptyMap;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -104,6 +106,10 @@ public void testExistsQuery() throws IOException {
}
}

public void testExistsQueryIsExpensive() throws IOException {
checkExpensiveQuery(RuntimeKeywordMappedFieldType::existsQuery);
}

public void testFuzzyQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": \"cat\"}")))); // No edits, matches
Expand All @@ -121,6 +127,19 @@ public void testFuzzyQuery() throws IOException {
}
}

public void testFuzzyQueryIsExpensive() throws IOException {
checkExpensiveQuery(
(ft, ctx) -> ft.fuzzyQuery(
randomAlphaOfLengthBetween(1, 1000),
randomFrom(Fuzziness.AUTO, Fuzziness.ZERO, Fuzziness.ONE, Fuzziness.TWO),
randomInt(),
randomInt(),
randomBoolean(),
ctx
)
);
}

public void testPrefixQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": \"cat\"}"))));
Expand All @@ -133,6 +152,10 @@ public void testPrefixQuery() throws IOException {
}
}

public void testPrefixQueryIsExpensive() throws IOException {
checkExpensiveQuery((ft, ctx) -> ft.prefixQuery(randomAlphaOfLengthBetween(1, 1000), null, ctx));
}

public void testRangeQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": \"cat\"}"))));
Expand All @@ -148,6 +171,21 @@ public void testRangeQuery() throws IOException {
}
}

public void testRangeQueryIsExpensive() throws IOException {
checkExpensiveQuery(
(ft, ctx) -> ft.rangeQuery(
"a" + randomAlphaOfLengthBetween(0, 1000),
"b" + randomAlphaOfLengthBetween(0, 1000),
randomBoolean(),
randomBoolean(),
null,
null,
null,
ctx
)
);
}

public void testRegexpQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": \"cat\"}"))));
Expand All @@ -165,6 +203,10 @@ public void testRegexpQuery() throws IOException {
}
}

public void testRegexpQueryIsExpensive() throws IOException {
checkExpensiveQuery((ft, ctx) -> ft.regexpQuery(randomAlphaOfLengthBetween(1, 1000), randomInt(0xFFFF), randomInt(), null, ctx));
}

public void testTermQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 1}"))));
Expand All @@ -176,6 +218,10 @@ public void testTermQuery() throws IOException {
}
}

public void testTermQueryIsExpensive() throws IOException {
checkExpensiveQuery((ft, ctx) -> ft.termQuery(randomAlphaOfLengthBetween(1, 1000), ctx));
}

public void testTermsQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": 1}"))));
Expand All @@ -189,6 +235,10 @@ public void testTermsQuery() throws IOException {
}
}

public void testTermsQueryIsExpensive() throws IOException {
checkExpensiveQuery((ft, ctx) -> ft.termsQuery(randomList(100, () -> randomAlphaOfLengthBetween(1, 1000)), ctx));
}

public void testWildcardQuery() throws IOException {
try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) {
iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": \"aab\"}"))));
Expand All @@ -200,6 +250,10 @@ public void testWildcardQuery() throws IOException {
}
}

public void testWildcardQueryIsExpensive() throws IOException {
checkExpensiveQuery((ft, ctx) -> ft.wildcardQuery(randomAlphaOfLengthBetween(1, 1000), null, ctx));
}

private RuntimeKeywordMappedFieldType build(String code) throws IOException {
Script script = new Script(code);
PainlessPlugin painlessPlugin = new PainlessPlugin();
Expand All @@ -218,9 +272,23 @@ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
}

private QueryShardContext mockContext() {
return mockContext(true);
}

private QueryShardContext mockContext(boolean allowExpensiveQueries) {
MapperService mapperService = mock(MapperService.class);
QueryShardContext context = mock(QueryShardContext.class);
when(context.allowExpensiveQueries()).thenReturn(allowExpensiveQueries);
when(context.lookup()).thenReturn(new SearchLookup(mapperService, mft -> null));
return context;
}

private void checkExpensiveQuery(BiConsumer<RuntimeKeywordMappedFieldType, QueryShardContext> queryBuilder) throws IOException {
RuntimeKeywordMappedFieldType ft = build("value('cat')");
Exception e = expectThrows(ElasticsearchException.class, () -> queryBuilder.accept(ft, mockContext(false)));
assertThat(
e.getMessage(),
equalTo("queries cannot be executed against [script] fields while [search.allow_expensive_queries] is set to [false].")
);
}
}

0 comments on commit 4ba86b1

Please sign in to comment.