Scripting: remove groovy sandbox

Groovy sandboxing was disabled by default from 1.4.3 on though since we found out that it could be worked around, so it makes little sense to keep it and maintain it.

Closes #10156
Closes #10480
This commit is contained in:
javanna 2015-04-08 13:34:49 +02:00 committed by Luca Cavanna
parent 371bc5a6b3
commit c914134355
12 changed files with 15 additions and 532 deletions

View File

@ -345,6 +345,11 @@ Deprecated script parameters `id`, `file`, and `scriptField` have been removed
from all scriptable APIs. `script_id`, `script_file` and `script` should be used
in their place.
=== Groovy scripts sandbox
The groovy sandbox and related settings have been removed. Groovy is now a non
sandboxed scripting language, without any option to turn the sandbox on.
=== Plugins making use of scripts
Plugins that make use of scripts must register their own script context through

View File

@ -11,26 +11,11 @@ The scripting module uses by default http://groovy.codehaus.org/[groovy]
scripting language with some extensions. Groovy is used since it is extremely
fast and very simple to use.
.Groovy dynamic scripting disabled by default from v1.4.3
.Groovy dynamic scripting off by default from v1.4.3
[IMPORTANT]
===================================================
Elasticsearch versions 1.3.0-1.3.7 and 1.4.0-1.4.2 have a vulnerability in the
Groovy scripting engine. The vulnerability allows an attacker to construct
Groovy scripts that escape the sandbox and execute shell commands as the user
running the Elasticsearch Java VM.
If you are running a vulnerable version of Elasticsearch, you should either
upgrade to at least v1.3.8 or v1.4.3, or disable dynamic Groovy scripts by
adding this setting to the `config/elasticsearch.yml` file in all nodes in the
cluster:
[source,yaml]
-----------------------------------
script.groovy.sandbox.enabled: false
-----------------------------------
This will turn off the Groovy sandbox, thus preventing dynamic Groovy scripts
Groovy dynamic scripting is off by default, preventing dynamic Groovy scripts
from being accepted as part of a request or retrieved from the special
`.scripts` index. You will still be able to use Groovy scripts stored in files
in the `config/scripts/` directory on every node.
@ -351,39 +336,6 @@ The default scripting language (assuming no `lang` parameter is provided) is
`groovy`. In order to change it, set the `script.default_lang` to the
appropriate language.
[float]
=== Groovy Sandboxing
Elasticsearch sandboxes Groovy scripts that are compiled and executed in order
to ensure they don't perform unwanted actions. There are a number of options
that can be used for configuring this sandbox:
`script.groovy.sandbox.receiver_whitelist`::
Comma-separated list of string classes for objects that may have methods
invoked.
`script.groovy.sandbox.package_whitelist`::
Comma-separated list of packages under which new objects may be constructed.
`script.groovy.sandbox.class_whitelist`::
Comma-separated list of classes that are allowed to be constructed.
`script.groovy.sandbox.method_blacklist`::
Comma-separated list of methods that are never allowed to be invoked,
regardless of target object.
`script.groovy.sandbox.enabled`::
Flag to enable the sandbox (defaults to `false` meaning the sandbox is
disabled).
When specifying whitelist or blacklist settings for the groovy sandbox, all
options replace the current whitelist, they are not additive.
[float]
=== Automatic Script Reloading

View File

