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
This commit is contained in:
Nik Everett 2020-09-09 14:47:24 -04:00 committed by GitHub
parent 1eb4595a29
commit 6d2cab9437
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 396 additions and 47 deletions

View File

@ -19,8 +19,8 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS
private long[] values = new long[1]; private long[] values = new long[1];
private int count; private int count;
public AbstractLongScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public AbstractLongScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
/** /**
@ -50,6 +50,7 @@ public abstract class AbstractLongScriptFieldScript extends AbstractScriptFieldS
} }
protected final void emitValue(long v) { protected final void emitValue(long v) {
checkMaxSize(count);
if (values.length < count + 1) { if (values.length < count + 1) {
values = ArrayUtil.grow(values, count + 1); values = ArrayUtil.grow(values, count + 1);
} }

View File

@ -17,6 +17,7 @@ import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.search.lookup.SourceLookup; import org.elasticsearch.search.lookup.SourceLookup;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.function.Function; 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. * {@link AggregationScript} but hopefully with less historical baggage.
*/ */
public abstract class AbstractScriptFieldScript { public abstract class AbstractScriptFieldScript {
/**
* The maximum number of values a script should be allowed to emit.
*/
static final int MAX_VALUES = 100;
public static <F> ScriptContext<F> newContext(String name, Class<F> factoryClass) { public static <F> ScriptContext<F> newContext(String name, Class<F> factoryClass) {
return new ScriptContext<F>( return new ScriptContext<F>(
name + "_script_field", name + "_script_field",
@ -54,10 +60,12 @@ public abstract class AbstractScriptFieldScript {
value -> ((SourceLookup) value).loadSourceIfNeeded() value -> ((SourceLookup) value).loadSourceIfNeeded()
); );
protected final String fieldName;
private final Map<String, Object> params; private final Map<String, Object> params;
private final LeafSearchLookup leafSearchLookup; private final LeafSearchLookup leafSearchLookup;
public AbstractScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public AbstractScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
this.fieldName = fieldName;
this.leafSearchLookup = searchLookup.getLeafSearchLookup(ctx); this.leafSearchLookup = searchLookup.getLeafSearchLookup(ctx);
params = new HashMap<>(params); params = new HashMap<>(params);
params.put("_source", leafSearchLookup.source()); params.put("_source", leafSearchLookup.source());
@ -94,5 +102,23 @@ public abstract class AbstractScriptFieldScript {
return leafSearchLookup.doc(); 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(); public abstract void execute();
} }

View File

@ -31,7 +31,7 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
} }
public interface LeafFactory { public interface LeafFactory {
@ -41,8 +41,8 @@ public abstract class BooleanScriptFieldScript extends AbstractScriptFieldScript
private int trues; private int trues;
private int falses; private int falses;
public BooleanScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public BooleanScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
/** /**

View File

@ -31,7 +31,7 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter);
} }
public interface LeafFactory { public interface LeafFactory {
@ -40,8 +40,14 @@ public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScrip
private final DateFormatter formatter; private final DateFormatter formatter;
public DateScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter, LeafReaderContext ctx) { public DateScriptFieldScript(
super(params, searchLookup, ctx); String fieldName,
Map<String, Object> params,
SearchLookup searchLookup,
DateFormatter formatter,
LeafReaderContext ctx
) {
super(fieldName, params, searchLookup, ctx);
this.formatter = formatter; this.formatter = formatter;
} }

View File

@ -31,7 +31,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
} }
public interface LeafFactory { public interface LeafFactory {
@ -41,8 +41,8 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
private double[] values = new double[1]; private double[] values = new double[1];
private int count; private int count;
public DoubleScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public DoubleScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
/** /**
@ -72,6 +72,7 @@ public abstract class DoubleScriptFieldScript extends AbstractScriptFieldScript
} }
protected final void emitValue(double v) { protected final void emitValue(double v) {
checkMaxSize(count);
if (values.length < count + 1) { if (values.length < count + 1) {
values = ArrayUtil.grow(values, count + 1); values = ArrayUtil.grow(values, count + 1);
} }

View File

@ -50,7 +50,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
} }
public interface LeafFactory { public interface LeafFactory {
@ -60,8 +60,8 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
private BytesRef[] values = new BytesRef[1]; private BytesRef[] values = new BytesRef[1];
private int count; private int count;
public IpScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public IpScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
/** /**
@ -94,6 +94,7 @@ public abstract class IpScriptFieldScript extends AbstractScriptFieldScript {
} }
protected final void emitValue(String v) { protected final void emitValue(String v) {
checkMaxSize(count);
if (values.length < count + 1) { if (values.length < count + 1) {
values = ArrayUtil.grow(values, count + 1); values = ArrayUtil.grow(values, count + 1);
} }

View File

@ -28,15 +28,15 @@ public abstract class LongScriptFieldScript extends AbstractLongScriptFieldScrip
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
} }
public interface LeafFactory { public interface LeafFactory {
LongScriptFieldScript newInstance(LeafReaderContext ctx) throws IOException; LongScriptFieldScript newInstance(LeafReaderContext ctx) throws IOException;
} }
public LongScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public LongScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
public static class EmitValue { public static class EmitValue {

View File

@ -17,9 +17,15 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
public abstract class StringScriptFieldScript extends AbstractScriptFieldScript { 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<Factory> CONTEXT = newContext("string_script_field", Factory.class); public static final ScriptContext<Factory> CONTEXT = newContext("string_script_field", Factory.class);
static List<Whitelist> whitelist() { static List<Whitelist> whitelist() {
@ -31,7 +37,7 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
public static final String[] PARAMETERS = {}; public static final String[] PARAMETERS = {};
public interface Factory extends ScriptFactory { public interface Factory extends ScriptFactory {
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup); LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup);
} }
public interface LeafFactory { public interface LeafFactory {
@ -39,9 +45,10 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
} }
private final List<String> results = new ArrayList<>(); private final List<String> results = new ArrayList<>();
private long chars;
public StringScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) { public StringScriptFieldScript(String fieldName, Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
super(params, searchLookup, ctx); super(fieldName, params, searchLookup, ctx);
} }
/** /**
@ -52,12 +59,26 @@ public abstract class StringScriptFieldScript extends AbstractScriptFieldScript
*/ */
public final List<String> resultsForDoc(int docId) { public final List<String> resultsForDoc(int docId) {
results.clear(); results.clear();
chars = 0;
setDocument(docId); setDocument(docId);
execute(); execute();
return results; return results;
} }
protected final void emitValue(String v) { 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); results.add(v);
} }

View File

@ -72,7 +72,7 @@ public class ScriptBooleanMappedFieldType extends AbstractScriptMappedFieldType
} }
private BooleanScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { private BooleanScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
return scriptFactory.newFactory(script.getParams(), searchLookup); return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
} }
@Override @Override

