blob: becff49ca5d225a1a0caf5fa77c5c895bd831ede [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.codehaus.groovy.control.customizers.SecureASTCustomizer
import org.codehaus.groovy.control.messages.ExceptionMessage
import org.codehaus.groovy.ast.expr.MethodPointerExpression
/**
* The blacklisting shell is similar to a GroovyShell in that it can evaluate text as
* code and return a result. It is intended as an example of using blacklisting to prevent
* running methods on a class - in this case, java.lang.System. Please note that in creating
* any secure environment, there is no substitution for using a SecurityManager.
*
* Amoung the many different calls this class prevents are:
* System.exit(0)
* Eval.me("System.exit(0)")
* evaluate("System.exit(0)")
* (new GroovyShell()).evaluate("System.exit(0)")
* Class.forName("java.lang.System").exit(0)
* System.&exit.call(0)
* System.getMetaClass().invokeMethod("exit",0)
* def s = System; s.exit(0)
* Script t = this; t.evaluate("System.exit(0)")
*
* The restrictions required, however, also prevent the following code from working:
* println "test"
* def s = "test" ; s.count("t")
*
* @author Jim Driscoll (jamesgdriscoll@gmail.com)
*/
class BlacklistingShell {
/**
* Compiles the text into a Groovy object and then executes it, returning the result.
* Prevents calling any method on java.lang.System within the VM
* @param text
* the script to evaluate typed as a string
* @throws SecurityException
* most likely the script is doing something other than arithmetic
* @throws IllegalStateException
* if the script returns something other than a number
*/
def evaluate(String text) {
try {
final SecureASTCustomizer secure = new SecureASTCustomizer()
secure.with {
receiversClassesBlackList = [
Object,
Script,
GroovyShell,
Eval,
System,
].asImmutable()
expressionsBlacklist = [MethodPointerExpression].asImmutable()
}
CompilerConfiguration config = new CompilerConfiguration()
config.addCompilationCustomizers(secure)
GroovyClassLoader loader = new GroovyClassLoader(this.class.classLoader, config)
Class clazz = loader.parseClass(text)
Script script = (Script) clazz.newInstance();
Object result = script.run()
return result
} catch (SecurityException ex) {
throw new SecurityException("Could not evaluate script: $text", ex)
} catch (MultipleCompilationErrorsException mce) {
//this allows compilation errors to be seen by the user
mce.errorCollector.errors.each {
if (it instanceof ExceptionMessage && it.cause instanceof SecurityException) {
throw it.cause
}
}
throw mce
}
}
}