@ -33,7 +33,6 @@ import org.elasticsearch.indices.cache.filter.IndicesFilterCache;
import org.elasticsearch.indices.recovery.RecoverySettings;
import org.elasticsearch.indices.store.IndicesStore;
import org.elasticsearch.indices.ttl.IndicesTTLService;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.threadpool.ThreadPool;
/**
@ -101,7 +100,6 @@ public class ClusterDynamicSettingsModule extends AbstractModule {
clusterDynamicSettings.addDynamicSetting(HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_OVERHEAD_SETTING, Validator.NON_NEGATIVE_DOUBLE);
clusterDynamicSettings.addDynamicSetting(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_LIMIT_SETTING, Validator.MEMORY_SIZE);
clusterDynamicSettings.addDynamicSetting(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_OVERHEAD_SETTING, Validator.NON_NEGATIVE_DOUBLE);
clusterDynamicSettings.addDynamicSetting(GroovyScriptEngineService.GROOVY_SCRIPT_BLACKLIST_PATCH);
}
public void addDynamicSettings(String... settings) {

View File

@ -58,7 +58,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.query.TemplateQueryParser;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.watcher.FileChangesListener;
@ -100,7 +99,6 @@ public class ScriptService extends AbstractComponent implements Closeable {
private final Cache<CacheKey, CompiledScript> cache;
private final Path scriptsDirectory;
private final FileWatcher fileWatcher;
private final ScriptModes scriptModes;
private final ScriptContextRegistry scriptContextRegistry;
@ -114,7 +112,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
@Inject
public ScriptService(Settings settings, Environment env, Set<ScriptEngineService> scriptEngines,
ResourceWatcherService resourceWatcherService, NodeSettingsService nodeSettingsService, ScriptContextRegistry scriptContextRegistry) throws IOException {
ResourceWatcherService resourceWatcherService, ScriptContextRegistry scriptContextRegistry) throws IOException {
super(settings);
if (Strings.hasLength(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING))) {
@ -159,7 +157,7 @@ public class ScriptService extends AbstractComponent implements Closeable {
if (logger.isTraceEnabled()) {
logger.trace("Using scripts directory [{}] ", scriptsDirectory);
}
this.fileWatcher = new FileWatcher(scriptsDirectory);
FileWatcher fileWatcher = new FileWatcher(scriptsDirectory);
fileWatcher.addListener(new ScriptChangesListener());
if (settings.getAsBoolean(SCRIPT_AUTO_RELOAD_ENABLED_SETTING, true)) {
@ -169,7 +167,6 @@ public class ScriptService extends AbstractComponent implements Closeable {
// automatic reload is disable just load scripts once
fileWatcher.init();
}
nodeSettingsService.addListener(new ApplySettings());
}
//This isn't set in the ctor because doing so creates a guice circular
@ -183,21 +180,6 @@ public class ScriptService extends AbstractComponent implements Closeable {
IOUtils.close(scriptEngines);
}
/**
* Clear both the in memory and on disk compiled script caches. Files on
* disk will be treated as if they are new and recompiled.
* */
public void clearCache() {
logger.debug("clearing script cache");
// Clear the in-memory script caches
this.cache.invalidateAll();
this.cache.cleanUp();
// Clear the cache of on-disk scripts
this.staticCache.clear();
// Clear the file watcher's state so it re-compiles on-disk scripts
this.fileWatcher.clearState();
}
private ScriptEngineService getScriptEngineServiceForLang(String lang) {
ScriptEngineService scriptEngineService = scriptEnginesByLang.get(lang);
if (scriptEngineService == null) {
@ -642,23 +624,4 @@ public class ScriptService extends AbstractComponent implements Closeable {
}
}
}
private class ApplySettings implements NodeSettingsService.Listener {
@Override
public void onRefreshSettings(Settings settings) {
GroovyScriptEngineService engine = (GroovyScriptEngineService) ScriptService.this.scriptEnginesByLang.get(GroovyScriptEngineService.NAME);
if (engine != null) {
String[] patches = settings.getAsArray(GroovyScriptEngineService.GROOVY_SCRIPT_BLACKLIST_PATCH, Strings.EMPTY_ARRAY);
boolean blacklistChanged = engine.addToBlacklist(patches);
if (blacklistChanged) {
logger.info("adding {} to [{}], new blacklisted methods: {}", patches,
GroovyScriptEngineService.GROOVY_SCRIPT_BLACKLIST_PATCH, engine.blacklistAdditions());
engine.reloadConfig();
// Because the GroovyScriptEngineService knows nothing about the
// cache, we need to clear it here if the setting changes
ScriptService.this.clearCache();
}
}
}
}
}

View File