View File

@ -85,7 +85,7 @@ public class ScriptDateMappedFieldType extends AbstractScriptMappedFieldType {
} }
private DateScriptFieldScript.LeafFactory leafFactory(SearchLookup lookup) { private DateScriptFieldScript.LeafFactory leafFactory(SearchLookup lookup) {
return scriptFactory.newFactory(script.getParams(), lookup, dateTimeFormatter); return scriptFactory.newFactory(name(), script.getParams(), lookup, dateTimeFormatter);
} }
@Override @Override

View File

@ -64,7 +64,7 @@ public class ScriptDoubleMappedFieldType extends AbstractScriptMappedFieldType {
} }
private DoubleScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { private DoubleScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
return scriptFactory.newFactory(script.getParams(), searchLookup); return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
} }
@Override @Override

View File

@ -80,7 +80,7 @@ public final class ScriptIpMappedFieldType extends AbstractScriptMappedFieldType
} }
private IpScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { private IpScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
return scriptFactory.newFactory(script.getParams(), searchLookup); return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
} }
@Override @Override

View File

@ -68,7 +68,7 @@ public final class ScriptKeywordMappedFieldType extends AbstractScriptMappedFiel
} }
private StringScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { private StringScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
return scriptFactory.newFactory(script.getParams(), searchLookup); return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
} }
@Override @Override

View File

@ -64,7 +64,7 @@ public class ScriptLongMappedFieldType extends AbstractScriptMappedFieldType {
} }
private LongScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) { private LongScriptFieldScript.LeafFactory leafFactory(SearchLookup searchLookup) {
return scriptFactory.newFactory(script.getParams(), searchLookup); return scriptFactory.newFactory(name(), script.getParams(), searchLookup);
} }
@Override @Override

View File

