title: Should one use a custom SecurityManager to sandbox javax.script.ScriptEngine?
body: <p>A big issue with the <a href="http://docs.oracle.com/javase/7/docs/api/javax/script/package-summary.html" rel="noreferrer">javax.script</a> package of <a href="http://jcp.org/aboutJava/communityprocess/final/jsr223/index.html" rel="noreferrer">JSR-223</a> is the lack of any obvious way to sandbox the script that gets run. So the obvious question is: How do you sandbox JSR-223 scripts? That has been asked and there have even been a few attempts at answers.</p>
  
  <p>Here are two interesting questions that ask about this problem but unfortunately miss the point:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/q/1399505/2684359">Sandboxing JSR-223</a></li>
  <li><a href="https://stackoverflow.com/q/3160678/2684359">Sandboxing with javax.script</a></li>
  </ul>
  
  <p>The point is that this isn't simply a matter of setting up the right security policy or using the right ClassLoader because the code that you are trying to secure isn't Java code and it has no class. You can try to secure the ScriptEngine by using a ClassLoader to give it a special ProtectionDomain, but that only works as long as the system ClassLoader can't find the ScriptEngine to defeat your efforts by loading the class with the wrong ProtectionDomain, which is bound to happen with any ScriptEngine that ever gets helpfully included as part of the JRE.</p>
  
  <p>Here's another resource that looks good but misses the point: <a href="http://calumleslie.blogspot.ca/2008/06/simple-jvm-sandboxing.html" rel="noreferrer">Simple JVM sandboxing</a>. It tells the unwary that they can sandbox their scripts by using <code>doPrivileged</code> in a custom AccessControlContext that contains a ProtectionDomain with very few Permissions, but of course doing that is meaningless because <code>doPrivileged</code> is only useful for gaining Permissions, and not useful for denying Permissions. If the untrusted code is already in a sandbox ProtectionDomain then the <code>doPrivileged</code> trick won't do anything at all, and if the untrusted code is in a non-sandbox ProtectionDomain then it can just call <code>doPrivileged</code> and completely bypass the sandboxing attempt.</p>
  
  <p>The <em>real</em> question is how does one work around these problems? Assuming we intend to use ProtectionDomains, it seems that the only option is to give ScriptEngineManager a custom ClassLoader that deliberately shadows some classes from the system ClassLoader. In that case, how do we decide which classes to put into the sandbox and which to get from the system ClassLoader? There doesn't seem to be any reliable way of knowing which classes might be responsible for giving the script the means to break out of the sandbox, especially for ScriptEngines that don't exist yet.</p>
  
  <p>The only alternative that I can think of is what I really want to ask about. Would it be a better solution to simply ignore ProtectionDomains and implement a custom SecurityManager that has a sandbox mode for evaluating scripts? For example:</p>
  
  <pre><code>public final class SandboxMan extends SecurityManager {
      private int sandboxDepth = 0;
      @Override public void checkPermission(Permission permission) {
          if(sandboxDepth &gt; 0) throw new SecurityException("Sandboxed: " + permission);
          else super.checkPermission(permission);
      }
      @Override public void checkPermission(Permission permission, Object context) {
          if(sandboxDepth &gt; 0) throw new SecurityException("Sandboxed: " + permission);
          else super.checkPermission(permission, context);
      }
      public Object eval(ScriptEngine engine, String script) throws ScriptException {
          if(sandboxDepth == Integer.MAX_VALUE) throw new SecurityException("Sandbox depth");
          sandboxDepth++;
          try {
              return engine.eval(script);
          } finally { sandboxDepth--; }
      }
  }
  </code></pre>
  
  <p>That looks tricky and dangerous. It's dangerous to try to be tricky when security is involved, but could this really be the best solution given the situation?</p>