@ -1,170 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.script.groovy;
import com.google.common.collect.ImmutableSet;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.control.customizers.SecureASTCustomizer;
import org.elasticsearch.common.settings.Settings;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
/**
* Class used to determine whether a Groovy expression should be allowed.
* During compilation, every expression is passed to the
* <code>isAuthorized</code> method, which returns true to allow that method
* and false to block it. Includes all of the sandbox-related whitelist and
* blacklist options.
*/
public class GroovySandboxExpressionChecker implements SecureASTCustomizer.ExpressionChecker {
public static String GROOVY_SANDBOX_METHOD_BLACKLIST = "script.groovy.sandbox.method_blacklist";
public static String GROOVY_SANDBOX_PACKAGE_WHITELIST = "script.groovy.sandbox.package_whitelist";
public static String GROOVY_SANDBOX_CLASS_WHITELIST = "script.groovy.sandbox.class_whitelist";
public static String GROOVY_SCRIPT_SANDBOX_RECEIVER_WHITELIST = "script.groovy.sandbox.receiver_whitelist";
private final Set<String> methodBlacklist;
private final Set<String> additionalMethodBlacklist;
private final Set<String> packageWhitelist;
private final Set<String> classWhitelist;
public GroovySandboxExpressionChecker(Settings settings, Set<String> blacklistAdditions) {
this.methodBlacklist = ImmutableSet.copyOf(settings.getAsArray(GROOVY_SANDBOX_METHOD_BLACKLIST, defaultMethodBlacklist, true));
this.additionalMethodBlacklist = ImmutableSet.copyOf(blacklistAdditions);
this.packageWhitelist = ImmutableSet.copyOf(settings.getAsArray(GROOVY_SANDBOX_PACKAGE_WHITELIST, defaultPackageWhitelist, true));
this.classWhitelist = ImmutableSet.copyOf(settings.getAsArray(GROOVY_SANDBOX_CLASS_WHITELIST, defaultClassConstructionWhitelist, true));
}
// Never allow calling these methods, regardless of the object type
public static String[] defaultMethodBlacklist = new String[]{
"getClass",
"class",
"forName",
"wait",
"notify",
"notifyAll",
"invokeMethod",
"finalize"
};
// Only instances of these classes in these packages can be instantiated
public static String[] defaultPackageWhitelist = new String[] {"java.util", "java.lang", "org.joda.time"};
// Classes that are allowed to be constructed
public static String[] defaultClassConstructionWhitelist = new String[]{
java.util.Date.class.getName(),
java.util.Map.class.getName(),
java.util.List.class.getName(),
java.util.Set.class.getName(),
java.util.ArrayList.class.getName(),
java.util.Arrays.class.getName(),
java.util.HashMap.class.getName(),
java.util.HashSet.class.getName(),
java.util.UUID.class.getName(),
java.math.BigDecimal.class.getName(),
org.joda.time.DateTime.class.getName(),
org.joda.time.DateTimeZone.class.getName()
};
// Default whitelisted receiver classes for the Groovy sandbox
private final static String[] defaultReceiverWhitelist = new String [] {
groovy.util.GroovyCollections.class.getName(),
java.lang.Math.class.getName(),
java.lang.Integer.class.getName(), "[I", "[[I", "[[[I",
java.lang.Float.class.getName(), "[F", "[[F", "[[[F",
java.lang.Double.class.getName(), "[D", "[[D", "[[[D",
java.lang.Long.class.getName(), "[J", "[[J", "[[[J",
java.lang.Short.class.getName(), "[S", "[[S", "[[[S",
java.lang.Character.class.getName(), "[C", "[[C", "[[[C",
java.lang.Byte.class.getName(), "[B", "[[B", "[[[B",
java.lang.Boolean.class.getName(), "[Z", "[[Z", "[[[Z",
java.math.BigDecimal.class.getName(),
java.util.Arrays.class.getName(),
java.util.Date.class.getName(),
java.util.List.class.getName(),
java.util.Map.class.getName(),
java.util.Set.class.getName(),
java.lang.Object.class.getName(),
org.joda.time.DateTime.class.getName(),
org.joda.time.DateTimeUtils.class.getName(),
org.joda.time.DateTimeZone.class.getName(),
org.joda.time.Instant.class.getName()
};
/**
* Checks whether the expression to be compiled is allowed
*/
@Override
public boolean isAuthorized(Expression expression) {
if (expression instanceof MethodPointerExpression) {
return false;
} else if (expression instanceof MethodCallExpression) {
MethodCallExpression mce = (MethodCallExpression) expression;
String methodName = mce.getMethodAsString();
if (methodBlacklist.contains(methodName)) {
return false;
} else if (additionalMethodBlacklist.contains(methodName)) {
return false;
} else if (methodName == null && mce.getMethod() instanceof GStringExpression) {
// We do not allow GStrings for method invocation, they are a security risk
return false;
}
} else if (expression instanceof ConstructorCallExpression) {
ConstructorCallExpression cce = (ConstructorCallExpression) expression;
ClassNode type = cce.getType();
if (!packageWhitelist.contains(type.getPackageName())) {
return false;
}
if (!classWhitelist.contains(type.getName())) {
return false;
}
}
return true;
}
/**
* Returns a customized ASTCustomizer that includes the whitelists and
* expression checker.
*/
public static SecureASTCustomizer getSecureASTCustomizer(Settings settings, Set<String> blacklistAdditions) {
SecureASTCustomizer scz = new SecureASTCustomizer();
// Closures are allowed
scz.setClosuresAllowed(true);
// But defining methods is not
scz.setMethodDefinitionAllowed(false);
// Only allow the imports that we explicitly call out
List<String> importWhitelist = new ArrayList<>();
importWhitelist.addAll(ImmutableSet.copyOf(GroovySandboxExpressionChecker.defaultClassConstructionWhitelist));
scz.setImportsWhitelist(importWhitelist);
// Package definitions are not allowed
scz.setPackageAllowed(false);
// White-listed receivers of method calls
String[] receiverWhitelist = settings.getAsArray(GROOVY_SCRIPT_SANDBOX_RECEIVER_WHITELIST, defaultReceiverWhitelist, true);
scz.setReceiversWhiteList(newArrayList(receiverWhitelist));
// Add the customized expression checker for finer-grained checking
scz.addExpressionCheckers(new GroovySandboxExpressionChecker(settings, blacklistAdditions));
return scz;
}
}

