diff --git a/source/net/sourceforge/filebot/cli/ArgumentProcessor.java b/source/net/sourceforge/filebot/cli/ArgumentProcessor.java index 1cee933d..4f853198 100644 --- a/source/net/sourceforge/filebot/cli/ArgumentProcessor.java +++ b/source/net/sourceforge/filebot/cli/ArgumentProcessor.java @@ -14,7 +14,6 @@ import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import java.security.AccessController; import java.util.Collection; import java.util.LinkedHashSet; import java.util.MissingResourceException; @@ -27,7 +26,6 @@ import javax.script.SimpleBindings; import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.MediaTypes; import net.sourceforge.filebot.StandardRenameAction; -import net.sourceforge.filebot.cli.ScriptShell.Script; import net.sourceforge.filebot.cli.ScriptShell.ScriptProvider; import net.sourceforge.filebot.web.CachedResource; @@ -88,7 +86,7 @@ public class ArgumentProcessor { Bindings bindings = new SimpleBindings(); bindings.put("args", args.getFiles(false)); - DefaultScriptProvider scriptProvider = new DefaultScriptProvider(args.trustScript); + DefaultScriptProvider scriptProvider = new DefaultScriptProvider(); URI script = scriptProvider.getScriptLocation(args.script); if (!scriptProvider.isInlineScheme(script.getScheme())) { @@ -105,7 +103,7 @@ public class ArgumentProcessor { } } - ScriptShell shell = new ScriptShell(cli, args, AccessController.getContext(), scriptProvider); + ScriptShell shell = new ScriptShell(scriptProvider, args.bindings); shell.runScript(script, bindings); } @@ -124,14 +122,8 @@ public class ArgumentProcessor { public static class DefaultScriptProvider implements ScriptProvider { - private final boolean trustRemoteScript; - private URI baseScheme; - public DefaultScriptProvider(boolean trustRemoteScript) { - this.trustRemoteScript = trustRemoteScript; - } - public void setBaseScheme(URI baseScheme) { this.baseScheme = baseScheme; } @@ -145,19 +137,14 @@ public class ArgumentProcessor { } public boolean isInlineScheme(String scheme) { - return "g".equals(scheme) || "system".equals(scheme); + return "g".equals(scheme); } @Override public URI getScriptLocation(String input) throws Exception { try { return new URL(input).toURI(); - } catch (Exception _) { - // system:in - if (input.equals("system:in")) { - return new URI("system", "in", null); - } - + } catch (Exception e) { // g:println 'hello world' if (input.startsWith("g:")) { return new URI("g", input.substring(2), null); @@ -185,31 +172,18 @@ public class ArgumentProcessor { } @Override - public Script fetchScript(URI uri) throws IOException { + public String fetchScript(URI uri) throws IOException { if (uri.getScheme().equals("file")) { - return new Script(readAll(new InputStreamReader(new FileInputStream(new File(uri)), "UTF-8")), true); - } - - if (uri.getScheme().equals("system")) { - return new Script(readAll(new InputStreamReader(System.in)), true); + return readAll(new InputStreamReader(new FileInputStream(new File(uri)), "UTF-8")); } if (uri.getScheme().equals("g")) { - return new Script(uri.getSchemeSpecificPart(), true); + return uri.getSchemeSpecificPart(); } // remote script - String url; - boolean trusted; - String resolver = getResourceTemplate(uri.getScheme()); - if (resolver != null) { - url = String.format(resolver, uri.getSchemeSpecificPart()); - trusted = true; - } else { - url = uri.toString(); - trusted = trustRemoteScript; - } + String url = (resolver != null) ? String.format(resolver, uri.getSchemeSpecificPart()) : uri.toString(); // fetch remote script only if modified CachedResource script = new CachedResource(url, String.class, CachedResource.ONE_DAY) { @@ -219,7 +193,7 @@ public class ArgumentProcessor { return Charset.forName("UTF-8").decode(data).toString(); } }; - return new Script(script.get(), trusted); + return script.get(); } } diff --git a/source/net/sourceforge/filebot/cli/GroovyPad.java b/source/net/sourceforge/filebot/cli/GroovyPad.java index 966975b7..5bc09ab9 100644 --- a/source/net/sourceforge/filebot/cli/GroovyPad.java +++ b/source/net/sourceforge/filebot/cli/GroovyPad.java @@ -16,8 +16,8 @@ import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.security.AccessController; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; @@ -164,10 +164,10 @@ public class GroovyPad extends JFrame { protected ScriptShell createScriptShell() { try { - DefaultScriptProvider scriptProvider = new DefaultScriptProvider(true); + DefaultScriptProvider scriptProvider = new DefaultScriptProvider(); scriptProvider.setBaseScheme(new URI("fn", "%s", null)); - return new ScriptShell(new CmdlineOperations(), ArgumentBean.parse(new String[0]), AccessController.getContext(), scriptProvider); + return new ScriptShell(scriptProvider, new HashMap()); } catch (Exception e) { throw new RuntimeException(e); } @@ -232,15 +232,15 @@ public class GroovyPad extends JFrame { @Override public void run() { try { - result = shell.evaluate(script, new SimpleBindings(), true); + result = shell.evaluate(script, new SimpleBindings()); // print result and make sure to flush Groovy output SimpleBindings binding = new SimpleBindings(); binding.put("result", result); if (result != null) { - shell.evaluate("print('Result: '); println(result);", binding, true); + shell.evaluate("print('Result: '); println(result);", binding); } else { - shell.evaluate("println();", binding, true); + shell.evaluate("println();", binding); } } catch (ScriptException e) { while (e.getCause() instanceof ScriptException) { diff --git a/source/net/sourceforge/filebot/cli/ScriptShell.java b/source/net/sourceforge/filebot/cli/ScriptShell.java index 0df2b46d..194c061b 100644 --- a/source/net/sourceforge/filebot/cli/ScriptShell.java +++ b/source/net/sourceforge/filebot/cli/ScriptShell.java @@ -2,35 +2,18 @@ package net.sourceforge.filebot.cli; import groovy.lang.GroovyClassLoader; -import java.awt.AWTPermission; -import java.io.File; -import java.io.FilePermission; import java.io.InputStreamReader; -import java.lang.management.ManagementPermission; -import java.lang.reflect.ReflectPermission; -import java.net.SocketPermission; import java.net.URI; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permissions; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.ProtectionDomain; -import java.util.LinkedHashMap; import java.util.Map; -import java.util.Map.Entry; -import java.util.PropertyPermission; import java.util.ResourceBundle; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; -import javax.script.SimpleBindings; import javax.script.SimpleScriptContext; import net.sourceforge.filebot.format.ExpressionFormat; -import net.sourceforge.filebot.format.PrivilegedInvocation; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -42,16 +25,26 @@ public class ScriptShell { private final ScriptEngine engine; private final ScriptProvider scriptProvider; - public ScriptShell(CmdlineInterface cli, ArgumentBean args, AccessControlContext acc, ScriptProvider scriptProvider) throws ScriptException { + public ScriptShell(ScriptProvider scriptProvider, Map globals) throws ScriptException { this.engine = createScriptEngine(); this.scriptProvider = scriptProvider; + // setup bindings + Bindings bindings = engine.createBindings(); + bindings.putAll(globals); + + // bind API objects + // TODO remove + bindings.put("_cli", new CmdlineOperations()); + bindings.put("_shell", this); + // setup script context ScriptContext context = new SimpleScriptContext(); - context.setBindings(initializeBindings(cli, args, acc), ScriptContext.GLOBAL_SCOPE); + context.setBindings(bindings, ScriptContext.GLOBAL_SCOPE); engine.setContext(context); // import additional functions into the shell environment + // TODO remove engine.eval(new InputStreamReader(ExpressionFormat.class.getResourceAsStream("ExpressionFormat.lib.groovy"))); engine.eval(new InputStreamReader(ScriptShell.class.getResourceAsStream("ScriptShell.lib.groovy"))); } @@ -74,22 +67,22 @@ public class ScriptShell { return new GroovyScriptEngineImpl(classLoader); } + public Object evaluate(final String script, final Bindings bindings) throws Throwable { + try { + return engine.eval(script, bindings); + } catch (Throwable e) { + while (e.getClass() == ScriptException.class && e.getCause() != null) { + e = e.getCause(); + } + throw StackTraceUtils.deepSanitize(e); // make Groovy stacktrace human-readable + } + } + public static interface ScriptProvider { public URI getScriptLocation(String input) throws Exception; - public Script fetchScript(URI uri) throws Exception; - } - - public static class Script { - - public final String code; - public final boolean trusted; - - public Script(String code, boolean trusted) { - this.code = code; - this.trusted = trusted; - } + public String fetchScript(URI uri) throws Exception; } public Object runScript(String input, Bindings bindings) throws Throwable { @@ -97,90 +90,7 @@ public class ScriptShell { } public Object runScript(URI resource, Bindings bindings) throws Throwable { - Script script = scriptProvider.fetchScript(resource); - return evaluate(script.code, bindings, script.trusted); - } - - public Object evaluate(final String script, final Bindings bindings, boolean trustScript) throws Throwable { - try { - if (trustScript) { - return engine.eval(script, bindings); - } - - try { - return AccessController.doPrivileged(new PrivilegedExceptionAction() { - - @Override - public Object run() throws ScriptException { - return engine.eval(script, bindings); - } - }, getSandboxAccessControlContext()); - } catch (PrivilegedActionException e) { - throw e.getException(); - } - } catch (Throwable e) { - while (e.getClass() == ScriptException.class && e.getCause() != null) { - e = e.getCause(); - } - throw StackTraceUtils.deepSanitize(e); // make Groovy stack human-readable - } - } - - protected Bindings initializeBindings(CmdlineInterface cli, ArgumentBean args, AccessControlContext acc) { - Bindings bindings = new SimpleBindings(); - - // bind external parameters - if (args.bindings != null) { - for (Entry it : args.bindings.entrySet()) { - bindings.put(it.getKey(), it.getValue()); - } - } - - // bind API objects - bindings.put("_cli", PrivilegedInvocation.newProxy(CmdlineInterface.class, cli, acc)); - bindings.put("_script", args.script); - bindings.put("_args", args); - bindings.put("_shell", this); - - Map defines = new LinkedHashMap(); - if (args.bindings != null) { - for (Entry it : args.bindings.entrySet()) { - defines.put(it.getKey(), it.getValue()); - } - } - bindings.put("_def", defines); - - return bindings; - } - - protected AccessControlContext getSandboxAccessControlContext() { - Permissions permissions = new Permissions(); - - permissions.add(new RuntimePermission("createClassLoader")); - permissions.add(new RuntimePermission("accessClassInPackage.*")); - permissions.add(new RuntimePermission("modifyThread")); - permissions.add(new FilePermission("<>", "read")); - permissions.add(new SocketPermission("*", "connect")); - permissions.add(new PropertyPermission("*", "read")); - permissions.add(new RuntimePermission("getenv.*")); - permissions.add(new RuntimePermission("getFileSystemAttributes")); - permissions.add(new ManagementPermission("monitor")); - - // write permissions for temp and cache folders - permissions.add(new FilePermission(new File(System.getProperty("ehcache.disk.store.dir")).getAbsolutePath() + File.separator + "-", "write, delete")); - permissions.add(new FilePermission(new File(System.getProperty("java.io.tmpdir")).getAbsolutePath() + File.separator + "-", "write, delete")); - - // AWT / Swing permissions - permissions.add(new AWTPermission("accessEventQueue")); - permissions.add(new AWTPermission("toolkitModality")); - permissions.add(new AWTPermission("showWindowWithoutWarningBanner")); - - // this is probably a security problem but nevermind - permissions.add(new RuntimePermission("accessDeclaredMembers")); - permissions.add(new ReflectPermission("suppressAccessChecks")); - permissions.add(new RuntimePermission("modifyThread")); - - return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, permissions) }); + return evaluate(scriptProvider.fetchScript(resource), bindings); } } diff --git a/source/net/sourceforge/filebot/cli/ScriptShell.properties b/source/net/sourceforge/filebot/cli/ScriptShell.properties index a1d73db7..57ae861a 100644 --- a/source/net/sourceforge/filebot/cli/ScriptShell.properties +++ b/source/net/sourceforge/filebot/cli/ScriptShell.properties @@ -1,3 +1,3 @@ scriptBaseClass: net.sourceforge.filebot.cli.ScriptShellBaseClass -starImport: net.sourceforge.filebot, net.sourceforge.filebot.util, net.sourceforge.filebot.web, net.sourceforge.filebot.media, net.sourceforge.filebot.mediainfo, net.sourceforge.filebot.hash, net.sourceforge.filebot.similarity, groovy.io, groovy.xml, groovy.json, org.jsoup, java.nio.file, java.nio.file.attribute, java.util.regex +starImport: net.sourceforge.filebot, net.sourceforge.filebot.hash, net.sourceforge.filebot.media, net.sourceforge.filebot.mediainfo, net.sourceforge.filebot.similarity, net.sourceforge.filebot.subtitle, net.sourceforge.filebot.torrent, net.sourceforge.filebot.web, net.sourceforge.filebot.util, groovy.io, groovy.xml, groovy.json, org.jsoup, java.nio.file, java.nio.file.attribute, java.util.regex starStaticImport: net.sourceforge.filebot.WebServices, net.sourceforge.filebot.media.MediaDetection, java.nio.file.Files \ No newline at end of file diff --git a/source/net/sourceforge/filebot/cli/ScriptShellBaseClass.java b/source/net/sourceforge/filebot/cli/ScriptShellBaseClass.java index 67c4b6f8..fe2f9566 100644 --- a/source/net/sourceforge/filebot/cli/ScriptShellBaseClass.java +++ b/source/net/sourceforge/filebot/cli/ScriptShellBaseClass.java @@ -1,10 +1,12 @@ package net.sourceforge.filebot.cli; +import static net.sourceforge.filebot.Settings.*; import static net.sourceforge.filebot.cli.CLILogging.*; import groovy.lang.Closure; import groovy.lang.Script; import java.io.Console; +import java.util.Map; import java.util.logging.Logger; import net.sourceforge.filebot.MediaTypes; @@ -30,6 +32,16 @@ public abstract class ScriptShellBaseClass extends Script { return null; } + // define global variable: _args + public ArgumentBean get_args() { + return getApplicationArguments(); + } + + // define global variable: _def + public Map get_def() { + return getApplicationArguments().bindings; + } + // define global variable: _system public AssociativeScriptObject get_system() { return new AssociativeScriptObject(System.getProperties());