如何使用Groovy的SecureASTCustomizer限制File类创建删除等文件操作
你之前的代码不生效是因为你配置的表达式黑名单仅匹配变量名恰好为file的实例调用delete方法,无法覆盖全限定名创建的File实例、其他变量名的File对象调用,也拦截不了new File()这类构造调用。要精准限制特定类的指定方法,可通过自定义CompilationCustomizer实现编译期拦截,具体方案如下:
实现原理
通过自定义Groovy编译期自定义器,在编译阶段遍历所有语法节点,拦截所有java.io.File类的实例化操作和指定方法调用,无论是否用全限定名调用都会被拦截。
完整实现代码
import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.expr.* import org.codehaus.groovy.control.* import org.codehaus.groovy.ast.ClassCodeVisitorSupport class FileOperationRestrictCustomizer extends CompilationCustomizer { FileOperationRestrictCustomizer() { // 指定在编译的规范化阶段执行拦截 super(CompilePhase.CANONICALIZATION) } @Override void call(SourceUnit source, GeneratorContext context, ClassNode classNode) { // 遍历类的所有代码节点 classNode.allCode.each { codeNode -> codeNode.visit(new ClassCodeVisitorSupport() { @Override protected SourceUnit getSourceUnit() { source } // 拦截实例方法调用 @Override void visitMethodCallExpression(MethodCallExpression call) { ClassNode receiverType = call.objectExpression.type // 匹配File类的高危方法 if (receiverType?.name == 'java.io.File' && call.methodAsString in ['delete', 'createNewFile', 'mkdir', 'mkdirs', 'deleteOnExit', 'renameTo']) { addError("禁止调用File类的${call.methodAsString}高危方法", call) } super.visitMethodCallExpression(call) } // 拦截构造器调用(禁止new File()) @Override void visitConstructorCallExpression(ConstructorCallExpression call) { if (call.type.name == 'java.io.File') { addError("禁止实例化java.io.File类", call) } super.visitConstructorCallExpression(call) } // 可选:拦截NIO、commons-io等工具类的文件操作 @Override void visitStaticMethodCallExpression(StaticMethodCallExpression call) { if (call.ownerType.name in ['java.nio.file.Files', 'org.apache.commons.io.FileUtils']) { addError("禁止调用${call.ownerType.name}类的文件操作方法", call) } super.visitStaticMethodCallExpression(call) } }) } } }
使用示例
// 配置编译器参数 def compilerConfig = new CompilerConfiguration() compilerConfig.addCompilationCustomizers(new FileOperationRestrictCustomizer()) // 创建GroovyShell时传入配置 def groovyShell = new GroovyShell(compilerConfig) // 测试全限定名调用,会被拦截 try { groovyShell.evaluate('new java.io.File("/tmp/test.txt").delete()') } catch (MultipleCompilationErrorsException e) { println "拦截到非法操作:${e.errorCollector.errors[0].message}" } // 测试导入后调用,会被拦截 try { groovyShell.evaluate(''' import java.io.File def testFile = new File("./test.log") testFile.createNewFile() ''') } catch (MultipleCompilationErrorsException e) { println "拦截到非法操作:${e.errorCollector.errors[0].message}" }
扩展说明
- 可根据业务需求调整拦截的方法列表,新增其他需要限制的File类操作
- 如需放行指定路径的文件操作,可在判断逻辑中增加参数校验,仅拦截路径不在白名单内的操作
- 若需要运行时动态判断权限,可使用Groovy Sandbox第三方库实现运行时方法调用拦截,无需依赖编译期处理
内容的提问来源于stack exchange,提问作者jignesh Kanjariya
相关产品推荐
相关产品推荐

