github / codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

Home Page:https://codeql.github.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Writing a query to find declarations of nested structs in Golang.

aaaayush-n opened this issue · comments

Description of the issue

I want to write a query to find the nested structs in an struct in Golang.
Let's say my struct is something like:

type SomeStruct struct {
	field1  int
	field2 dto.NewStruct
	field3  []*statusCode
	field4  map[string]http.Response
	field5  map[category.Category]map[time.Time]*http.Header
	field6  []statusCode
	field7  []*time.Time
	field8  http.Request
	field9  statusCode
}

and I want to write a query that gives me all the nested types in this struct ie. statusCode,Response,Category,Time,Header,NewStruct etc.
The field types can be more complex than what is mentioned here.
My main problem statement is to find the declarations of these nested structs, which I will continue my query by finding all typedecl with their idents matching with the above mentioned idents while matching the import path and the filepath for nested objects from different packages like dto.NewStruct. If there is some better approach please suggest.
Please suggest me an approach.

Hi @aaaayush-n,

It sounds like you're doing the right thing, but it's a bit hard to make suggestions with seeing the query code you currently have. Would you be able to share the current version of your query?

@jketema

/**
 * @id go/get-location
 * @kind problem
 * @problem.severity recommendation
 */

 import go


class MyFunc extends Decl{
    TypeDecl typeDecl;
    Ident ident;
    
    TypeDecl getObject(string objectName,string objDirPath){
        ident.getAPrimaryQlClass()="TypeName"
        and ident.toString()=objectName
        and ident.getParent().getParent()=typeDecl
        and typeDecl.getFile().toString().replaceAll(typeDecl.getFile().getBaseName().toString(), "").substring(0, typeDecl.getFile().toString().replaceAll(typeDecl.getFile().getBaseName().toString(), "").length()-1)=objDirPath
        and
        result=typeDecl
    }
}


from string objectName
, MyFunc func
,TypeDecl object
,string objDirPath
,FieldDecl fieldDecl

where objectName="MyStruct"
and objDirPath="path/to/src/directory"
and object=func.getObject(objectName,objDirPath)

select object,"MyObject"

Now I want to find the nested objects inside TypeDecl object
I was thinking of adding

ident.getAPrimaryQLClass()="TypeName"
and ident.getEnclosingTypeDecl()=object

select ident,"MyNestedObjects"

but there is no such getEnclosingTypeDecl() predicate, I can't do ident.getParent()=object because idk how deep in AST my ident will be.

Suggest me some way to find out how to figure this out.

Hi @aaaayush-n 👋

Thanks for sharing your query! What you have written looks a bit unusual and I am guessing you are coming from a background of object-oriented and imperative programming languages. If you haven't seen our documentation for the QL language yet, I'd recommend having a look for lots of advice for how to write good QL. In particular, you may find the section on recursion relevant to your question.

The getAPrimaryQLClass predicate is mainly intended for debugging, not for actual queries. For example, you don't need the MyFunc class and your getObject predicate could be simplified to:

TypeDecl getObject(string objectName,string objDirPath){
  exists(TypeName tn | 
    tn.toString()=objectName and
    tn.getParent().getParent()=result and
    result.getFile().toString().replaceAll(result.getFile().getBaseName().toString(), "").substring(0, result.getFile().toString().replaceAll(result.getFile().getBaseName().toString(), "").length()-1)=objDirPath
  )
}

Use concrete types rather than strings whenever possible. This can probably be further simplified as well, particularly the last line.

In any case, the way I would break this problem down is to start with a predicate which, given some TypeDecl, finds all the TypeNames you are interested in. Something like:

predicate isUsedInTypeDecl(TypeDecl decl, TypeName ty) {
  result = ty.getParent()
}

You can then extend/modify this to find all TypeNames with a corresponding TypeDecl of their own. From there, you can apply the predicate recursively.

@mbg

predicate isUsedInTypeDecl(TypeDecl decl, TypeName ty) {
  result = ty.getParent()
}

This would only give me the statusCode in field9 in context of example struct I gave above (that too on having ty.getParent().getParent().getParent() as the AST structure is TypeName>FieldDecl>StructTypeExpr>TypeDecl)
I fail to see, how I would call this recursively to get the other types.
Please help me with this.

The definition I gave for isUsedInTypeDecl is just an example. You can tweak it to suit your needs. You can use the resulting TypeNames for the initial TypeDecl to find TypeNames that have corresponding TypeDecls, which you can then recurse on.