refactor GroovySecurityTests into a unit test.

This was basically a resurrected form of the tests for the old sandbox.
We use it to check that groovy scripts some degree of additional containment.

The other scripting plugins (javascript, python) already have this as a unit test,
its much easier to debug any problems that way.

closes #14484
This commit is contained in:
Robert Muir 2015-11-03 13:39:17 -05:00
parent 64a01cfb05
commit 7e6008f0b9
1 changed files with 35 additions and 60 deletions

View File

@ -20,61 +20,44 @@
package org.elasticsearch.script.groovy;
import org.apache.lucene.util.Constants;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import java.nio.file.Path;
import java.util.Collection;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Locale;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import java.util.HashMap;
import java.util.Map;
/**
* Tests for the Groovy security permissions
*/
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0)
// TODO: refactor into unit test, or, proper REST test
public class GroovySecurityTests extends ESIntegTestCase {
public class GroovySecurityTests extends ESTestCase {
private GroovyScriptEngineService se;
@Override
public void setUp() throws Exception {
super.setUp();
se = new GroovyScriptEngineService(Settings.Builder.EMPTY_SETTINGS);
// otherwise will exit your VM and other bad stuff
assumeTrue("test requires security manager to be enabled", System.getSecurityManager() != null);
}
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(GroovyPlugin.class);
public void tearDown() throws Exception {
se.close();
super.tearDown();
}
public void testEvilGroovyScripts() throws Exception {
int nodes = randomIntBetween(1, 3);
Settings nodeSettings = Settings.builder()
.put("script.inline", true)
.put("script.indexed", true)
.build();
internalCluster().startNodesAsync(nodes, nodeSettings).get();
client().admin().cluster().prepareHealth().setWaitForNodes(nodes + "").get();
client().prepareIndex("test", "doc", "1").setSource("foo", 5, "bar", "baz").setRefresh(true).get();
// Plain test
assertSuccess("");
// numeric field access
// field access
assertSuccess("def foo = doc['foo'].value; if (foo == null) { return 5; }");
// string field access
assertSuccess("def bar = doc['bar'].value; if (bar == null) { return 5; }");
// List
assertSuccess("def list = [doc['foo'].value, 3, 4]; def v = list.get(1); list.add(10)");
// Ranges
@ -119,36 +102,28 @@ public class GroovySecurityTests extends ESIntegTestCase {
}
}
private void assertSuccess(String script) {
logger.info("--> script: " + script);
SearchResponse resp = client()
.prepareSearch("test")
.setSource(
new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).sort(
SortBuilders.scriptSort(new Script(script + "; doc['foo'].value + 2", ScriptType.INLINE, "groovy", null),
"number"))).get();
assertNoFailures(resp);
assertEquals(1, resp.getHits().getTotalHits());
assertThat(resp.getHits().getAt(0).getSortValues(), equalTo(new Object[]{7.0}));
/** runs a script */
private void doTest(String script) {
Map<String, Object> vars = new HashMap<String, Object>();
// we add a "mock document" containing a single field "foo" that returns 4 (abusing a jdk class with a getValue() method)
vars.put("doc", Collections.singletonMap("foo", new AbstractMap.SimpleEntry<Object,Integer>(null, 4)));
se.executable(new CompiledScript(ScriptService.ScriptType.INLINE, "test", "js", se.compile(script)), vars).run();
}
/** asserts that a script runs without exception */
private void assertSuccess(String script) {
doTest(script);
}
/** asserts that a script triggers securityexception */
private void assertFailure(String script) {
logger.info("--> script: " + script);
SearchResponse resp = client()
.prepareSearch("test")
.setSource(
new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).sort(
SortBuilders.scriptSort(new Script(script + "; doc['foo'].value + 2", ScriptType.INLINE, "groovy", null),
"number"))).get();
assertEquals(0, resp.getHits().getTotalHits());
ShardSearchFailure fails[] = resp.getShardFailures();
// TODO: GroovyScriptExecutionException needs work:
// fix it to preserve cause so we don't do this flaky string-check stuff
for (ShardSearchFailure fail : fails) {
assertThat(fail.getCause(), instanceOf(ScriptException.class));
assertTrue("unexpected exception" + fail.getCause(),
// different casing, depending on jvm impl...
fail.getCause().toString().toLowerCase(Locale.ROOT).contains("[access denied"));
try {
doTest(script);
fail("did not get expected exception");
} catch (ScriptException expected) {
Throwable cause = expected.getCause();
assertNotNull(cause);
assertTrue("unexpected exception: " + cause, cause instanceof SecurityException);
}
}
}