如何遍历AST树

时间:2016-11-04 20:27:16

标签: groovy abstract-syntax-tree

我正在尝试为Groovy创建静态分析。作为我的上司的POC,我只是试图解析简单的代码并检测SQL注入,这是最容易发现的。我在Python上做得很成功,这是我的主要语言,但我的公司主要使用Grails(在Groovy上)。

这是我到目前为止所做的:

import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.ast.CodeVisitorSupport
import org.codehaus.groovy.ast.builder.AstBuilder

public class SecurityCheck extends CodeVisitorSupport {
    void visitBlockStatement(BlockStatement statement) {
        println "NEW BLOCK STATEMENT:"
        println statement.getText();
        //keep walking...
        statement.getStatements().each { ASTNode child ->
            println "CHILD FOUND: "
            println child.getText();
            child.visit(this)
        }
    }
}

def code = new File('groovy_source.groovy').text // get the code from the source file
def AstBuilder astBuilder = new AstBuilder()  // build an instance of the ast builder
def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, code) // build from string when the compiler converts from tokens to AST
def SecurityCheck securityCheck = new SecurityCheck()  // create an instance of our security check class
println ast
println ast[0]
ast[0].visit(securityCheck)

groovy_source.groovy文件非常简单,只包含一个极易发现漏洞的最小文件:

def post(id) {
   query = "SELECT * FROM table WHERE id = " + id;
   result = sql.execute query
   return result;
}

我的理解是,当我继承CodeVisitorSupport时,这只会访问一个BlockStatement,然后,对于该语句中的每个语句,它将使用来自supper类的方法访问它。

然而,当我从BlockStatement打印文本时,它是一个空字符串,并且for each方法甚至都没有被调用(我认为这必须意味着AST找不到我的块语句的子代,即使函数最终也是如此)里面有陈述。

[org.codehaus.groovy.ast.stmt.BlockStatement@363a52f[]]  // println ast
org.codehaus.groovy.ast.stmt.BlockStatement@363a52f[]  // println ast[0]
NEW BLOCK STATEMENT:
{  }  // println statement.getText()

这里的任何帮助都将非常感激。谢谢!

1 个答案:

答案 0 :(得分:0)

我找到了答案。最后我并不那么努力,但可怕的文档并不容易。如果您要遍历树,则需要为构造函数提供false boolean作为第二个参数,如下所示:

def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, false, code)

然后您可以按预期使用visit *方法。