@ -6,10 +6,22 @@
package org.elasticsearch.xpack.runtimefields; 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.script.ScriptContext;
import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException;
import static org.mockito.Mockito.mock;
public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase<BooleanScriptFieldScript.Factory> { public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase<BooleanScriptFieldScript.Factory> {
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, params,
lookup, lookup,
ctx ctx
@ -29,4 +41,27 @@ public class BooleanScriptFieldScriptTests extends ScriptFieldScriptTestCase<Boo
protected BooleanScriptFieldScript.Factory dummyScript() { protected BooleanScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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()) {
BooleanScriptFieldScript script = new BooleanScriptFieldScript(
"test",
org.elasticsearch.common.collect.Map.of(),
new SearchLookup(mock(MapperService.class), (ft, lookup) -> 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();
}
}
}
} }

View File

@ -6,10 +6,24 @@
package org.elasticsearch.xpack.runtimefields; 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.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<DateScriptFieldScript.Factory> { public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase<DateScriptFieldScript.Factory> {
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, params,
lookup, lookup,
formatter, formatter,
@ -30,4 +44,31 @@ public class DateScriptFieldScriptTests extends ScriptFieldScriptTestCase<DateSc
protected DateScriptFieldScript.Factory dummyScript() { protected DateScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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()) {
DateScriptFieldScript script = new DateScriptFieldScript(
"test",
org.elasticsearch.common.collect.Map.of(),
new SearchLookup(mock(MapperService.class), (ft, lookup) -> 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]")
);
}
}
}
} }

View File

@ -6,10 +6,23 @@
package org.elasticsearch.xpack.runtimefields; 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.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<DoubleScriptFieldScript.Factory> { public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase<DoubleScriptFieldScript.Factory> {
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, params,
lookup, lookup,
ctx ctx
@ -29,4 +42,30 @@ public class DoubleScriptFieldScriptTests extends ScriptFieldScriptTestCase<Doub
protected DoubleScriptFieldScript.Factory dummyScript() { protected DoubleScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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()) {
DoubleScriptFieldScript script = new DoubleScriptFieldScript(
"test",
org.elasticsearch.common.collect.Map.of(),
new SearchLookup(mock(MapperService.class), (ft, lookup) -> 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]")
);
}
}
}
} }

View File

@ -6,10 +6,27 @@
package org.elasticsearch.xpack.runtimefields; 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.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<IpScriptFieldScript.Factory> { public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase<IpScriptFieldScript.Factory> {
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 @Override
public void execute() { public void execute() {
emitValue("192.168.0.1"); emitValue("192.168.0.1");
@ -25,4 +42,30 @@ public class IpScriptFieldScriptTests extends ScriptFieldScriptTestCase<IpScript
protected IpScriptFieldScript.Factory dummyScript() { protected IpScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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()) {
IpScriptFieldScript script = new IpScriptFieldScript(
"test",
org.elasticsearch.common.collect.Map.of(),
new SearchLookup(mock(MapperService.class), (ft, lookup) -> 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]")
);
}
}
}
} }

View File

@ -6,10 +6,27 @@
package org.elasticsearch.xpack.runtimefields; 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.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<LongScriptFieldScript.Factory> { public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase<LongScriptFieldScript.Factory> {
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 @Override
public void execute() { public void execute() {
emitValue(1); emitValue(1);
@ -25,4 +42,30 @@ public class LongScriptFieldScriptTests extends ScriptFieldScriptTestCase<LongSc
protected LongScriptFieldScript.Factory dummyScript() { protected LongScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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()) {
LongScriptFieldScript script = new LongScriptFieldScript(
"test",
org.elasticsearch.common.collect.Map.of(),
new SearchLookup(mock(MapperService.class), (ft, lookup) -> 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]")
);
}
}
}
} }

View File

@ -6,10 +6,23 @@
package org.elasticsearch.xpack.runtimefields; 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.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<StringScriptFieldScript.Factory> { public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase<StringScriptFieldScript.Factory> {
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, params,
lookup, lookup,
ctx ctx
@ -29,4 +42,61 @@ public class StringScriptFieldScriptTests extends ScriptFieldScriptTestCase<Stri
protected StringScriptFieldScript.Factory dummyScript() { protected StringScriptFieldScript.Factory dummyScript() {
return DUMMY; return DUMMY;
} }
public void testTooManyValues() 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() {
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]")
);
}
}
}
} }

View File

