如何在 Groovy 脚本中通过 @Grab 禁止依赖
How to disallow dependencies via @Grab in Groovy script
我想允许用户在我的 Java 服务器应用程序中 运行 他们的 Groovy 脚本,但我也想禁止他们使用 @Grab
添加任何随机依赖项。
是的,我可以通过在源代码中搜索和替换来简单地删除所有 @Grab
注释,但最好以更优雅的方式执行此操作,例如仅允许批准的依赖项。
是的,我知道这个问题的最佳解决方案是 JVM 的 SecurityManager
。
有多种方法,例如 Groovy Sandbox,它可能比您将要看到的更好。
import groovy.grape.Grape
Grape.metaClass.static.grab = {String endorsed ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map args, Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
def source1 = '''
println('This is a nice safe Groovy script.')
'''
def source2 = '''
@Grab('commons-validator:commons-validator:1.4.1')
import org.apache.commons.validator.routines.EmailValidator
def emailValidator = EmailValidator.getInstance();
assert emailValidator.isValid('what.a.shame@us.elections.gov')
assert !emailValidator.isValid('an_invalid_emai_address')
println 'You should not see this message!'
'''
def script
def shell = new GroovyShell()
try {
script = shell.parse(source1)
script.run()
} catch (Exception e) {
assert false, "Oh, oh. That wasn't supposed to happen :("
}
try {
script = shell.parse(source2)
assert false, "Oh, oh. That wasn't supposed to happen :("
} catch (ExceptionInInitializerError e) {
println 'Naughty script was blocked when parsed.'
}
上面的例子演示了如何阻止@Grab。它不是通过阻塞注解来做到这一点,而是通过覆盖注解添加的方法调用:groovy.grape.Grape.grab()。
Grape.metaClass.static.grab = {String endorsed ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map args, Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
这是 顽皮的 脚本,由 Groovy 控制台 AST 查看器剖析:
@groovy.lang.Grab(module = 'commons-validator', group = 'commons-validator', version = '1.4.1')
import org.apache.commons.validator.routines.EmailValidator as EmailValidator
public class script1440223706571 extends groovy.lang.Script {
private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo
public static transient boolean __$stMC
public script1440223706571() {
}
public script1440223706571(groovy.lang.Binding context) {
super(context)
}
public static void main(java.lang.String[] args) {
org.codehaus.groovy.runtime.InvokerHelper.runScript(script1440223706571, args)
}
public java.lang.Object run() {
java.lang.Object emailValidator = org.apache.commons.validator.routines.EmailValidator.getInstance()
assert emailValidator.isValid('what.a.shame@us.elections.gov') : null
assert !(emailValidator.isValid('an_invalid_emai_address')) : null
return null
}
static {
groovy.grape.Grape.grab([:], ['group': 'commons-validator', 'module': 'commons-validator', 'version': '1.4.1'])
}
protected groovy.lang.MetaClass $getStaticMetaClass() {
}
}
在这里您可以看到在静态初始化程序中对 Grape.grab() 的调用。要添加依赖项的细粒度过滤,您可以内省依赖项和背书参数。
依赖性
['group': 'commons-validator', 'module': 'commons-validator', 'version': '1.4.1']
认可
公共验证器:公共验证器:1.4.1
修订实施
这个新实现使用拦截器来 block/allow 抓取葡萄。
import groovy.grape.GrapeIvy
def source1 = '''
println('This is a nice safe Groovy script.')
'''
def source2 = '''
@Grab('commons-validator:commons-validator:1.4.1')
import org.apache.commons.validator.routines.EmailValidator
def emailValidator = EmailValidator.getInstance();
assert emailValidator.isValid('what.a.shame@us.elections.gov')
assert !emailValidator.isValid('an_invalid_emai_address')
println 'You should not see this message!'
'''
def script
def shell = new GroovyShell()
def proxy = ProxyMetaClass.getInstance(GrapeIvy)
proxy.interceptor = new GrapeInterceptor({group, module, version ->
if(group == 'commons-validator' && module == 'commons-validator') false
else true
})
proxy.use {
shell.parse(source1).run()
try {
shell.parse(source2).run()
} catch (org.codehaus.groovy.control.MultipleCompilationErrorsException e) {
assert e.message.contains('unable to resolve class')
}
}
@groovy.transform.TupleConstructor
class GrapeInterceptor implements Interceptor {
private boolean invokeMethod = true
Closure authorizer
def afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
invokeMethod = true
return result
}
def beforeInvoke(Object object, String methodName, Object[] arguments) {
if(methodName == 'createGrabRecord') {
def dependencies = arguments[0]
invokeMethod = authorizer(dependencies.group, dependencies.module, dependencies.version)
} else {
invokeMethod = true
}
return null
}
boolean doInvoke() { invokeMethod }
}
GrapeInterceptor 构造函数将闭包作为其唯一参数。有了这个 Closure,你可以很容易地决定是否允许 Grab 发生 :)
例如,如果 Grab 看起来像这样:@Grab('commons-validator:commons-validator:1.4.1')
闭包的参数分配如下:
- 组:commons-validator
- 模块:通用验证器
- 版本:1.4.1
要允许抓取,闭包必须 return 为真。 Return false 阻止它。
我想允许用户在我的 Java 服务器应用程序中 运行 他们的 Groovy 脚本,但我也想禁止他们使用 @Grab
添加任何随机依赖项。
是的,我可以通过在源代码中搜索和替换来简单地删除所有 @Grab
注释,但最好以更优雅的方式执行此操作,例如仅允许批准的依赖项。
是的,我知道这个问题的最佳解决方案是 JVM 的 SecurityManager
。
有多种方法,例如 Groovy Sandbox,它可能比您将要看到的更好。
import groovy.grape.Grape
Grape.metaClass.static.grab = {String endorsed ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map args, Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
def source1 = '''
println('This is a nice safe Groovy script.')
'''
def source2 = '''
@Grab('commons-validator:commons-validator:1.4.1')
import org.apache.commons.validator.routines.EmailValidator
def emailValidator = EmailValidator.getInstance();
assert emailValidator.isValid('what.a.shame@us.elections.gov')
assert !emailValidator.isValid('an_invalid_emai_address')
println 'You should not see this message!'
'''
def script
def shell = new GroovyShell()
try {
script = shell.parse(source1)
script.run()
} catch (Exception e) {
assert false, "Oh, oh. That wasn't supposed to happen :("
}
try {
script = shell.parse(source2)
assert false, "Oh, oh. That wasn't supposed to happen :("
} catch (ExceptionInInitializerError e) {
println 'Naughty script was blocked when parsed.'
}
上面的例子演示了如何阻止@Grab。它不是通过阻塞注解来做到这一点,而是通过覆盖注解添加的方法调用:groovy.grape.Grape.grab()。
Grape.metaClass.static.grab = {String endorsed ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
Grape.metaClass.static.grab = {Map args, Map dependency ->
throw new SecurityException("Oh no you didn't! Grabbing is forbidden.")
}
这是 顽皮的 脚本,由 Groovy 控制台 AST 查看器剖析:
@groovy.lang.Grab(module = 'commons-validator', group = 'commons-validator', version = '1.4.1')
import org.apache.commons.validator.routines.EmailValidator as EmailValidator
public class script1440223706571 extends groovy.lang.Script {
private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo
public static transient boolean __$stMC
public script1440223706571() {
}
public script1440223706571(groovy.lang.Binding context) {
super(context)
}
public static void main(java.lang.String[] args) {
org.codehaus.groovy.runtime.InvokerHelper.runScript(script1440223706571, args)
}
public java.lang.Object run() {
java.lang.Object emailValidator = org.apache.commons.validator.routines.EmailValidator.getInstance()
assert emailValidator.isValid('what.a.shame@us.elections.gov') : null
assert !(emailValidator.isValid('an_invalid_emai_address')) : null
return null
}
static {
groovy.grape.Grape.grab([:], ['group': 'commons-validator', 'module': 'commons-validator', 'version': '1.4.1'])
}
protected groovy.lang.MetaClass $getStaticMetaClass() {
}
}
在这里您可以看到在静态初始化程序中对 Grape.grab() 的调用。要添加依赖项的细粒度过滤,您可以内省依赖项和背书参数。
依赖性
['group': 'commons-validator', 'module': 'commons-validator', 'version': '1.4.1']
认可
公共验证器:公共验证器:1.4.1
修订实施
这个新实现使用拦截器来 block/allow 抓取葡萄。
import groovy.grape.GrapeIvy
def source1 = '''
println('This is a nice safe Groovy script.')
'''
def source2 = '''
@Grab('commons-validator:commons-validator:1.4.1')
import org.apache.commons.validator.routines.EmailValidator
def emailValidator = EmailValidator.getInstance();
assert emailValidator.isValid('what.a.shame@us.elections.gov')
assert !emailValidator.isValid('an_invalid_emai_address')
println 'You should not see this message!'
'''
def script
def shell = new GroovyShell()
def proxy = ProxyMetaClass.getInstance(GrapeIvy)
proxy.interceptor = new GrapeInterceptor({group, module, version ->
if(group == 'commons-validator' && module == 'commons-validator') false
else true
})
proxy.use {
shell.parse(source1).run()
try {
shell.parse(source2).run()
} catch (org.codehaus.groovy.control.MultipleCompilationErrorsException e) {
assert e.message.contains('unable to resolve class')
}
}
@groovy.transform.TupleConstructor
class GrapeInterceptor implements Interceptor {
private boolean invokeMethod = true
Closure authorizer
def afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
invokeMethod = true
return result
}
def beforeInvoke(Object object, String methodName, Object[] arguments) {
if(methodName == 'createGrabRecord') {
def dependencies = arguments[0]
invokeMethod = authorizer(dependencies.group, dependencies.module, dependencies.version)
} else {
invokeMethod = true
}
return null
}
boolean doInvoke() { invokeMethod }
}
GrapeInterceptor 构造函数将闭包作为其唯一参数。有了这个 Closure,你可以很容易地决定是否允许 Grab 发生 :)
例如,如果 Grab 看起来像这样:@Grab('commons-validator:commons-validator:1.4.1')
闭包的参数分配如下:
- 组:commons-validator
- 模块:通用验证器
- 版本:1.4.1
要允许抓取,闭包必须 return 为真。 Return false 阻止它。