POTD: Groovy Sandbox

I posted the other day about Groovy’s SecureASTCustomizer and how it is harmful. In the end of that post, I suggested that doing the check dynamically would work better.

So in this installaition of “Project Of The Day”, I went ahead and implemented it. The result is Groovy sandbox.

My main idea is to confine the sandboxed script into its local object graph. The script should be allowed to mutate this graph all it wants, but it’ll be only allowed to include objects of known whitelisted “safe” types (such as String, List, Date etc.), and a few known safe instances, which acts as a bridge between the sandbox and the rest of the world. These bridge objects would have to be written carefully.

To use this, you have to add SandboxTransformer to your CompilerConfiguration first:

def cc = new CompilerConfiguration()
cc.addCompilationCustomizers(new SecureTransformer())
def binding = new Binding();
binding.robot = robot = new Robot();
def sh = new GroovyShell(binding,cc)

And now any script compiled via the resulting shell object will be sandboxed.

When a sandboxed script executes, all of the following operations are intercepted.

  • static/instance method invocation foo.bar(....)
  • object allocation new Foo(...)
  • property access and assignment zot=foo.bar / foo.bar=zot
  • attributes access and assignment zot=foo.@bar / foo.@bar=zot
  • array access and assignment zot=foo[bar] / foo[bar]=zot

To examine those calls and reject some of them, create your own implementation of GroovyInterceptor and registers it to the thread before you start executing the script:

def sandbox = new RobotSandbox()
sandbox.register()
try {
    sh.evaluate("robot.leftArm.move()")  // this is allowed to complete
    sh.evaluate("robot.selfDestruct()")  // no!
} finally {
    sandbox.unregister()
}

See the robot example for a complete example.

Now let’s see if I can get some feedback from real Groovy experts, and see if they’d be willing to take this into Groovy itself…

8 thoughts on “POTD: Groovy Sandbox”

  1. Looks very promising! I’m considering using this inside IDEA. What do you think about it? Will you release this as a library?

  2. Hey, it’s an honor to be helping IntelliJ IDEA, my favorite IDE! Yes, let me release what I have.

    One known TODO list is that it currently doesn’t intercept calls like “a||=b” or “a+=b” or “a%=b”. The way Groovy works make it tedious to intercept them all, so I haven’t done it.

    Any chance you guys can help me out here?

  3. Well, helping is quite possible, if you need it :)

    From what I see, such operators are just the usual method calls like ‘or’ or ‘mod’.

  4. @kohsuke
    Great, thanks! I’ve created an issue in our tracker to use it, which I invite you to watch: http://youtrack.jetbrains.com/issue/IDEA-87317

    I have a question though. I might be stupid, not knowing the basic things, but I can’t find an obvious way to obtain the jar (we don’t use Maven/Ivy/whatever in IDEA, just plain jars). Should I build it from source?

  5. Hi Kohsuke,

    I am the writer of the SecureASTCustomizer stuff and I’ve just been pointed at this by a friend of mine, too bad I missed it. All you wrote about this is true and we should have made this more clear from the docs. Anyway, I wouldn’t consider this harmful as it’s the way it’s supposed to work. I must confess a very bad name for this class, but the idea behind it is to prevent some syntactic constructions at compile time, meaning “securing” the AST. For example, the calc stuff would not allow you to write explicit method calls.

    Note that now that we have static type checking, it would be possible to add a new transformation that goes beyond that, but it would still have flaws.

    Regarding your sandbox, there are still a lot of potential issues. First of them is @Grab and AST transformations that *must* be disallowed. Otherwise, it’s very easy to introduce global AST transforms or constructs like @CompileStatic that would bypass all of your checks. So I’d better use a combination of the two transforms. Even with that, I’m pretty certain there are ways to workaround checks. All we do is making it harder for an attacker to do so. In the end, you will always have to use a security manager: static and runtime checks are not enough.

    In the following weeks I’ll take some time to look closer at your code and see how we can improve it, that could indeed make a nice addition to Groovy core.

    Thanks!

  6. Hi Kohsuke,

    I am the writer of the SecureASTCustomizer stuff and I’ve just been pointed at this by a friend of mine, too bad I missed it. All you wrote about this is true and we should have made this more clear from the docs. Anyway, I wouldn’t consider this harmful because it’s the way it’s supposed to work. I must confess a very bad name for this class, but the idea behind it is to prevent some syntactic constructions at compile time, meaning “securing” the AST. For example, the calc stuff would not allow you to write explicit method calls.

    Note that now that we have static type checking, it would be possible to add a new transformation that goes beyond that, but it would still have flaws.

    Regarding your sandbox, there are still a lot of potential issues. First of them is @Grab and AST transformations that *must* be disallowed. Otherwise, it’s very easy to introduce global AST transforms or constructs like @CompileStatic that would bypass all of your checks. So I’d better use a combination of the two transforms. Even with that, I’m pretty certain there are ways to workaround checks. All we do is making it harder for an attacker to do so. In the end, you will always have to use a security manager: static and runtime checks are not enough.

    In the following weeks I’ll take some time to look closer at your code and see how we can improve it, that could indeed make a nice addition to Groovy core.

    Thanks!

  7. I’m trying to split a String into a String array but I’m the following:
    java.lang.SecurityException: Unexpected type: class [Ljava.lang.String;

    I’ve tried putting [Ljava.lang.String in the groovy-whitelist.txt file but the exception still persists.

    What should I put in the roovy-whitelist.txt file for a this?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>