joaquinlpereyra joaquinlpereyra - 10 months ago 73
Groovy Question

How to traverse AST tree

I'm trying to create an static analysis for Groovy. As a POC for my superiors I'm just trying to parse simple code and detect SQL injections, which are the easiest kind to spot. I did it successfully on Python, which is my main language, but my company mostly uses Grails (on Groovy).

This is what I have so far:

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 statement.getText();
//keep walking...
statement.getStatements().each { ASTNode child ->
println "CHILD FOUND: "
println child.getText();

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]

The groovy_source.groovy file is very simple, containing only a minimal file with a super easy to spot vulnerability:

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

It is my understanding that, as I'm inheriting from CodeVisitorSupport, this would just visit a BlockStatement and then, for each statement inside that statement, it would visit it using the method from the supper class.

Nevertheless, when I print the text from the BlockStatement, it is an empty string, and the for each method never even gets called (which I assume must mean the AST found no children for my block statement, even when the function definitively has statements inside it.

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

Any help here would be tremendously appreciated. Thanks!

Answer Source

I found the answer. I wasn't so hard in the end, but the horrible documentation doesn't make it easy. If you one to traverse the tree, you need to give the constructor the false boolean as a second argument, like this:

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

Then you can use the visit* methods as you expect.