You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/07/15 23:00:38 UTC
[23/31] incubator-tinkerpop git commit: Lay some foundations for
method whitelisting in the sandbox.
Lay some foundations for method whitelisting in the sandbox.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/6af00c69
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/6af00c69
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/6af00c69
Branch: refs/heads/tp30
Commit: 6af00c6984c206e1fea2cb30305a877c54ef1e73
Parents: e598829
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jul 10 08:50:28 2015 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jul 10 08:50:28 2015 -0400
----------------------------------------------------------------------
.../groovy/jsr223/SandboxExtension.groovy | 52 ++++++++++++++++++--
.../jsr223/GremlinGroovyScriptEngine.java | 32 ++++++------
2 files changed, 64 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/6af00c69/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/SandboxExtension.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/SandboxExtension.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/SandboxExtension.groovy
index 4888f8c..d62ccb3 100644
--- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/SandboxExtension.groovy
+++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/jsr223/SandboxExtension.groovy
@@ -21,21 +21,40 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.apache.tinkerpop.gremlin.structure.Graph
import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.transform.stc.ExtensionMethodNode
import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport
import java.util.function.BiPredicate
/**
+ * A sandbox for the {@link GremlinGroovyScriptEngine} that provides base functionality for securing evaluated scripts.
+ * By default, this implementation ensures that the variable "graph" is always a {@link Graph} instance and the
+ * variable "g" is always a {@link GraphTraversalSource}.
+ * <p/>
+ * Users should typically extend this class to modify features.
+ *
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
class SandboxExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
+ public static final BiPredicate<VariableExpression, Map<String,ClassNode>> VARIABLES_ALLOW_ALL = { var, types -> true }
+
+ public static final BiPredicate<VariableExpression, Map<String,ClassNode>> METHODS_ALLOW_ALL = { exp, method -> true }
+ public static final BiPredicate<Expression, MethodNode> METHODS_REGEX_WHITELIST = { exp, method ->
+ def descriptor = toMethodDescriptor(method)
+ !methodList.any { descriptor =~ it }
+ }
+ public static final BiPredicate<Expression, MethodNode> METHODS_REGEX_BLACKLIST = { exp, method -> !METHODS_REGEX_WHITELIST.test(exp, method) }
+
protected boolean graphIsAlwaysGraphInstance = true
protected boolean gIsAlwaysGraphTraversalSource = true
- protected BiPredicate<VariableExpression, Map<String,ClassNode>> filter = USE_ALL
-
- public static final BiPredicate<VariableExpression, Map<String,ClassNode>> USE_ALL = { var, types -> true }
+ protected BiPredicate<VariableExpression, Map<String,ClassNode>> variableFilter = VARIABLES_ALLOW_ALL
+ protected BiPredicate<Expression, MethodNode> methodFilter = METHODS_ALLOW_ALL
+ protected List<String> methodList = new ArrayList<String>();
@Override
Object run() {
@@ -54,7 +73,7 @@ class SandboxExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDS
final Map<String,ClassNode> varTypes = (Map<String,ClassNode>) GremlinGroovyScriptEngine.COMPILE_OPTIONS.get()
.get(GremlinGroovyScriptEngine.COMPILE_OPTIONS_VAR_TYPES)
- if (varTypes.containsKey(var.name) && filter.test(var, varTypes)
+ if (varTypes.containsKey(var.name) && variableFilter.test(var, varTypes)
|| (var.name == "graph" && !graphIsAlwaysGraphInstance
|| var.name == "g" && !gIsAlwaysGraphTraversalSource)) {
storeType(var, varTypes.get(var.name))
@@ -62,5 +81,30 @@ class SandboxExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDS
return
}
}
+
+ onMethodSelection { expr, MethodNode methodNode ->
+ if (!methodFilter.test(expr, methodNode))
+ addStaticTypeError("Not authorized to call this method: $descr", expr)
+ }
+ }
+
+ private static String prettyPrint(ClassNode node) {
+ node.isArray()?"${prettyPrint(node.componentType)}[]":node.toString(false)
+ }
+
+ private static String toMethodDescriptor(final MethodNode node) {
+ if (node instanceof ExtensionMethodNode)
+ return toMethodDescriptor(node.extensionMethodNode)
+
+ def sb = new StringBuilder()
+ sb.append(node.declaringClass.toString(false))
+ sb.append("#")
+ sb.append(node.name)
+ sb.append('(')
+ sb.append(node.parameters.collect { Parameter it ->
+ prettyPrint(it.originType)
+ }.join(','))
+ sb.append(')')
+ sb
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/6af00c69/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
index 09af47f..d484d4e 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
@@ -84,6 +84,7 @@ import java.util.stream.Collectors;
public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements DependencyManager, AutoCloseable {
public static final String COMPILE_OPTIONS_VAR_TYPES = "sandbox.bindings";
+ public static final String COMPILE_OPTIONS_WHITELIST = "sandbox.whitelist";
public static final String KEY_REFERENCE_TYPE = "#jsr223.groovy.engine.keep.globals";
public static final String REFERENCE_TYPE_PHANTOM = "phantom";
public static final String REFERENCE_TYPE_WEAK = "weak";
@@ -148,12 +149,12 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
GremlinLoader.load();
- this.importCustomizerProvider = providers.stream()
+ importCustomizerProvider = providers.stream()
.filter(p -> p instanceof ImportCustomizerProvider)
.map(p -> (ImportCustomizerProvider) p)
.findFirst().orElse(NoImportCustomizerProvider.INSTANCE);
- this.securityProvider = providers.stream()
+ securityProvider = providers.stream()
.filter(p -> p instanceof SecurityCustomizerProvider)
.map(p -> (SecurityCustomizerProvider) p)
.findFirst();
@@ -238,8 +239,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
// use the EmptyImportCustomizer because it doesn't come with static initializers containing
// existing imports.
- this.importCustomizerProvider = new EmptyImportCustomizerProvider(
- this.importCustomizerProvider, imports, staticImports);
+ importCustomizerProvider = new EmptyImportCustomizerProvider(importCustomizerProvider, imports, staticImports);
reset();
}
@@ -249,25 +249,25 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
@Override
public void close() throws Exception {
- this.securityProvider.ifPresent(SecurityCustomizerProvider::unregisterInterceptors);
+ securityProvider.ifPresent(SecurityCustomizerProvider::unregisterInterceptors);
}
@Override
public void reset() {
- this.securityProvider.ifPresent(SecurityCustomizerProvider::unregisterInterceptors);
+ securityProvider.ifPresent(SecurityCustomizerProvider::unregisterInterceptors);
createClassLoader();
// must clear the local cache here because the the classloader has been reset. therefore, classes previously
// referenced before that might not have evaluated might cleanly evaluate now.
- this.classMap.clear();
- this.globalClosures.clear();
+ classMap.clear();
+ globalClosures.clear();
- this.loadedPlugins.clear();
+ loadedPlugins.clear();
final Set<Artifact> toReuse = new HashSet<>(artifactsToUse);
toReuse.forEach(this::use);
- this.getContext().getBindings(ScriptContext.ENGINE_SCOPE).clear();
+ getContext().getBindings(ScriptContext.ENGINE_SCOPE).clear();
}
private void use(final Artifact artifact) {
@@ -315,10 +315,10 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
@Override
public ScriptEngineFactory getFactory() {
- if (this.factory == null) {
+ if (factory == null) {
synchronized (this) {
- if (this.factory == null) {
- this.factory = new GremlinGroovyScriptEngineFactory();
+ if (factory == null) {
+ factory = new GremlinGroovyScriptEngineFactory();
}
}
}
@@ -476,9 +476,9 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
}
private void ensureSandbox() {
- if (securityProvider.isPresent() && !this.registeredSandbox.get()) {
- this.securityProvider.get().registerInterceptors();
- this.registeredSandbox.set(true);
+ if (securityProvider.isPresent() && !registeredSandbox.get()) {
+ securityProvider.get().registerInterceptors();
+ registeredSandbox.set(true);
}
}