View File

@ -22,9 +22,6 @@ package org.elasticsearch.script.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.Script;
import com.google.common.collect.ImmutableSet;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Scorer;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
@ -40,28 +37,18 @@ import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.ScoreAccessor;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.script.*;
import org.elasticsearch.search.lookup.LeafSearchLookup;
import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
/**
@ -70,47 +57,17 @@ import java.util.concurrent.atomic.AtomicLong;
public class GroovyScriptEngineService extends AbstractComponent implements ScriptEngineService {
public static final String NAME = "groovy";
public static String GROOVY_SCRIPT_SANDBOX_ENABLED = "script.groovy.sandbox.enabled";
public static String GROOVY_SCRIPT_BLACKLIST_PATCH = "script.groovy.sandbox.method_blacklist_patch";
private final AtomicLong counter = new AtomicLong();
private final boolean sandboxed;
private volatile GroovyClassLoader loader;
private volatile Set<String> blacklistAdditions;
private final GroovyClassLoader loader;
@Inject
public GroovyScriptEngineService(Settings settings) {
super(settings);
this.sandboxed = settings.getAsBoolean(GROOVY_SCRIPT_SANDBOX_ENABLED, false);
this.blacklistAdditions = ImmutableSet.copyOf(settings.getAsArray(GROOVY_SCRIPT_BLACKLIST_PATCH, Strings.EMPTY_ARRAY));
reloadConfig();
}
public Set<String> blacklistAdditions() {
return this.blacklistAdditions;
}
/**
* Appends the additional blacklisted methods to the current blacklist,
* returns true if the black list has changed
*/
public boolean addToBlacklist(String... additions) {
Set<String> newBlackList = new HashSet<>(blacklistAdditions);
Collections.addAll(newBlackList, additions);
boolean changed = this.blacklistAdditions.equals(newBlackList) == false;
this.blacklistAdditions = ImmutableSet.copyOf(newBlackList);
return changed;
}
public void reloadConfig() {
ImportCustomizer imports = new ImportCustomizer();
imports.addStarImports("org.joda.time");
imports.addStaticStars("java.lang.Math");
CompilerConfiguration config = new CompilerConfiguration();
config.addCompilationCustomizers(imports);
if (this.sandboxed) {
config.addCompilationCustomizers(GroovySandboxExpressionChecker.getSecureASTCustomizer(settings, this.blacklistAdditions));
}
// Add BigDecimal -> Double transformer
config.addCompilationCustomizers(new GroovyBigDecimalTransformer(CompilePhase.CONVERSION));
this.loader = new GroovyClassLoader(settings.getClassLoader(), config);
@ -148,7 +105,7 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri
@Override
public boolean sandboxed() {
return this.sandboxed;
return false;
}
@Override
@ -360,5 +317,4 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri
return super.transform(newExpr);
}
}
}

