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);
         }
     }