@ -458,7 +458,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp
private BooleanScriptFieldScript.Factory factory(String code) { private BooleanScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_foo": case "read_foo":
return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript(
fieldName,
params,
lookup,
ctx
) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -467,7 +472,12 @@ public class ScriptBooleanMappedFieldTypeTests extends AbstractNonTextScriptMapp
} }
}; };
case "xor_param": case "xor_param":
return (params, lookup) -> (ctx) -> new BooleanScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new BooleanScriptFieldScript(
fieldName,
params,
lookup,
ctx
) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {

View File

@ -504,7 +504,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF
private DateScriptFieldScript.Factory factory(String code) { private DateScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_timestamp": 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 @Override
public void execute() { public void execute() {
for (Object timestamp : (List<?>) getSource().get("timestamp")) { for (Object timestamp : (List<?>) getSource().get("timestamp")) {
@ -514,7 +520,13 @@ public class ScriptDateMappedFieldTypeTests extends AbstractNonTextScriptMappedF
} }
}; };
case "add_days": 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 @Override
public void execute() { public void execute() {
for (Object timestamp : (List<?>) getSource().get("timestamp")) { for (Object timestamp : (List<?>) getSource().get("timestamp")) {

View File

@ -295,7 +295,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe
private DoubleScriptFieldScript.Factory factory(String code) { private DoubleScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_foo": case "read_foo":
return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -304,7 +304,7 @@ public class ScriptDoubleMappedFieldTypeTests extends AbstractNonTextScriptMappe
} }
}; };
case "add_param": case "add_param":
return (params, lookup) -> (ctx) -> new DoubleScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new DoubleScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {

View File

@ -338,7 +338,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT
private IpScriptFieldScript.Factory factory(String code) { private IpScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_foo": case "read_foo":
return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -347,7 +347,7 @@ public class ScriptIpMappedFieldTypeTests extends AbstractScriptMappedFieldTypeT
} }
}; };
case "append_param": case "append_param":
return (params, lookup) -> (ctx) -> new IpScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new IpScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {

View File

@ -379,7 +379,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField
private StringScriptFieldScript.Factory factory(String code) { private StringScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_foo": case "read_foo":
return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -388,7 +388,7 @@ public class ScriptKeywordMappedFieldTypeTests extends AbstractScriptMappedField
} }
}; };
case "append_param": case "append_param":
return (params, lookup) -> (ctx) -> new StringScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> ctx -> new StringScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {

View File

@ -324,7 +324,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
private LongScriptFieldScript.Factory factory(String code) { private LongScriptFieldScript.Factory factory(String code) {
switch (code) { switch (code) {
case "read_foo": case "read_foo":
return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -333,7 +333,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
} }
}; };
case "add_param": case "add_param":
return (params, lookup) -> (ctx) -> new LongScriptFieldScript(params, lookup, ctx) { return (fieldName, params, lookup) -> (ctx) -> new LongScriptFieldScript(fieldName, params, lookup, ctx) {
@Override @Override
public void execute() { public void execute() {
for (Object foo : (List<?>) getSource().get("foo")) { for (Object foo : (List<?>) getSource().get("foo")) {
@ -344,7 +344,7 @@ public class ScriptLongMappedFieldTypeTests extends AbstractNonTextScriptMappedF
case "millis_ago": case "millis_ago":
// Painless actually call System.currentTimeMillis. We could mock the time but this works fine too. // Painless actually call System.currentTimeMillis. We could mock the time but this works fine too.
long now = System.currentTimeMillis(); 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 @Override
public void execute() { public void execute() {
for (Object timestamp : (List<?>) getSource().get("timestamp")) { for (Object timestamp : (List<?>) getSource().get("timestamp")) {

View File

@ -91,7 +91,7 @@ public class LongScriptFieldDistanceFeatureQueryTests extends AbstractScriptFiel
try (DirectoryReader reader = iw.getReader()) { try (DirectoryReader reader = iw.getReader()) {
IndexSearcher searcher = newSearcher(reader); IndexSearcher searcher = newSearcher(reader);
CheckedFunction<LeafReaderContext, AbstractLongScriptFieldScript, IOException> leafFactory = CheckedFunction<LeafReaderContext, AbstractLongScriptFieldScript, IOException> 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 @Override
public void execute() { public void execute() {
for (Object timestamp : (List<?>) getSource().get("timestamp")) { for (Object timestamp : (List<?>) getSource().get("timestamp")) {