View File

@ -1,159 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.script;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.CoreMatchers.equalTo;
/**
* Tests for the Groovy scripting sandbox
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST, numDataNodes = 0)
public class GroovySandboxScriptTests extends ElasticsearchIntegrationTest {
@Test
public void testSandboxedGroovyScript() throws Exception {
int nodes = randomIntBetween(1, 3);
Settings nodeSettings = ImmutableSettings.builder()
.put(GroovyScriptEngineService.GROOVY_SCRIPT_SANDBOX_ENABLED, true)
.build();
internalCluster().startNodesAsync(nodes, nodeSettings).get();
client().admin().cluster().prepareHealth().setWaitForNodes(nodes + "").get();
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefresh(true).get();
// Plain test
testSuccess("");
// List
testSuccess("def list = [doc['foo'].value, 3, 4]; def v = list.get(1); list.add(10)");
// Ranges
testSuccess("def range = 1..doc['foo'].value; def v = range.get(0)");
// Maps
testSuccess("def v = doc['foo'].value; def m = [:]; m.put(\\\"value\\\", v)");
// Times
testSuccess("def t = Instant.now().getMillis()");
// GroovyCollections
testSuccess("def n = [1,2,3]; GroovyCollections.max(n)");
// Fail cases
testFailure("pr = Runtime.getRuntime().exec(\\\"touch /tmp/gotcha\\\"); pr.waitFor()",
"Method calls not allowed on [java.lang.Runtime]");
testFailure("d = new DateTime(); d.getClass().getDeclaredMethod(\\\"plus\\\").setAccessible(true)",
"Expression [MethodCallExpression] is not allowed: d.getClass()");
testFailure("d = new DateTime(); d.\\\"${'get' + 'Class'}\\\"()." +
"\\\"${'getDeclared' + 'Method'}\\\"(\\\"now\\\").\\\"${'set' + 'Accessible'}\\\"(false)",
"Expression [MethodCallExpression] is not allowed: d.$(get + Class)().$(getDeclared + Method)(now).$(set + Accessible)(false)");
testFailure("Class.forName(\\\"DateTime\\\").getDeclaredMethod(\\\"plus\\\").setAccessible(true)",
"Expression [MethodCallExpression] is not allowed: java.lang.Class.forName(DateTime)");
testFailure("Eval.me('2 + 2')", "Method calls not allowed on [groovy.util.Eval]");
testFailure("Eval.x(5, 'x + 2')", "Method calls not allowed on [groovy.util.Eval]");
testFailure("t = new java.util.concurrent.ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, " +
"new java.util.concurrent.LinkedBlockingQueue<Runnable>()); t.execute({ println 5 })",
"Expression [ConstructorCallExpression] is not allowed: new java.util.concurrent.ThreadPoolExecutor");
testFailure("d = new Date(); java.lang.reflect.Field f = Date.class.getDeclaredField(\\\"fastTime\\\");" +
" f.setAccessible(true); f.get(\\\"fastTime\\\")",
"Method calls not allowed on [java.lang.reflect.Field]");
testFailure("t = new Thread({ println 3 }); t.start(); t.join()",
"Expression [ConstructorCallExpression] is not allowed: new java.lang.Thread");
testFailure("Thread.start({ println 4 })", "Method calls not allowed on [java.lang.Thread]");
testFailure("import java.util.concurrent.ThreadPoolExecutor;",
"Importing [java.util.concurrent.ThreadPoolExecutor] is not allowed");
testFailure("s = new java.net.URL();", "Expression [ConstructorCallExpression] is not allowed: new java.net.URL()");
testFailure("def methodName = 'ex'; Runtime.\\\"${'get' + 'Runtime'}\\\"().\\\"${methodName}ec\\\"(\\\"touch /tmp/gotcha2\\\")",
"Expression [MethodCallExpression] is not allowed: java.lang.Runtime.$(get + Runtime)().$methodNameec(touch /tmp/gotcha2)");
testFailure("def c = [doc['foo'].value, 3, 4].&size; c()",
"Expression [MethodPointerExpression] is not allowed");
testFailure("[doc['foo'].value, 3, 4].invokeMethod([1,2],\\\"size\\\", new Object[0])",
"Expression [MethodCallExpression] is not allowed: [doc[foo].value, 3, 4].invokeMethod([1, 2], size, [])");
}
@Test
public void testDynamicBlacklist() throws Exception {
int nodes = randomIntBetween(1, 3);
Settings nodeSettings = ImmutableSettings.builder()
.put(GroovyScriptEngineService.GROOVY_SCRIPT_SANDBOX_ENABLED, true)
.build();
internalCluster().startNodesAsync(nodes, nodeSettings).get();
client().admin().cluster().prepareHealth().setWaitForNodes(nodes + "").get();
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefresh(true).get();
testSuccess("[doc['foo'].value, 3, 4].isEmpty()");
testSuccess("[doc['foo'].value, 3, 4].size()");
// Now we blacklist two methods, .isEmpty() and .size()
Settings blacklistSettings = ImmutableSettings.builder()
.put(GroovyScriptEngineService.GROOVY_SCRIPT_BLACKLIST_PATCH, "isEmpty,size")
.build();
client().admin().cluster().prepareUpdateSettings().setTransientSettings(blacklistSettings).get();
testFailure("[doc['foo'].value, 3, 4].isEmpty()",
"Expression [MethodCallExpression] is not allowed: [doc[foo].value, 3, 4].isEmpty()");
testFailure("[doc['foo'].value, 3, 4].size()",
"Expression [MethodCallExpression] is not allowed: [doc[foo].value, 3, 4].size()");
}
public void testSuccess(String script) {
logger.info("--> script: " + script);
SearchResponse resp = client().prepareSearch("test")
.setSource("{\"query\": {\"match_all\": {}}," +
"\"sort\":{\"_script\": {\"script\": \""+ script +
"; doc['foo'].value + 2\", \"type\": \"number\", \"lang\": \"groovy\"}}}").get();
assertNoFailures(resp);
assertThat(resp.getHits().getAt(0).getSortValues(), equalTo(new Object[]{7.0}));
}
public void testFailure(String script, String failMessage) {
logger.info("--> script: " + script);
try {
client().prepareSearch("test")
.setSource("{\"query\": {\"match_all\": {}}," +
"\"sort\":{\"_script\": {\"script\": \""+ script +
"; doc['foo'].value + 2\", \"type\": \"number\", \"lang\": \"groovy\"}}}").get();
fail("script: " + script + " failed to be caught be the sandbox!");
} catch (SearchPhaseExecutionException e) {
assertThat("script failed, but with incorrect message: " + e.toString(), e.toString().contains(failMessage), equalTo(true));
}
}
}

View File

@ -28,7 +28,6 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.env.Environment;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
@ -81,7 +80,7 @@ public class NativeScriptTests extends ElasticsearchTestCase {
nativeScriptFactoryMap.put("my", new MyNativeScriptFactory());
Set<ScriptEngineService> scriptEngineServices = ImmutableSet.<ScriptEngineService>of(new NativeScriptEngineService(settings, nativeScriptFactoryMap));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Lists.<ScriptContext.Plugin>newArrayList());
ScriptService scriptService = new ScriptService(settings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(settings), scriptContextRegistry);
ScriptService scriptService = new ScriptService(settings, environment, scriptEngineServices, resourceWatcherService, scriptContextRegistry);
for (ScriptContext scriptContext : scriptContextRegistry.scriptContexts()) {
assertThat(scriptService.compile(new Script(NativeScriptEngineService.NAME, "my", ScriptType.INLINE, null), scriptContext), notNullValue());

View File

@ -1,57 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.script;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
/**
* Test that a system where the sandbox is disabled while dynamic scripting is
* also disabled does not allow a script to be sent
*/
@ElasticsearchIntegrationTest.ClusterScope(scope=ElasticsearchIntegrationTest.Scope.SUITE)
public class SandboxDisabledTests extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder().put(super.nodeSettings(nodeOrdinal))
.put(GroovyScriptEngineService.GROOVY_SCRIPT_SANDBOX_ENABLED, false)
.put("script.inline", false).build();
}
@Test
public void testScriptingDisabledWhileSandboxDisabled() {
client().prepareIndex("test", "doc", "1").setSource("foo", 5).setRefresh(true).get();
try {
client().prepareSearch("test")
.setSource("{\"query\": {\"match_all\": {}}," +
"\"sort\":{\"_script\": {\"script\": \"doc['foo'].value + 2\", \"type\": \"number\", \"lang\": \"groovy\"}}}").get();
fail("shards should fail because the sandbox and dynamic scripting are disabled");
} catch (Exception e) {
assertThat(e.toString(), containsString("scripts of type [inline], operation [search] and lang [groovy] are disabled"));
}
}
}

