Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new Overlay and Concept nodes to the graph. #1897

Merged
merged 16 commits into from
Dec 17, 2024
Merged
32 changes: 23 additions & 9 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.TypeManager
import de.fraunhofer.aisec.cpg.frontends.Handler
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.edges.*
import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf
import de.fraunhofer.aisec.cpg.graph.edges.flows.ControlDependences
import de.fraunhofer.aisec.cpg.graph.edges.flows.Dataflows
import de.fraunhofer.aisec.cpg.graph.edges.flows.EvaluationOrders
import de.fraunhofer.aisec.cpg.graph.edges.flows.FullDataflowGranularity
import de.fraunhofer.aisec.cpg.graph.edges.flows.ProgramDependences
import de.fraunhofer.aisec.cpg.graph.scopes.*
import de.fraunhofer.aisec.cpg.graph.edges.flows.*
import de.fraunhofer.aisec.cpg.graph.edges.overlay.OverlaySingleEdge
import de.fraunhofer.aisec.cpg.graph.edges.unwrapping
import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope
import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import de.fraunhofer.aisec.cpg.helpers.neo4j.LocationConverter
import de.fraunhofer.aisec.cpg.helpers.neo4j.NameConverter
Expand All @@ -52,7 +53,10 @@ import java.util.*
import kotlin.uuid.Uuid
import org.apache.commons.lang3.builder.ToStringBuilder
import org.apache.commons.lang3.builder.ToStringStyle
import org.neo4j.ogm.annotation.*
import org.neo4j.ogm.annotation.GeneratedValue
import org.neo4j.ogm.annotation.Id
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.annotation.Transient
import org.neo4j.ogm.annotation.typeconversion.Convert
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -274,6 +278,16 @@ abstract class Node :
*/
val additionalProblems: MutableSet<ProblemNode> = mutableSetOf()

@Relationship(value = "OVERLAY", direction = Relationship.Direction.OUTGOING)
val overlayNodeEdge: OverlaySingleEdge =
OverlaySingleEdge(
this,
of = null,
mirrorProperty = OverlayNode::underlyingNodeEdge,
outgoing = true
)
var overlayNode by unwrapping(Node::overlayNodeEdge)

