diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/Extensions.kt b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/Extensions.kt index 4cf7574c0d..72ff4ba2af 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/Extensions.kt +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/Extensions.kt @@ -158,3 +158,24 @@ fun Node.followPrevEOG(predicate: (PropertyEdge<*>) -> Boolean): List Boolean): MutableList? { + val path = mutableListOf() + + for (prev in this.prevDFG) { + path.add(prev) + + if (predicate(prev)) { + return path + } + + val subPath = prev.followPrevDFG(predicate) + if (subPath != null) { + path.addAll(subPath) + } + + return path + } + + return null +} diff --git a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.java b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.java index 68a854ee13..4df9372489 100644 --- a/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.java +++ b/cpg-core/src/main/java/de/fraunhofer/aisec/cpg/graph/statements/expressions/InitializerListExpression.java @@ -62,6 +62,9 @@ public void addInitializer(Expression initializer) { var edge = new PropertyEdge<>(this, initializer); edge.addProperty(Properties.INDEX, this.initializers.size()); + initializer.registerTypeListener(this); + this.addPrevDFG(initializer); + this.initializers.add(edge); } diff --git a/cpg-language-go/src/main/golang/expressions.go b/cpg-language-go/src/main/golang/expressions.go index 79ff70e2b2..43189f0d24 100644 --- a/cpg-language-go/src/main/golang/expressions.go +++ b/cpg-language-go/src/main/golang/expressions.go @@ -339,6 +339,10 @@ func (c *ConstructExpression) AddArgument(e *Expression) { (*jnigi.ObjectRef)(c).CallMethod(env, "addArgument", jnigi.Void, (*jnigi.ObjectRef)(e).Cast("de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression")) } +func (c *ConstructExpression) AddPrevDFG(n *Node) { + (*jnigi.ObjectRef)(c).CallMethod(env, "addPrevDFG", jnigi.Void, (*jnigi.ObjectRef)(n).Cast("de/fraunhofer/aisec/cpg/graph/Node")) +} + func (n *NewExpression) SetInitializer(e *Expression) (err error) { _, err = (*jnigi.ObjectRef)(n).CallMethod(env, "setInitializer", jnigi.Void, (*jnigi.ObjectRef)(e).Cast("de/fraunhofer/aisec/cpg/graph/statements/expressions/Expression")) diff --git a/cpg-language-go/src/main/golang/frontend/handler.go b/cpg-language-go/src/main/golang/frontend/handler.go index acfa369a3a..5058e115fb 100644 --- a/cpg-language-go/src/main/golang/frontend/handler.go +++ b/cpg-language-go/src/main/golang/frontend/handler.go @@ -1098,18 +1098,22 @@ func (this *GoLanguageFrontend) handleCompositeLit(fset *token.FileSet, lit *ast c := cpg.NewConstructExpression(fset, lit) // parse the type field, to see which kind of expression it is - var reference = this.handleExpr(fset, lit.Type) + var typ = this.handleType(lit.Type) - if reference == nil { - return nil + if typ != nil { + (*cpg.Node)(c).SetName((*cpg.Node)(typ).GetName()) + (*cpg.Expression)(c).SetType(typ) } - (*cpg.Node)(c).SetName(reference.GetName()) - l := cpg.NewInitializerListExpression(fset, lit) c.AddArgument((*cpg.Expression)(l)) + // Normally, the construct expression would not have DFG edge, but in this case we are mis-using it + // to simulate an object literal, so we need to add a DFG here, otherwise a declaration is disconnected + // from its initialization. + c.AddPrevDFG((*cpg.Node)(l)) + for _, elem := range lit.Elts { expr := this.handleExpr(fset, elem) diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index 3f82cde26d..65953fb972 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -28,9 +28,7 @@ package de.fraunhofer.aisec.cpg.frontends.golang import de.fraunhofer.aisec.cpg.BaseTest import de.fraunhofer.aisec.cpg.ExperimentalGolang import de.fraunhofer.aisec.cpg.TestUtils -import de.fraunhofer.aisec.cpg.graph.body -import de.fraunhofer.aisec.cpg.graph.bodyOrNull -import de.fraunhofer.aisec.cpg.graph.byNameOrNull +import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* @@ -44,6 +42,82 @@ import kotlin.test.assertTrue @ExperimentalGolang class GoLanguageFrontendTest : BaseTest() { + @Test + fun testArrayCompositeLiteral() { + val topLevel = Path.of("src", "test", "resources", "golang") + val tu = + TestUtils.analyzeAndGetFirstTU( + listOf(topLevel.resolve("values.go").toFile()), + topLevel, + true + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } + assertNotNull(tu) + + val p = tu.byNameOrNull("p") + assertNotNull(p) + + val main = p.byNameOrNull("main") + + assertNotNull(main) + + val message = + main.bodyOrNull(2)?.singleDeclaration as? VariableDeclaration + + assertNotNull(message) + + val map = + ((message.initializer as? ConstructExpression)?.arguments?.firstOrNull() as? + InitializerListExpression) + + assertNotNull(map) + + val nameEntry = map.initializers.firstOrNull() as? KeyValueExpression + + assertNotNull(nameEntry) + + assertEquals("string[]", (nameEntry.value as? ConstructExpression)?.name) + } + + @Test + fun testDFG() { + val topLevel = Path.of("src", "test", "resources", "golang") + val tu = + TestUtils.analyzeAndGetFirstTU( + listOf(topLevel.resolve("dfg.go").toFile()), + topLevel, + true + ) { + it.registerLanguage( + GoLanguageFrontend::class.java, + GoLanguageFrontend.GOLANG_EXTENSIONS + ) + } + assertNotNull(tu) + + val p = tu.byNameOrNull("p") + assertNotNull(p) + + val main = p.byNameOrNull("main") + + assertNotNull(main) + + val data = main.bodyOrNull(0)?.singleDeclaration + + assertNotNull(data) + + // We should be able to follow the DFG backwards from the declaration to the individual + // key/value expressions + val path = data.followPrevDFG { it is KeyValueExpression } + + assertNotNull(path) + assertEquals(4, path.size) + } + @Test fun testConstruct() { val topLevel = Path.of("src", "test", "resources", "golang") diff --git a/cpg-language-go/src/test/resources/golang/dfg.go b/cpg-language-go/src/test/resources/golang/dfg.go new file mode 100644 index 0000000000..67d8a787fd --- /dev/null +++ b/cpg-language-go/src/test/resources/golang/dfg.go @@ -0,0 +1,6 @@ +package p + +func main() int { + data := &Data{Name: name} + db.Create(data) +} \ No newline at end of file diff --git a/cpg-language-go/src/test/resources/golang/values.go b/cpg-language-go/src/test/resources/golang/values.go new file mode 100644 index 0000000000..b0e791047c --- /dev/null +++ b/cpg-language-go/src/test/resources/golang/values.go @@ -0,0 +1,13 @@ +package p + +import "net/url" + +func main() { + name := "firstname lastname" + data := "data" + + message := url.Values{ + "Name": []string{name}, + "Data": []string{data}, + } +}