View File

@ -26,7 +26,6 @@ import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.node.settings.NodeSettingsService;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.expression.ExpressionScriptEngineService;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
@ -103,7 +102,7 @@ public class ScriptServiceTests extends ElasticsearchTestCase {
private void buildScriptService(Settings additionalSettings) throws IOException {
Settings finalSettings = ImmutableSettings.builder().put(baseSettings).put(additionalSettings).build();
Environment environment = new Environment(finalSettings);
scriptService = new ScriptService(finalSettings, environment, scriptEngineServices, resourceWatcherService, new NodeSettingsService(finalSettings), scriptContextRegistry) {
scriptService = new ScriptService(finalSettings, environment, scriptEngineServices, resourceWatcherService, scriptContextRegistry) {
@Override
String getScriptFromIndex(String scriptLang, String id) {
//mock the script that gets retrieved from an index

View File

@ -26,7 +26,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.cache.filter.FilterCacheModule;
import org.elasticsearch.index.cache.filter.FilterCacheModule.FilterCacheSettings;
import org.elasticsearch.index.cache.filter.weighted.WeightedFilterCache;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
@ -50,7 +49,6 @@ public class ScriptFilterSearchTests extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder().put(super.nodeSettings(nodeOrdinal))
.put(GroovyScriptEngineService.GROOVY_SCRIPT_SANDBOX_ENABLED, false)
// aggressive filter caching so that we can assert on the number of iterations of the script filters
.put(FilterCacheModule.FilterCacheSettings.FILTER_CACHE_TYPE, WeightedFilterCache.class)
.put(FilterCacheSettings.FILTER_CACHE_EVERYTHING, true)

View File

@ -22,7 +22,6 @@ package org.elasticsearch.search.timeout;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
@ -38,7 +37,7 @@ public class SearchTimeoutTests extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder().put(super.nodeSettings(nodeOrdinal)).put(GroovyScriptEngineService.GROOVY_SCRIPT_SANDBOX_ENABLED, false).build();
return ImmutableSettings.settingsBuilder().put(super.nodeSettings(nodeOrdinal)).build();
}
@Test