/**
* If a node should be removed from the graph, just removing it from the AST is not enough (see
* issue #60). It will most probably be referenced somewhere via DFG or EOG edges. Thus, if it
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.edges.overlay.OverlaySingleEdge
import de.fraunhofer.aisec.cpg.graph.edges.unwrapping
import org.neo4j.ogm.annotation.Relationship

/**
* Represents an extra node added to the CPG. These nodes can live next to the CPG, typically having
* shared edges to extend the original CPG graph.
*/
abstract class OverlayNode() : Node() {
@Relationship(value = "OVERLAY", direction = Relationship.Direction.INCOMING)
/** All [OverlayNode]s nodes are connected to an original cpg [Node] by this. */
val underlyingNodeEdge: OverlaySingleEdge =
OverlaySingleEdge(
this,
of = null,
mirrorProperty = Node::overlayNodeEdge,
outgoing = false,
)
var underlyingNode by unwrapping(OverlayNode::underlyingNodeEdge)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph.concepts

import de.fraunhofer.aisec.cpg.graph.OverlayNode
import org.neo4j.ogm.annotation.Relationship

/**
* Represents a new concept added to the CPG. This is intended for modelling "concepts" like
* logging, files, databases. The relevant operations on this concept are modeled as [Operation]s
* and stored in [ops].
*/
abstract class Concept<T : Operation>() : OverlayNode() {
/** All [Operation]s belonging to this concept. */
val ops: MutableSet<T> = mutableSetOf()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph.concepts

import de.fraunhofer.aisec.cpg.graph.OverlayNode

/**
* Represents an operation executed on/with a [Concept] (stored in [concept]). This is typically a
* `write` on a file or log object or an `execute` on a database.
*/
abstract class Operation(
/** The [Concept] this operation belongs to. */
val concept: Concept<*>
) : OverlayNode()
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import org.neo4j.ogm.annotation.Transient
*
* Therefore, we need to wrap the edge in a list with a single element.
*/
class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType : Edge<NodeType>>(
open class EdgeSingletonList<
konradweiss marked this conversation as resolved.
Show resolved Hide resolved
NodeType : Node,
NullableNodeType : NodeType?,
EdgeType : Edge<NodeType>
>(
override var thisRef: Node,
override var init: (Node, NodeType) -> EdgeType,
var onChanged: ((old: EdgeType?, new: EdgeType?) -> Unit)? = null,
Expand Down Expand Up @@ -71,7 +75,15 @@ class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType
}

override fun add(element: EdgeType): Boolean {
throw UnsupportedOperationException()
if (this.element == null) {
this.element = element
onChanged?.invoke(null, this.element)
return true
} else {
throw UnsupportedOperationException(
"We cannot 'add' to a singleton edge list, that is already populated"
)
}
}

override fun addAll(elements: Collection<EdgeType>): Boolean {
Expand Down Expand Up @@ -151,6 +163,13 @@ class EdgeSingletonList<NodeType : Node, NullableNodeType : NodeType?, EdgeType
@Suppress("UNCHECKED_CAST") init(node, thisRef as NodeType)
}
onChanged?.invoke(old, this.element)

val element = this.element
if (element != null) {
handleOnAdd(element)
} else if (old != null) {
handleOnRemove(old)
}
}

fun <ThisType : Node> delegate(): UnwrapDelegate<ThisType> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph.edges.overlay

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.edges.Edge
import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeSingletonList
import de.fraunhofer.aisec.cpg.graph.edges.collections.MirroredEdgeCollection
import kotlin.reflect.KProperty

class OverlayEdge(start: Node, end: Node) : Edge<Node>(start, end) {
override var labels: Set<String> = setOf("OVERLAY")
}

class OverlaySingleEdge(
thisRef: Node,
of: Node?,
override var mirrorProperty: KProperty<MutableCollection<OverlayEdge>>,
outgoing: Boolean = true,
) :
EdgeSingletonList<Node, Node?, OverlayEdge>(
thisRef = thisRef,
init = ::OverlayEdge,
outgoing = outgoing,
of = of,
),
MirroredEdgeCollection<Node, OverlayEdge>
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fun TranslationResult.persist() {

val astNodes = [email protected]
val connected = astNodes.flatMap { it.connectedNodes }.toSet()
val nodes = (astNodes + connected).distinct()
val nodes = (astNodes + connected + this.additionalNodes).distinct()

log.info(
"Persisting {} nodes: AST nodes ({}), other nodes ({})",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
package de.fraunhofer.aisec.cpg_vis_neo4j

import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.concepts.Concept
import de.fraunhofer.aisec.cpg.graph.concepts.Operation
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import java.math.BigInteger
import kotlin.test.Test
Expand All @@ -37,24 +39,49 @@ import org.junit.jupiter.api.Tag
class Neo4JTest {
@Test
fun testPush() {
val (application, translationResult) = createTranslationResult()
val (application, result) = createTranslationResult()

// 22 inferred functions, 1 inferred method, 2 inferred constructors, 11 regular functions
assertEquals(36, translationResult.functions.size)
assertEquals(36, result.functions.size)

application.pushToNeo4j(translationResult)
application.pushToNeo4j(result)
}

@Test
fun testPushVeryLong() {
val (application, translationResult) = createTranslationResult("very_long.cpp")
val (application, result) = createTranslationResult("very_long.cpp")

assertEquals(1, translationResult.variables.size)
assertEquals(1, result.variables.size)

val lit = translationResult.variables["l"]?.initializer
val lit = result.variables["l"]?.initializer
assertIs<Literal<BigInteger>>(lit)
assertEquals(BigInteger("10958011617037158669"), lit.value)

application.pushToNeo4j(translationResult)
application.pushToNeo4j(result)
}

@Test
fun testPushConcepts() {
val (application, result) = createTranslationResult()

val connectCall = result.calls["connect"]

abstract class NetworkingOperation(
concept: Concept<out Operation>,
) : Operation(concept)
class Connect(concept: Concept<out Operation>) : NetworkingOperation(concept)
class Networking() : Concept<NetworkingOperation>()

val nw = Networking()
nw.underlyingNode = result.translationUnits.first()

val connect = Connect(concept = nw)
connect.underlyingNode = connectCall
connect.name = Name("connect")
nw.ops += connect

assertEquals(connect, connectCall?.overlayNode)

application.pushToNeo4j(result)
}
}
Loading