Sunday, August 9, 2009

Updated Script Sandbox

The one fixes a minor bug and adds support for code from Readers and support for compiled scripts:

import groovy.util.GroovyScriptEngine;

import java.io.Reader;
import java.io.StringReader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permission;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.Collection;

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptSandbox {
    ScriptEngine _scriptEngine;
    AccessControlContext _accessControlContext;
   
    public ScriptSandbox(String engineShortName) throws InstantiationException{
         ScriptEngineManager sem = new ScriptEngineManager();
         _scriptEngine = sem.getEngineByName(engineShortName);
         if (_scriptEngine==null){
             throw new InstantiationException("Could not load script engine: "+
                     engineShortName);
         }
         setPermissions(null);
    }
   
    public boolean isCompilable(){
        return _scriptEngine instanceof Compilable;
    }
   
    public void setPermissions(Collection<Permission> permissionCollection){
        Permissions perms = new Permissions();
        perms.add(new RuntimePermission("accessDeclaredMembers"));
        if (permissionCollection!=null){
            for (Permission p : permissionCollection){
                perms.add(p);
            }
        }
        // Cast to Certificate[] required because of ambiguity:
         ProtectionDomain domain = new ProtectionDomain(
                 new CodeSource( null, (Certificate[]) null ), perms );
         _accessControlContext = new AccessControlContext(
                 new ProtectionDomain[] { domain } );
    }
   
    public Object eval(String code){
        return eval(new StringReader(code));
    }
   
    public Object eval(final Reader rdr){
        return AccessController.doPrivileged(new PrivilegedAction(){
            @Override
            public Object run() {
                try {
                    return _scriptEngine.eval(rdr);
                } catch (ScriptException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return null;
            }}, _accessControlContext);
    }
   
    public CompiledScript compile(String str) throws ScriptException{
        return ((Compilable)_scriptEngine).compile(str);
    }
   
    public CompiledScript compile(Reader rdr) throws ScriptException{
        return ((Compilable)_scriptEngine).compile(rdr);
    }
   
    public Object execute(final CompiledScript script){
        return AccessController.doPrivileged(new PrivilegedAction(){
            @Override
            public Object run() {
                try {
                    return script.eval();
                } catch (ScriptException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return null;
            }}, _accessControlContext);
    } 
}


1 comment:

Mostafa Eweda said...

I really love this post.
May you send me a sample code to call this sandbox as I don't know what permissions to allow for the untrusted code.
If your client is open source, I would love to see the code.

mail me at: mostafa.eweda17@gmail.com

THAAAAAAAAAAAAANKS