Groovy is a very cool dynamic language for the JVM. Because it runs on the JVM, it also has the great security features as well.
Let's see how we can run trusted code and allow a dynamic (possibly user defined) script to execute with limited permissions. We will also see how the script can call functions in our trusted code and how we can elevate privileges to allow the untrusted script to get access to trusted data.
Setting up a security policy
We are going to create a java policy file that gives our script and groovy full access, but no permissions for our untrusted scripts
~/.java.policy
grant codeBase "file:/Users/chris/securegroovy/trusted.groovy" {
permission java.security.AllPermission;
};
grant codeBase "file:${groovy.home}/-" {
permission java.security.AllPermission;
};
grant codeBase "file:/restrictedScript" {
};
Trusted Script
Now we are going to create our Trusted script, which will evaluate our untrusted script in a sandbox.
trusted.groovy
package com.cmoos
import java.security.*
class TrustedScript {
public static int main(String[] args) {
println("Checking to see if security manager is enabled...")
if(System.getSecurityManager() == null) {
println("Security manager not enabled, using default.")
System.setSecurityManager(new SecurityManager())
}
else {
println("Security manager enabled.")
}
/* Create a shell with a restricted code base */
def binding = new Binding([trusted: TrustedScript])
def shell = new GroovyShell(binding)
def codeSource = new GroovyCodeSource(new File("untrusted.groovy").getText(), "UntrustedScript", "/restrictedScript")
shell.evaluate(codeSource)
return 0
}
public static String getSystemProperty(String name) {
println("Raising privileges to access system property...")
return AccessController.doPrivileged({ -> System.getProperty(name) } as PrivilegedAction)
}
}
We specify the code base to assign to the script, which will inherit the permissions in our security policy.
We also pass in our class into the script's binding, allowing it to call our methods. This let's us specify an API for the script to use to access trusted data, which we control access to.
By using AccessController.doPrivileged(), we can elevate our privileges to that of the calling code base.
Untrusted Script
untrusted.groovy
println("I'm in a sandbox...let's see if I can access a system property")
try {
System.getProperty("java.home")
}
catch(java.security.AccessControlException e) {
println("Couldn't get property: ${e.getMessage()}")
}
/* Call our trusted code (which is accessible through a binding) and get the property */
println("Calling trusted code to get property...")
println("java.home: ${trusted.getSystemProperty("java.home")}")
This code is executed in a sandbox, with permissions defined in our security policy. You can see that if we try to do something we don't have permission for, access is denied.
By calling our trusted class, which has all permissions, we can give the script access to certain things, but we control it.
Running the example
$ groovy trusted.groovy
Checking to see if security manager is enabled...
Security manager not enabled, using default.
I'm in a sandbox...let's see if I can access a system property
Couldn't get property: access denied (java.util.PropertyPermission java.home read)
Calling trusted code to get property...
Raising privileges to access system property...
java.home: /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home