diff --git a/core/src/main/java/org/elasticsearch/script/ClassPermission.java b/core/src/main/java/org/elasticsearch/script/ClassPermission.java
new file mode 100644
index 00000000000..eb580bac3ea
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/script/ClassPermission.java
@@ -0,0 +1,171 @@
+/*
+ * 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 java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Checked by scripting engines to allow loading a java class.
+ *
+ * Examples:
+ *
+ * Allow permission to {@code java.util.List}
+ *
+ * Set of classes (allowed by special value <<STANDARD>>):
+ *
+ *
{@link java.lang.Boolean}
+ *
{@link java.lang.Byte}
+ *
{@link java.lang.Character}
+ *
{@link java.lang.Double}
+ *
{@link java.lang.Integer}
+ *
{@link java.lang.Long}
+ *
{@link java.lang.Math}
+ *
{@link java.lang.Object}
+ *
{@link java.lang.Short}
+ *
{@link java.lang.String}
+ *
{@link java.math.BigDecimal}
+ *
{@link java.util.ArrayList}
+ *
{@link java.util.Arrays}
+ *
{@link java.util.Date}
+ *
{@link java.util.HashMap}
+ *
{@link java.util.HashSet}
+ *
{@link java.util.Iterator}
+ *
{@link java.util.List}
+ *
{@link java.util.Map}
+ *
{@link java.util.Set}
+ *
{@link java.util.UUID}
+ *
{@link org.joda.time.DateTime}
+ *
{@link org.joda.time.DateTimeUtils}
+ *
{@link org.joda.time.DateTimeZone}
+ *
{@link org.joda.time.Instant}
+ *
+ */
+public final class ClassPermission extends BasicPermission {
+ private static final long serialVersionUID = 3530711429252193884L;
+
+ public static final String STANDARD = "<>";
+ /** Typical set of classes for scripting: basic data types, math, dates, and simple collections */
+ // this is the list from the old grovy sandbox impl (+ some things like String, Iterator, etc that were missing)
+ public static final Set STANDARD_CLASSES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ // jdk classes
+ java.lang.Boolean.class.getName(),
+ java.lang.Byte.class.getName(),
+ java.lang.Character.class.getName(),
+ java.lang.Double.class.getName(),
+ java.lang.Integer.class.getName(),
+ java.lang.Long.class.getName(),
+ java.lang.Math.class.getName(),
+ java.lang.Object.class.getName(),
+ java.lang.Short.class.getName(),
+ java.lang.String.class.getName(),
+ java.math.BigDecimal.class.getName(),
+ java.util.ArrayList.class.getName(),
+ java.util.Arrays.class.getName(),
+ java.util.Date.class.getName(),
+ java.util.HashMap.class.getName(),
+ java.util.HashSet.class.getName(),
+ java.util.Iterator.class.getName(),
+ java.util.List.class.getName(),
+ java.util.Map.class.getName(),
+ java.util.Set.class.getName(),
+ java.util.UUID.class.getName(),
+ // joda-time
+ org.joda.time.DateTime.class.getName(),
+ org.joda.time.DateTimeUtils.class.getName(),
+ org.joda.time.DateTimeZone.class.getName(),
+ org.joda.time.Instant.class.getName()
+ )));
+
+ /**
+ * Creates a new ClassPermission object.
+ *
+ * @param name class to grant permission to
+ */
+ public ClassPermission(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates a new ClassPermission object.
+ * This constructor exists for use by the {@code Policy} object to instantiate new Permission objects.
+ *
+ * @param name class to grant permission to
+ * @param actions ignored
+ */
+ public ClassPermission(String name, String actions) {
+ this(name);
+ }
+
+ @Override
+ public boolean implies(Permission p) {
+ // check for a special value of STANDARD to imply the basic set
+ if (p != null && p.getClass() == getClass()) {
+ ClassPermission other = (ClassPermission) p;
+ if (STANDARD.equals(getName()) && STANDARD_CLASSES.contains(other.getName())) {
+ return true;
+ }
+ }
+ return super.implies(p);
+ }
+
+ @Override
+ public PermissionCollection newPermissionCollection() {
+ // BasicPermissionCollection only handles wildcards, we expand <> here
+ PermissionCollection impl = super.newPermissionCollection();
+ return new PermissionCollection() {
+ private static final long serialVersionUID = 6792220143549780002L;
+
+ @Override
+ public void add(Permission permission) {
+ if (permission instanceof ClassPermission && STANDARD.equals(permission.getName())) {
+ for (String clazz : STANDARD_CLASSES) {
+ impl.add(new ClassPermission(clazz));
+ }
+ } else {
+ impl.add(permission);
+ }
+ }
+
+ @Override
+ public boolean implies(Permission permission) {
+ return impl.implies(permission);
+ }
+
+ @Override
+ public Enumeration elements() {
+ return impl.elements();
+ }
+ };
+ }
+}
diff --git a/core/src/main/resources/org/elasticsearch/bootstrap/untrusted.policy b/core/src/main/resources/org/elasticsearch/bootstrap/untrusted.policy
index d32ea6a2435..8e7ca8d8b6e 100644
--- a/core/src/main/resources/org/elasticsearch/bootstrap/untrusted.policy
+++ b/core/src/main/resources/org/elasticsearch/bootstrap/untrusted.policy
@@ -34,5 +34,6 @@ grant {
permission java.util.PropertyPermission "rhino.stack.style", "read";
// needed IndyInterface selectMethod (setCallSiteTarget)
+ // TODO: clean this up / only give it to engines that really must have it
permission java.lang.RuntimePermission "getClassLoader";
};
diff --git a/core/src/test/java/org/elasticsearch/script/ClassPermissionTests.java b/core/src/test/java/org/elasticsearch/script/ClassPermissionTests.java
new file mode 100644
index 00000000000..05a65363ff5
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/script/ClassPermissionTests.java
@@ -0,0 +1,79 @@
+/*
+ * 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.test.ESTestCase;
+
+import java.security.AllPermission;
+import java.security.PermissionCollection;
+
+/** Very simple sanity checks for {@link ClassPermission} */
+public class ClassPermissionTests extends ESTestCase {
+
+ public void testEquals() {
+ assertEquals(new ClassPermission("pkg.MyClass"), new ClassPermission("pkg.MyClass"));
+ assertFalse(new ClassPermission("pkg.MyClass").equals(new AllPermission()));
+ }
+
+ public void testImplies() {
+ assertTrue(new ClassPermission("pkg.MyClass").implies(new ClassPermission("pkg.MyClass")));
+ assertFalse(new ClassPermission("pkg.MyClass").implies(new ClassPermission("pkg.MyOtherClass")));
+ assertFalse(new ClassPermission("pkg.MyClass").implies(null));
+ assertFalse(new ClassPermission("pkg.MyClass").implies(new AllPermission()));
+ }
+
+ public void testStandard() {
+ assertTrue(new ClassPermission("<>").implies(new ClassPermission("java.lang.Math")));
+ assertFalse(new ClassPermission("<>").implies(new ClassPermission("pkg.MyClass")));
+ }
+
+ public void testPermissionCollection() {
+ ClassPermission math = new ClassPermission("java.lang.Math");
+ PermissionCollection collection = math.newPermissionCollection();
+ collection.add(math);
+ assertTrue(collection.implies(new ClassPermission("java.lang.Math")));
+ assertFalse(collection.implies(new ClassPermission("pkg.MyClass")));
+ }
+
+ public void testPermissionCollectionStandard() {
+ ClassPermission standard = new ClassPermission("<>");
+ PermissionCollection collection = standard.newPermissionCollection();
+ collection.add(standard);
+ assertTrue(collection.implies(new ClassPermission("java.lang.Math")));
+ assertFalse(collection.implies(new ClassPermission("pkg.MyClass")));
+ }
+
+ /** not recommended but we test anyway */
+ public void testWildcards() {
+ assertTrue(new ClassPermission("*").implies(new ClassPermission("pkg.MyClass")));
+ assertTrue(new ClassPermission("pkg.*").implies(new ClassPermission("pkg.MyClass")));
+ assertTrue(new ClassPermission("pkg.*").implies(new ClassPermission("pkg.sub.MyClass")));
+ assertFalse(new ClassPermission("pkg.My*").implies(new ClassPermission("pkg.MyClass")));
+ assertFalse(new ClassPermission("pkg*").implies(new ClassPermission("pkg.MyClass")));
+ }
+
+ public void testPermissionCollectionWildcards() {
+ ClassPermission lang = new ClassPermission("java.lang.*");
+ PermissionCollection collection = lang.newPermissionCollection();
+ collection.add(lang);
+ assertTrue(collection.implies(new ClassPermission("java.lang.Math")));
+ assertFalse(collection.implies(new ClassPermission("pkg.MyClass")));
+ }
+}
diff --git a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java
index 72a1dd7d5c6..d773d5a8866 100644
--- a/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java
+++ b/modules/lang-expression/src/main/java/org/elasticsearch/script/expression/ExpressionScriptEngineService.java
@@ -36,6 +36,7 @@ import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.mapper.core.NumberFieldMapper;
+import org.elasticsearch.script.ClassPermission;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptEngineService;
@@ -95,7 +96,7 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
@Override
public Object compile(String script) {
// classloader created here
- SecurityManager sm = System.getSecurityManager();
+ final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SpecialPermission());
}
@@ -103,8 +104,22 @@ public class ExpressionScriptEngineService extends AbstractComponent implements
@Override
public Expression run() {
try {
+ ClassLoader loader = getClass().getClassLoader();
+ if (sm != null) {
+ loader = new ClassLoader(loader) {
+ @Override
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ sm.checkPermission(new ClassPermission(name));
+ } catch (SecurityException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ return super.loadClass(name, resolve);
+ }
+ };
+ }
// NOTE: validation is delayed to allow runtime vars, and we don't have access to per index stuff here
- return JavascriptCompiler.compile(script);
+ return JavascriptCompiler.compile(script, JavascriptCompiler.DEFAULT_FUNCTIONS, loader);
} catch (ParseException e) {
throw new ScriptException("Failed to parse expression: " + script, e);
}
diff --git a/modules/lang-expression/src/main/plugin-metadata/plugin-security.policy b/modules/lang-expression/src/main/plugin-metadata/plugin-security.policy
index 5bada15755e..9f50be3dd05 100644
--- a/modules/lang-expression/src/main/plugin-metadata/plugin-security.policy
+++ b/modules/lang-expression/src/main/plugin-metadata/plugin-security.policy
@@ -22,4 +22,13 @@ grant {
permission java.lang.RuntimePermission "createClassLoader";
// needed because of security problems in JavascriptCompiler
permission java.lang.RuntimePermission "getClassLoader";
+
+ // expression runtime
+ permission org.elasticsearch.script.ClassPermission "java.lang.String";
+ permission org.elasticsearch.script.ClassPermission "org.apache.lucene.expressions.Expression";
+ permission org.elasticsearch.script.ClassPermission "org.apache.lucene.queries.function.FunctionValues";
+ // available functions
+ permission org.elasticsearch.script.ClassPermission "java.lang.Math";
+ permission org.elasticsearch.script.ClassPermission "org.apache.lucene.util.MathUtil";
+ permission org.elasticsearch.script.ClassPermission "org.apache.lucene.util.SloppyMath";
};
diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java
index 4ac4b402c17..89a5be7ff1c 100644
--- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java
+++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/MoreExpressionTests.java
@@ -97,6 +97,16 @@ public class MoreExpressionTests extends ESIntegTestCase {
assertEquals(1, rsp.getHits().getTotalHits());
assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D);
}
+
+ public void testFunction() throws Exception {
+ createIndex("test");
+ ensureGreen("test");
+ client().prepareIndex("test", "doc", "1").setSource("foo", 4).setRefresh(true).get();
+ SearchResponse rsp = buildRequest("doc['foo'] + abs(1)").get();
+ assertSearchResponse(rsp);
+ assertEquals(1, rsp.getHits().getTotalHits());
+ assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D);
+ }
public void testBasicUsingDotValue() throws Exception {
createIndex("test");
diff --git a/modules/lang-groovy/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java b/modules/lang-groovy/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java
index 42ee05a7e03..85f57694ce6 100644
--- a/modules/lang-groovy/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java
+++ b/modules/lang-groovy/src/main/java/org/elasticsearch/script/groovy/GroovyScriptEngineService.java
@@ -51,6 +51,7 @@ import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
+import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
@@ -65,16 +66,6 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri
* The name of the scripting engine/language.
*/
public static final String NAME = "groovy";
- /**
- * The setting to enable or disable invokedynamic instruction support in Java 7+.
- *
- * Note: If this is disabled because invokedynamic is causing issues, then the Groovy
- * indy jar needs to be replaced by the non-indy variant of it on the classpath (e.g.,
- * groovy-all-2.4.4-indy.jar should be replaced by groovy-all-2.4.4.jar).
- *
- * Defaults to {@code true}.
- */
- public static final String GROOVY_INDY_ENABLED = "script.groovy.indy";
/**
* The name of the Groovy compiler setting to use associated with activating invokedynamic support.
*/
@@ -96,22 +87,33 @@ public class GroovyScriptEngineService extends AbstractComponent implements Scri
// Add BigDecimal -> Double transformer
config.addCompilationCustomizers(new GroovyBigDecimalTransformer(CompilePhase.CONVERSION));
- // Implicitly requires Java 7u60 or later to get valid support
- if (settings.getAsBoolean(GROOVY_INDY_ENABLED, true)) {
- // maintain any default optimizations
- config.getOptimizationOptions().put(GROOVY_INDY_SETTING_NAME, true);
- }
+ // always enable invokeDynamic, not the crazy softreference-based stuff
+ config.getOptimizationOptions().put(GROOVY_INDY_SETTING_NAME, true);
// Groovy class loader to isolate Groovy-land code
// classloader created here
- SecurityManager sm = System.getSecurityManager();
+ final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SpecialPermission());
}
this.loader = AccessController.doPrivileged(new PrivilegedAction() {
@Override
public GroovyClassLoader run() {
- return new GroovyClassLoader(getClass().getClassLoader(), config);
+ // snapshot our context (which has permissions for classes), since the script has none
+ final AccessControlContext engineContext = AccessController.getContext();
+ return new GroovyClassLoader(new ClassLoader(getClass().getClassLoader()) {
+ @Override
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (sm != null) {
+ try {
+ engineContext.checkPermission(new ClassPermission(name));
+ } catch (SecurityException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ }
+ return super.loadClass(name, resolve);
+ }
+ }, config);
}
});
}
diff --git a/modules/lang-groovy/src/main/plugin-metadata/plugin-security.policy b/modules/lang-groovy/src/main/plugin-metadata/plugin-security.policy
index 4481203994e..7de3e1a62aa 100644
--- a/modules/lang-groovy/src/main/plugin-metadata/plugin-security.policy
+++ b/modules/lang-groovy/src/main/plugin-metadata/plugin-security.policy
@@ -28,4 +28,24 @@ grant {
permission java.lang.RuntimePermission "closeClassLoader";
// Allow executing groovy scripts with codesource of /untrusted
permission groovy.security.GroovyCodeSourcePermission "/untrusted";
+
+ // Standard set of classes
+ permission org.elasticsearch.script.ClassPermission "<>";
+ // groovy runtime (TODO: clean these up if possible)
+ permission org.elasticsearch.script.ClassPermission "groovy.grape.GrabAnnotationTransformation";
+ permission org.elasticsearch.script.ClassPermission "groovy.json.JsonOutput";
+ permission org.elasticsearch.script.ClassPermission "groovy.lang.Binding";
+ permission org.elasticsearch.script.ClassPermission "groovy.lang.GroovyObject";
+ permission org.elasticsearch.script.ClassPermission "groovy.lang.GString";
+ permission org.elasticsearch.script.ClassPermission "groovy.lang.Script";
+ permission org.elasticsearch.script.ClassPermission "groovy.util.GroovyCollections";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.ast.builder.AstBuilderTransformation";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.reflection.ClassInfo";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.GStringImpl";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.ValueRecorder";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.powerassert.AssertionRenderer";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.ScriptBytecodeAdapter";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation";
+ permission org.elasticsearch.script.ClassPermission "org.codehaus.groovy.vmplugin.v7.IndyInterface";
+ permission org.elasticsearch.script.ClassPermission "sun.reflect.ConstructorAccessorImpl";
};
diff --git a/modules/lang-groovy/src/test/java/org/elasticsearch/script/groovy/GroovySecurityTests.java b/modules/lang-groovy/src/test/java/org/elasticsearch/script/groovy/GroovySecurityTests.java
index 258d957d77e..5f91631c021 100644
--- a/modules/lang-groovy/src/test/java/org/elasticsearch/script/groovy/GroovySecurityTests.java
+++ b/modules/lang-groovy/src/test/java/org/elasticsearch/script/groovy/GroovySecurityTests.java
@@ -20,16 +20,22 @@
package org.elasticsearch.script.groovy;
import org.apache.lucene.util.Constants;
+import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
+import groovy.lang.MissingPropertyException;
+
import java.nio.file.Path;
+import java.security.PrivilegedActionException;
import java.util.AbstractMap;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -48,7 +54,7 @@ public class GroovySecurityTests extends ESTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
- se = new GroovyScriptEngineService(Settings.Builder.EMPTY_SETTINGS);
+ se = new GroovyScriptEngineService(Settings.EMPTY);
// otherwise will exit your VM and other bad stuff
assumeTrue("test requires security manager to be enabled", System.getSecurityManager() != null);
}
@@ -62,8 +68,16 @@ public class GroovySecurityTests extends ESTestCase {
public void testEvilGroovyScripts() throws Exception {
// Plain test
assertSuccess("");
- // field access
+ // field access (via map)
assertSuccess("def foo = doc['foo'].value; if (foo == null) { return 5; }");
+ // field access (via list)
+ assertSuccess("def foo = mylist[0]; if (foo == null) { return 5; }");
+ // field access (via array)
+ assertSuccess("def foo = myarray[0]; if (foo == null) { return 5; }");
+ // field access (via object)
+ assertSuccess("def foo = myobject.primitive.toString(); if (foo == null) { return 5; }");
+ assertSuccess("def foo = myobject.object.toString(); if (foo == null) { return 5; }");
+ assertSuccess("def foo = myobject.list[0].primitive.toString(); if (foo == null) { return 5; }");
// List
assertSuccess("def list = [doc['foo'].value, 3, 4]; def v = list.get(1); list.add(10)");
// Ranges
@@ -78,35 +92,35 @@ public class GroovySecurityTests extends ESTestCase {
assertSuccess("def n = [1,2,3]; GroovyCollections.max(n)");
// Fail cases:
- // AccessControlException[access denied ("java.io.FilePermission" "<>" "execute")]
- assertFailure("pr = Runtime.getRuntime().exec(\"touch /tmp/gotcha\"); pr.waitFor()");
+ assertFailure("pr = Runtime.getRuntime().exec(\"touch /tmp/gotcha\"); pr.waitFor()", MissingPropertyException.class);
- // AccessControlException[access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.reflect")]
- assertFailure("d = new DateTime(); d.getClass().getDeclaredMethod(\"year\").setAccessible(true)");
+ // infamous:
+ assertFailure("java.lang.Math.class.forName(\"java.lang.Runtime\")", PrivilegedActionException.class);
+ // filtered directly by our classloader
+ assertFailure("getClass().getClassLoader().loadClass(\"java.lang.Runtime\").availableProcessors()", PrivilegedActionException.class);
+ // unfortunately, we have access to other classloaders (due to indy mechanism needing getClassLoader permission)
+ // but we can't do much with them directly at least.
+ assertFailure("myobject.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").availableProcessors()", SecurityException.class);
+ assertFailure("d = new DateTime(); d.getClass().getDeclaredMethod(\"year\").setAccessible(true)", SecurityException.class);
assertFailure("d = new DateTime(); d.\"${'get' + 'Class'}\"()." +
- "\"${'getDeclared' + 'Method'}\"(\"year\").\"${'set' + 'Accessible'}\"(false)");
- assertFailure("Class.forName(\"org.joda.time.DateTime\").getDeclaredMethod(\"year\").setAccessible(true)");
+ "\"${'getDeclared' + 'Method'}\"(\"year\").\"${'set' + 'Accessible'}\"(false)", SecurityException.class);
+ assertFailure("Class.forName(\"org.joda.time.DateTime\").getDeclaredMethod(\"year\").setAccessible(true)", MissingPropertyException.class);
- // AccessControlException[access denied ("groovy.security.GroovyCodeSourcePermission" "/groovy/shell")]
- assertFailure("Eval.me('2 + 2')");
- assertFailure("Eval.x(5, 'x + 2')");
+ assertFailure("Eval.me('2 + 2')", MissingPropertyException.class);
+ assertFailure("Eval.x(5, 'x + 2')", MissingPropertyException.class);
- // AccessControlException[access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")]
assertFailure("d = new Date(); java.lang.reflect.Field f = Date.class.getDeclaredField(\"fastTime\");" +
- " f.setAccessible(true); f.get(\"fastTime\")");
+ " f.setAccessible(true); f.get(\"fastTime\")", MultipleCompilationErrorsException.class);
- // AccessControlException[access denied ("java.io.FilePermission" "<>" "execute")]
- assertFailure("def methodName = 'ex'; Runtime.\"${'get' + 'Runtime'}\"().\"${methodName}ec\"(\"touch /tmp/gotcha2\")");
+ assertFailure("def methodName = 'ex'; Runtime.\"${'get' + 'Runtime'}\"().\"${methodName}ec\"(\"touch /tmp/gotcha2\")", MissingPropertyException.class);
- // AccessControlException[access denied ("java.lang.RuntimePermission" "modifyThreadGroup")]
- assertFailure("t = new Thread({ println 3 });");
+ assertFailure("t = new Thread({ println 3 });", MultipleCompilationErrorsException.class);
// test a directory we normally have access to, but the groovy script does not.
Path dir = createTempDir();
// TODO: figure out the necessary escaping for windows paths here :)
if (!Constants.WINDOWS) {
- // access denied ("java.io.FilePermission" ".../tempDir-00N" "read")
- assertFailure("new File(\"" + dir + "\").exists()");
+ assertFailure("new File(\"" + dir + "\").exists()", MultipleCompilationErrorsException.class);
}
}
@@ -115,8 +129,18 @@ public class GroovySecurityTests extends ESTestCase {
Map vars = new HashMap();
// 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