From 6d2cab943754062f1ec215f35d133b209553ebd2 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 9 Sep 2020 14:47:24 -0400 Subject: [PATCH] Stop runtime script from emitting too many values (#61938) (#62186) This prevent `keyword` valued runtime scripts from emitting too many values or values that take up too much space. Without this you can put allocate a ton of memory with the script by sticking it into a tight loop. Painless has some protections against this but: 1. I don't want to rely on them out of sheer paranoia 2. They don't really kick in when the script uses callbacks like we do anyway. Relates to #59332 --- .../AbstractLongScriptFieldScript.java | 5 +- .../AbstractScriptFieldScript.java | 28 +++++++- .../BooleanScriptFieldScript.java | 6 +- .../runtimefields/DateScriptFieldScript.java | 12 +++- .../DoubleScriptFieldScript.java | 7 +- .../runtimefields/IpScriptFieldScript.java | 7 +- .../runtimefields/LongScriptFieldScript.java | 6 +- .../StringScriptFieldScript.java | 27 ++++++- .../mapper/ScriptBooleanMappedFieldType.java | 2 +- .../mapper/ScriptDateMappedFieldType.java | 2 +- .../mapper/ScriptDoubleMappedFieldType.java | 2 +- .../mapper/ScriptIpMappedFieldType.java | 2 +- .../mapper/ScriptKeywordMappedFieldType.java | 2 +- .../mapper/ScriptLongMappedFieldType.java | 2 +- .../BooleanScriptFieldScriptTests.java | 37 +++++++++- .../DateScriptFieldScriptTests.java | 43 ++++++++++- .../DoubleScriptFieldScriptTests.java | 41 ++++++++++- .../IpScriptFieldScriptTests.java | 45 +++++++++++- .../LongScriptFieldScriptTests.java | 45 +++++++++++- .../StringScriptFieldScriptTests.java | 72 ++++++++++++++++++- .../ScriptBooleanMappedFieldTypeTests.java | 14 +++- .../ScriptDateMappedFieldTypeTests.java | 16 ++++- .../ScriptDoubleMappedFieldTypeTests.java | 4 +- .../mapper/ScriptIpMappedFieldTypeTests.java | 4 +- .../ScriptKeywordMappedFieldTypeTests.java | 4 +- .../ScriptLongMappedFieldTypeTests.java | 6 +- ...gScriptFieldDistanceFeatureQueryTests.java | 2 +- 27 files changed, 396 insertions(+), 47 deletions(-) diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java index d7b261a4fae..93c6ad382d2 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractLongScriptFieldScript.java @@ -19,8 +19,8 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS private long[] values = new long[1]; private int count; - public AbstractLongScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public AbstractLongScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } /** @@ -50,6 +50,7 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS } protected final void emitValue(long v) { + checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java index 5fe3d296b3a..27d3527666b 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java @@ -17,6 +17,7 @@ import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.SourceLookup; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -27,6 +28,11 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; * {@link AggregationScript} but hopefully with less historical baggage. */ public abstract class AbstractScriptFieldScript { + /** + * The maximum number of values a script should be allowed to emit. + */ + static final int MAX_VALUES = 100; + public static ScriptContext newContext(String name, Class factoryClass) { return new ScriptContext( name + "_script_field", @@ -54,10 +60,12 @@ public abstract class AbstractScriptFieldScript { value -> ((SourceLookup) value).loadSourceIfNeeded() ); + protected final String fieldName; private final Map params; private final LeafSearchLookup leafSearchLookup; - public AbstractScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + public AbstractScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + this.fieldName = fieldName; this.leafSearchLookup = searchLookup.getLeafSearchLookup(ctx); params = new HashMap<>(params); params.put("_source", leafSearchLookup.source()); @@ -94,5 +102,23 @@ public abstract class AbstractScriptFieldScript { return leafSearchLookup.doc(); } + /** + * Check if the we can add another value to the list of values. + * @param currentSize the current size of the list + */ + protected final void checkMaxSize(int currentSize) { + if (currentSize >= MAX_VALUES) { + throw new IllegalArgumentException( + String.format( + Locale.ROOT, + "Runtime field [%s] is emitting [%s] values while the maximum number of values allowed is [%s]", + fieldName, + currentSize + 1, + MAX_VALUES + ) + ); + } + } + public abstract void execute(); } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java index 3e4ef9ce11e..d0e3fd4b038 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScript.java @@ -31,7 +31,7 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup); } public interface LeafFactory { @@ -41,8 +41,8 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript private int trues; private int falses; - public BooleanScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public BooleanScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } /** diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java index 9b9ff47cd68..b169b034f06 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java @@ -31,7 +31,7 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup, DateFormatter formatter); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup, DateFormatter formatter); } public interface LeafFactory { @@ -40,8 +40,14 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip private final DateFormatter formatter; - public DateScriptFieldScript(Map params, SearchLookup searchLookup, DateFormatter formatter, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public DateScriptFieldScript( + String fieldName, + Map params, + SearchLookup searchLookup, + DateFormatter formatter, + LeafReaderContext ctx + ) { + super(fieldName, params, searchLookup, ctx); this.formatter = formatter; } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java index a4fc3254206..840f5395aad 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScript.java @@ -31,7 +31,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup); } public interface LeafFactory { @@ -41,8 +41,8 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript private double[] values = new double[1]; private int count; - public DoubleScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public DoubleScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } /** @@ -72,6 +72,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript } protected final void emitValue(double v) { + checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java index e99ebb4513d..fe20c572476 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScript.java @@ -50,7 +50,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript { public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup); } public interface LeafFactory { @@ -60,8 +60,8 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript { private BytesRef[] values = new BytesRef[1]; private int count; - public IpScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public IpScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } /** @@ -94,6 +94,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript { } protected final void emitValue(String v) { + checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java index e50ccee619f..cf9852563a3 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScript.java @@ -28,15 +28,15 @@ public abstract class LongScriptFieldScript extends AbstractLongScriptFieldScrip public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup); } public interface LeafFactory { LongScriptFieldScript newInstance(LeafReaderContext ctx) throws IOException; } - public LongScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public LongScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } public static class EmitValue { diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java index 7faf8fbffc8..9b89e6714bb 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScript.java @@ -17,9 +17,15 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; public abstract class StringScriptFieldScript extends AbstractScriptFieldScript { + /** + * The maximum number of chars a script should be allowed to emit. + */ + public static final long MAX_CHARS = 1024 * 1024; + public static final ScriptContext CONTEXT = newContext("string_script_field", Factory.class); static List whitelist() { @@ -31,7 +37,7 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript public static final String[] PARAMETERS = {}; public interface Factory extends ScriptFactory { - LeafFactory newFactory(Map params, SearchLookup searchLookup); + LeafFactory newFactory(String fieldName, Map params, SearchLookup searchLookup); } public interface LeafFactory { @@ -39,9 +45,10 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript } private final List results = new ArrayList<>(); + private long chars; - public StringScriptFieldScript(Map params, SearchLookup searchLookup, LeafReaderContext ctx) { - super(params, searchLookup, ctx); + public StringScriptFieldScript(String fieldName, Map params, SearchLookup searchLookup, LeafReaderContext ctx) { + super(fieldName, params, searchLookup, ctx); } /** @@ -52,12 +59,26 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript */ public final List resultsForDoc(int docId) { results.clear(); + chars = 0; setDocument(docId); execute(); return results; } protected final void emitValue(String v) { + checkMaxSize(results.size()); + chars += v.length(); + if (chars > MAX_CHARS) { + throw new IllegalArgumentException( + String.format( + Locale.ROOT, + "Runtime field [%s] is emitting [%s] characters while the maximum number of values allowed is [%s]", + fieldName, + chars, + MAX_CHARS + ) + ); + } results.add(v); } diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java index 8af187f5435..98b58200782 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldType.java @@ -72,7 +72,7 @@ public class ScriptBooleanMappedFieldType extends AbstractScriptMappedFieldType } private BooleanScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { - return scriptFactory.newFactory(script.getParams(), searchLookup); + return scriptFactory.newFactory(name(), script.getParams(), searchLookup); } @Override diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java index 904a35ca3f8..8395508e916 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java @@ -85,7 +85,7 @@ public class ScriptDateMappedFieldType extends AbstractScriptMappedFieldType { } private DateScriptFieldScript.LeafFactory leafFactory(SearchLookup lookup) { - return scriptFactory.newFactory(script.getParams(), lookup, dateTimeFormatter); + return scriptFactory.newFactory(name(), script.getParams(), lookup, dateTimeFormatter); } @Override diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java index e835d45f861..bc898bbfa23 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldType.java @@ -64,7 +64,7 @@ public class ScriptDoubleMappedFieldType extends AbstractScriptMappedFieldType { } private DoubleScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { - return scriptFactory.newFactory(script.getParams(), searchLookup); + return scriptFactory.newFactory(name(), script.getParams(), searchLookup); } @Override diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java index 762c134a0e0..0257fa116c2 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldType.java @@ -80,7 +80,7 @@ public final class ScriptIpMappedFieldType extends AbstractScriptMappedFieldType } private IpScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { - return scriptFactory.newFactory(script.getParams(), searchLookup); + return scriptFactory.newFactory(name(), script.getParams(), searchLookup); } @Override diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java index f41c053f7d5..3341f7c4ed0 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldType.java @@ -68,7 +68,7 @@ public final class ScriptKeywordMappedFieldType extends AbstractScriptMappedFiel } private StringScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { - return scriptFactory.newFactory(script.getParams(), searchLookup); + return scriptFactory.newFactory(name(), script.getParams(), searchLookup); } @Override diff --git a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java index 24ecde240c3..0568ee4e9e7 100644 --- a/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java +++ b/x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldType.java @@ -64,7 +64,7 @@ public class ScriptLongMappedFieldType extends AbstractScriptMappedFieldType { } private LongScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { - return scriptFactory.newFactory(script.getParams(), searchLookup); + return scriptFactory.newFactory(name(), script.getParams(), searchLookup); } @Override diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java index 0f55db05b99..4ed0db9e3ed 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/BooleanScriptFieldScriptTests.java @@ -6,10 +6,22 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.mockito.Mockito.mock; public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final BooleanScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new BooleanScriptFieldScript( + public static final BooleanScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new BooleanScriptFieldScript( + fieldName, params, lookup, ctx @@ -29,4 +41,27 @@ public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES * 1000; i++) { + emitValue(i % 2 == 0); + } + } + }; + // There isn't a limit to the number of values so this won't throw + script.execute(); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java index 768f22347f4..547b2aa9ad5 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScriptTests.java @@ -6,10 +6,24 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.time.DateFormatter; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final DateScriptFieldScript.Factory DUMMY = (params, lookup, formatter) -> ctx -> new DateScriptFieldScript( + public static final DateScriptFieldScript.Factory DUMMY = (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript( + fieldName, params, lookup, formatter, @@ -30,4 +44,31 @@ public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + DateFormatter.forPattern(randomDateFormatterPattern()).withLocale(randomLocale(random())), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) { + emitValue(0); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java index 4cf222c12eb..82b7ae74c23 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/DoubleScriptFieldScriptTests.java @@ -6,10 +6,23 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final DoubleScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new DoubleScriptFieldScript( + public static final DoubleScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new DoubleScriptFieldScript( + fieldName, params, lookup, ctx @@ -29,4 +42,30 @@ public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) { + emitValue(1.0); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java index 15d47c6ce39..c9cccfad1b4 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/IpScriptFieldScriptTests.java @@ -6,10 +6,27 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final IpScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new IpScriptFieldScript(params, lookup, ctx) { + public static final IpScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new IpScriptFieldScript( + fieldName, + params, + lookup, + ctx + ) { @Override public void execute() { emitValue("192.168.0.1"); @@ -25,4 +42,30 @@ public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) { + emitValue("192.168.0.1"); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java index 4dd408e92fe..1df9cc6d4c1 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/LongScriptFieldScriptTests.java @@ -6,10 +6,27 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final LongScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new LongScriptFieldScript(params, lookup, ctx) { + public static final LongScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new LongScriptFieldScript( + fieldName, + params, + lookup, + ctx + ) { @Override public void execute() { emitValue(1); @@ -25,4 +42,30 @@ public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) { + emitValue(0); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java index 7b500f6406e..7f375371bb6 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/StringScriptFieldScriptTests.java @@ -6,10 +6,23 @@ package org.elasticsearch.xpack.runtimefields; +import org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase { - public static final StringScriptFieldScript.Factory DUMMY = (params, lookup) -> ctx -> new StringScriptFieldScript( + public static final StringScriptFieldScript.Factory DUMMY = (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript( + fieldName, params, lookup, ctx @@ -29,4 +42,61 @@ public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractScriptFieldScript.MAX_VALUES; i++) { + emitValue("test"); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } + + public void testTooManyChars() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(org.elasticsearch.common.collect.List.of(new StoredField("_source", new BytesRef("{}")))); + try (DirectoryReader reader = iw.getReader()) { + StringScriptFieldScript script = new StringScriptFieldScript( + "test", + org.elasticsearch.common.collect.Map.of(), + new SearchLookup(mock(MapperService.class), (ft, lookup) -> null, null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + StringBuilder big = new StringBuilder(); + while (big.length() < StringScriptFieldScript.MAX_CHARS / 4) { + big.append("test"); + } + String bigString = big.toString(); + for (int i = 0; i <= 4; i++) { + emitValue(bigString); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [test] is emitting [1310720] characters while the maximum number of values allowed is [1048576]") + ); + } + } + } } diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java index 2d67638f400..f8b765b7874 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptBooleanMappedFieldTypeTests.java @@ -458,7 +458,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp private BooleanScriptFieldScript.Factory factory(String code) { switch (code) { case "read_foo": - return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript( + fieldName, + params, + lookup, + ctx + ) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -467,7 +472,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp } }; case "xor_param": - return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript( + fieldName, + params, + lookup, + ctx + ) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java index b2896c343ca..757448e6728 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java @@ -504,7 +504,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF private DateScriptFieldScript.Factory factory(String code) { switch (code) { case "read_timestamp": - return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) { + return (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript( + fieldName, + params, + lookup, + formatter, + ctx + ) { @Override public void execute() { for (Object timestamp : (List) getSource().get("timestamp")) { @@ -514,7 +520,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF } }; case "add_days": - return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) { + return (fieldName, params, lookup, formatter) -> ctx -> new DateScriptFieldScript( + fieldName, + params, + lookup, + formatter, + ctx + ) { @Override public void execute() { for (Object timestamp : (List) getSource().get("timestamp")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java index 05d33c4d54a..6626562fdbd 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDoubleMappedFieldTypeTests.java @@ -295,7 +295,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe private DoubleScriptFieldScript.Factory factory(String code) { switch (code) { case "read_foo": - return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -304,7 +304,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe } }; case "add_param": - return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java index a52e6761bc1..2ca12fb6d00 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptIpMappedFieldTypeTests.java @@ -338,7 +338,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT private IpScriptFieldScript.Factory factory(String code) { switch (code) { case "read_foo": - return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -347,7 +347,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT } }; case "append_param": - return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java index 5b857493907..71cd4b1d0bc 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptKeywordMappedFieldTypeTests.java @@ -379,7 +379,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField private StringScriptFieldScript.Factory factory(String code) { switch (code) { case "read_foo": - return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -388,7 +388,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField } }; case "append_param": - return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java index 0599bd3a6ad..33c9c1a98ec 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptLongMappedFieldTypeTests.java @@ -324,7 +324,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF private LongScriptFieldScript.Factory factory(String code) { switch (code) { case "read_foo": - return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -333,7 +333,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF } }; case "add_param": - return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object foo : (List) getSource().get("foo")) { @@ -344,7 +344,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF case "millis_ago": // Painless actually call System.currentTimeMillis. We could mock the time but this works fine too. long now = System.currentTimeMillis(); - return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) { + return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) { @Override public void execute() { for (Object timestamp : (List) getSource().get("timestamp")) { diff --git a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java index 8cbe71595fe..bc1c51adee6 100644 --- a/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java +++ b/x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java @@ -91,7 +91,7 @@ public class LongScriptFieldDistanceFeatureQueryTests extends AbstractScriptFiel try (DirectoryReader reader = iw.getReader()) { IndexSearcher searcher = newSearcher(reader); CheckedFunction leafFactory = - ctx -> new DateScriptFieldScript(Collections.emptyMap(), new SearchLookup(null, null, null), null, ctx) { + ctx -> new DateScriptFieldScript("test", Collections.emptyMap(), new SearchLookup(null, null, null), null, ctx) { @Override public void execute() { for (Object timestamp : (List) getSource().get("timestamp")) {