diff --git a/build.gradle.kts b/build.gradle.kts index 0c664b1..3d3e894 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { // Apply the Kotlin JVM plugin to add support for Kotlin. - id("org.jetbrains.kotlin.jvm") version "1.7.20-Beta" apply false - kotlin("plugin.serialization") version "1.7.20-Beta" apply false + id("org.jetbrains.kotlin.jvm") version "1.9.20" apply false + kotlin("plugin.serialization") version "1.9.20" apply false id("com.diffplug.spotless") version "5.12.1" } @@ -15,7 +15,7 @@ subprojects { mavenCentral() ivy { - setUrl("https://download.eclipse.org/tools/cdt/releases/10.2/cdt-10.2.0/plugins") + setUrl("https://download.eclipse.org/tools/cdt/releases/11.3/cdt-11.3.1/plugins") metadataSources { artifact() } diff --git a/cloudpg/build.gradle.kts b/cloudpg/build.gradle.kts index 1aab640..4a7e60d 100644 --- a/cloudpg/build.gradle.kts +++ b/cloudpg/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.com.intellij.openapi.vfs.StandardFileSystems.jar - /* * This file was generated by the Gradle 'init' task. * @@ -10,7 +8,7 @@ plugins { application idea `maven-publish` - id("org.jetbrains.kotlinx.benchmark") version "0.4.4" + id("org.jetbrains.kotlinx.benchmark") version "0.4.10" // use this plugin to set all classes open which is required for kotlinx benchmark: id("org.jetbrains.kotlin.plugin.allopen") version "1.7.20-Beta" } @@ -31,13 +29,13 @@ java { sourceSets["main"].java { srcDir("${generatedDir}/main/java") } - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } tasks.withType().configureEach { kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } } @@ -50,6 +48,7 @@ publishing { } repositories { + mavenLocal() mavenCentral() maven { setUrl("https://jitpack.io") } @@ -59,32 +58,40 @@ repositories { } ivy { - setUrl("https://download.eclipse.org/tools/cdt/releases/10.3/cdt-10.3.2/plugins") + setUrl("https://download.eclipse.org/tools/cdt/releases/") metadataSources { artifact() } + patternLayout { - artifact("/[organisation].[module]_[revision].[ext]") + artifact("[organisation].[module]_[revision].[ext]") } } } dependencies { implementation("org.junit.jupiter:junit-jupiter:5.7.0") - val version = "4.6.0" + implementation("com.github.Fraunhofer-AISEC:cpg:4e7c0b862e") + //implementation("de.fraunhofer.aisec:cpg-language-cxx:8.3.0") + //val version = "8.2.0" + + implementation("de.fraunhofer.aisec:cpg-language-typescript:8.3.0") - implementation("de.fraunhofer.aisec:cpg-core:$version") + /*implementation("de.fraunhofer.aisec:cpg-core:$version") implementation("de.fraunhofer.aisec:cpg-analysis:$version") implementation("de.fraunhofer.aisec:cpg-language-go:$version") implementation("de.fraunhofer.aisec:cpg-language-python:$version") + implementation("de.fraunhofer.aisec:cpg-language-typescript:$version") + implementation("de.fraunhofer.aisec:cpg-language-java:$version") + implementation("de.fraunhofer.aisec:cpg-language-cxx:$version")*/ - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.+") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2") implementation ("org.xmlunit:xmlunit-core:2.9.0") implementation("org.xmlunit:xmlunit-matchers:2.9.0") - api("org.neo4j", "neo4j-ogm-core", "3.2.31") - api("org.neo4j", "neo4j-ogm", "3.2.21") - api("org.neo4j", "neo4j-ogm-bolt-driver", "3.2.21") + api("org.neo4j", "neo4j-ogm-core", "4.0.10") + api("org.neo4j", "neo4j-ogm", "4.0.10") + api("org.neo4j", "neo4j-ogm-bolt-driver", "4.0.10") implementation(platform("org.jetbrains.kotlin:kotlin-bom")) @@ -118,7 +125,7 @@ dependencies { application { // Define the main class for the application. - mainClassName = "io.clouditor.graph.AppKt" + mainClass = "io.clouditor.graph.AppKt" } tasks.named("compileJava") { @@ -159,7 +166,7 @@ kotlin { sourceSets { test { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.4") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10") } } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/App.kt b/cloudpg/src/main/java/io/clouditor/graph/App.kt index 658ab9f..2bebcf2 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/App.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/App.kt @@ -4,24 +4,22 @@ package io.clouditor.graph import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguageFrontend -import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguageFrontend -import de.fraunhofer.aisec.cpg.frontends.typescript.TypeScriptLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage +import de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage +import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguage +import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage +import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguage +import de.fraunhofer.aisec.cpg.frontends.typescript.TypeScriptLanguage import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.allChildren import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.graph import de.fraunhofer.aisec.cpg.helpers.Benchmark -import io.clouditor.graph.frontends.ruby.RubyLanguageFrontend import io.clouditor.graph.nodes.Builder import io.clouditor.graph.passes.* import io.clouditor.graph.passes.golang.* import io.clouditor.graph.passes.java.JaxRsClientPass -import io.clouditor.graph.passes.java.JaxRsPass -import io.clouditor.graph.passes.java.SpringBootPass import io.clouditor.graph.passes.js.FetchPass -import io.clouditor.graph.passes.js.JSHttpPass import io.clouditor.graph.passes.python.* -import io.clouditor.graph.passes.ruby.WebBrickPass import io.clouditor.graph.testing.LocalTestingPass import java.nio.file.Path import java.util.concurrent.Callable @@ -35,12 +33,6 @@ import picocli.CommandLine mixinStandardHelpOptions = true, description = ["Builds the Cloud Property Graph and persists it into a graph database."] ) -@OptIn( - ExperimentalTypeScript::class, - ExperimentalPython::class, - ExperimentalGolang::class, - ExperimentalGraph::class -) object App : Callable { @CommandLine.Option( names = ["-k8s-n", "--kubernetes-namespaces"], @@ -87,18 +79,24 @@ object App : Callable { .build() val sessionFactory = - SessionFactory(configuration, "de.fraunhofer.aisec.cpg.graph", "io.clouditor.graph") + SessionFactory( + configuration, + "de.fraunhofer.aisec.cpg.graph", + "io.clouditor.graph", + "de.fraunhofer.aisec.cpg.frontends" + ) val session = sessionFactory.openSession() val result = doTranslate() val nodes = mutableListOf() - nodes.addAll(result.graph.nodes) - nodes.addAll(result.translationUnits) + val translationUnits = + result.components.stream().flatMap { it.translationUnits.stream() }.toList() + nodes.addAll(result.allChildren()) nodes.addAll(result.images) nodes.addAll(result.builders) nodes.addAll(result.computes) - nodes.addAll(result.translationUnits) + nodes.addAll(translationUnits) nodes.addAll(result.additionalNodes) session.beginTransaction().use { transaction -> @@ -122,68 +120,53 @@ object App : Callable { val builder = TranslationConfiguration.builder() .topLevel(rootPath.toFile()) - .sourceLocations(paths.map { rootPath.resolve(it).toFile() }) - .defaultPasses() - .defaultLanguages() - .registerLanguage( - RubyLanguageFrontend::class.java, - RubyLanguageFrontend.RUBY_EXTENSIONS - ) - .registerLanguage( - TypeScriptLanguageFrontend::class.java, - TypeScriptLanguageFrontend.TYPESCRIPT_EXTENSIONS + - TypeScriptLanguageFrontend.JAVASCRIPT_EXTENSIONS - ) - .registerLanguage( - PythonLanguageFrontend::class.java, - PythonLanguageFrontend.PY_EXTENSIONS - ) - .registerLanguage( - GoLanguageFrontend::class.java, - GoLanguageFrontend.GOLANG_EXTENSIONS - ) + .sourceLocations(paths.map { rootPath.resolve(it).toFile().normalize() }) + .registerLanguage(JavaLanguage()) + .registerLanguage(CPPLanguage()) + .registerLanguage(CLanguage()) + .registerLanguage(TypeScriptLanguage()) + .registerLanguage(PythonLanguage()) + .registerLanguage(GoLanguage()) .debugParser(true) - .registerPass(GitHubWorkflowPass()) - .registerPass(SpringBootPass()) - .registerPass(JaxRsPass()) - .registerPass(GolangHttpPass()) - .registerPass(GinGonicPass()) - .registerPass(WebBrickPass()) - .registerPass(JSHttpPass()) - .registerPass(FlaskPass()) + .defaultPasses() + .registerPass(GitHubWorkflowPass::class) + // .registerPass(SpringBootPass::class) + // .registerPass(JaxRsPass::class) + .registerPass(GolangHttpPass::class) + .registerPass(GinGonicPass::class) + // .registerPass(WebBrickPass::class) + // .registerPass(JSHttpPass::class) + // .registerPass(FlaskPass::class) .apply { if (localMode) { // register the localTestingPass after the HTTP Passes since it needs HTTP // request handlers - registerPass(LocalTestingPass()) - registerPass(GolangHttpRequestPass()) + registerPass(LocalTestingPass::class) + registerPass(GolangHttpRequestPass::class) } else { - registerPass(AzurePass()) - registerPass(AzureClientSDKPass()) - registerPass(KubernetesPass()) - registerPass(IngressInvocationPass()) + registerPass(AzurePass::class) + registerPass(AzureClientSDKPass::class) + registerPass(KubernetesPass::class) + registerPass(IngressInvocationPass::class) } } - .registerPass(CryptographyPass()) - .registerPass(GoCryptoPass()) - .registerPass(JaxRsClientPass()) - .registerPass(FetchPass()) - .registerPass(RequestsPass()) - .registerPass(PythonLogPass()) - .registerPass(GolangLogPass()) - .registerPass(GormDatabasePass()) - .registerPass(PyMongoPass()) - .registerPass(Psycopg2Pass()) + .registerPass(CryptographyPass::class) + .registerPass(GoCryptoPass::class) + .registerPass(JaxRsClientPass::class) + .registerPass(FetchPass::class) + .registerPass(RequestsPass::class) + .registerPass(PythonLogPass::class) + .registerPass(GolangLogPass::class) + .registerPass(GormDatabasePass::class) + .registerPass(PyMongoPass::class) + .registerPass(Psycopg2Pass::class) .processAnnotations(true) if (labelsEnabled) { - val edgesCache: BidirectionalEdgesCachePass = BidirectionalEdgesCachePass() - val labelPass: LabelExtractionPass = LabelExtractionPass() - labelPass.edgesCachePass = edgesCache builder - .registerPass(DFGExtensionPass()) - .registerPass(edgesCache) - .registerPass(labelPass) + .registerPass(DFGExtensionPass::class) + .registerPass(BidirectionalEdgesCachePass::class) + .registerPass(LabelExtractionPass::class) .matchCommentsToNodes(true) } @@ -196,7 +179,6 @@ object App : Callable { } } -@OptIn(ExperimentalPython::class, ExperimentalTypeScript::class, ExperimentalGolang::class) fun main(args: Array): Unit = exitProcess(CommandLine(App).execute(*args)) val TranslationResult.images: MutableList @@ -212,13 +194,8 @@ val TranslationResult.computes: MutableList this.scratch.computeIfAbsent("computes") { mutableListOf() } as MutableList -val TranslationResult.additionalNodes: MutableList - get() = - this.scratch.computeIfAbsent("additionalNodes") { mutableListOf() } as - MutableList - fun TranslationResult.findApplicationByTU(tu: TranslationUnitDeclaration): Application? { - return this.additionalNodes.filterIsInstance(Application::class.java).firstOrNull { + return this.additionalNodes.filterIsInstance().firstOrNull { it.translationUnits.contains(tu) } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/ValueResolver.kt b/cloudpg/src/main/java/io/clouditor/graph/ValueResolver.kt index befeaeb..084386e 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/ValueResolver.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/ValueResolver.kt @@ -14,6 +14,8 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.* * literal values. Furthermore, its behaviour can be adjusted by implementing the [cannotResolve] * function, which is called when the default behaviour would not be able to resolve the value. This * way, language specific features such as string formatting can be modelled. + * + * TODO: Replace with Evaluator from the new cpg-analysis package */ open class ValueResolver( /** @@ -44,7 +46,7 @@ open class ValueResolver( when (expr) { is KeyValueExpression -> return resolve(expr.value) is Literal<*> -> return expr.value?.toString() ?: "" - is DeclaredReferenceExpression -> return resolveDeclaration(expr.refersTo) + // is Reference -> return resolveDeclaration(expr.refersTo) is BinaryOperator -> { // resolve lhs val lhsValue = resolve(expr.lhs) @@ -117,10 +119,8 @@ open class ValueResolver( is CastExpression -> { return this.resolve(expr.expression) } - is ArraySubscriptionExpression -> { - val array = - (expr.arrayExpression as? DeclaredReferenceExpression)?.refersTo as? - VariableDeclaration + is SubscriptExpression -> { + val array = (expr.arrayExpression as? Reference)?.refersTo as? VariableDeclaration val ile = array?.initializer as? InitializerListExpression ile?.let { @@ -144,14 +144,17 @@ open class ValueResolver( val rhs = resolve((expr.condition as? BinaryOperator)?.rhs) return if (lhs == rhs) { - resolve(expr.thenExpr) + resolve(expr.thenExpression) } else { - resolve(expr.elseExpr) + resolve(expr.elseExpression) } } return cannotResolve(expr, this) } + is Reference -> { + return expr.refersTo + } } return cannotResolve(expr, this) diff --git a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/DeclarationHandler.kt b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/DeclarationHandler.kt deleted file mode 100644 index 012daf4..0000000 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/DeclarationHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.clouditor.graph.frontends.ruby - -import de.fraunhofer.aisec.cpg.frontends.Handler -import de.fraunhofer.aisec.cpg.graph.NodeBuilder -import de.fraunhofer.aisec.cpg.graph.declarations.Declaration -import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration -import de.fraunhofer.aisec.cpg.graph.types.UnknownType -import org.jruby.ast.* - -class DeclarationHandler(lang: RubyLanguageFrontend) : - Handler({ ProblemDeclaration() }, lang) { - - init { - map.put(ArgumentNode::class.java, ::handleArgumentNode) - } - - private fun handleArgumentNode(node: Node?): Declaration? { - if (node !is ArgumentNode) { - return null - } - - val param = - NodeBuilder.newMethodParameterIn( - node.name.idString(), - UnknownType.getUnknownType(), - false, - lang.getCodeFromRawNode(node) - ) - - return param - } -} diff --git a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/ExpressionHandler.kt b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/ExpressionHandler.kt deleted file mode 100644 index 047e090..0000000 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/ExpressionHandler.kt +++ /dev/null @@ -1,191 +0,0 @@ -package io.clouditor.graph.frontends.ruby - -import de.fraunhofer.aisec.cpg.frontends.Handler -import de.fraunhofer.aisec.cpg.graph.NodeBuilder -import de.fraunhofer.aisec.cpg.graph.statements.Statement -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression -import de.fraunhofer.aisec.cpg.graph.types.TypeParser -import de.fraunhofer.aisec.cpg.graph.types.UnknownType -import org.jruby.ast.* - -class ExpressionHandler(lang: RubyLanguageFrontend) : - Handler({ ProblemExpression() }, lang) { - - init { - map.put(CallNode::class.java, ::handleCallNode) - map.put(FCallNode::class.java, ::handleFCallNode) - map.put(IterNode::class.java, ::handleIterNode) - map.put(StrNode::class.java, ::handleStrNode) - map.put(DVarNode::class.java, ::handleDVarNode) - map.put(AttrAssignNode::class.java, ::handleAttrAssignNode) - map.put(AssignableNode::class.java, ::handleAssignableNode) - } - - private fun handleFCallNode(node: Node?): Statement? { - if (node !is FCallNode) { - return null - } - - return null - } - - private fun handleAttrAssignNode(node: Node?): Statement? { - if (node !is AttrAssignNode) { - return null - } - - var binOp = NodeBuilder.newBinaryOperator("=", lang.getCodeFromRawNode(node)) - - var base = this.handle(node.receiverNode) as? Expression - var expr = - NodeBuilder.newMemberExpression( - base, - UnknownType.getUnknownType(), - node.name.idString(), - "=", - lang.getCodeFromRawNode(base) - ) - - binOp.lhs = expr - binOp.rhs = this.handle(node.argsNode) as? Expression - - return expr - } - - private fun handleDVarNode(node: Node?): Statement? { - if (node !is DVarNode) { - return null - } - - val ref = - NodeBuilder.newDeclaredReferenceExpression( - node.name.idString(), - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(node) - ) - - return ref - } - - private fun handleAssignableNode(node: Node?): Statement? { - if (node !is DAsgnNode && node !is LocalAsgnNode) { - return null - } - - val name = - if (node is DAsgnNode) { - node.name - } else { - (node as LocalAsgnNode).name - } - - // either a binary operator or a variable declaration - val lhs = - NodeBuilder.newDeclaredReferenceExpression( - name.idString(), - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(node) - ) - val rhs = this.handle((node as AssignableNode).valueNode) as? Expression - - // can we resolve it? - var decl = this.lang.scopeManager.resolveReference(lhs) - - if (decl == null) { - val stmt = NodeBuilder.newDeclarationStatement(lang.getCodeFromRawNode(lhs)) - decl = - NodeBuilder.newVariableDeclaration( - lhs.name, - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(lhs), - false - ) - decl.initializer = rhs - - stmt.singleDeclaration = decl - - return stmt - } - - val binOp = NodeBuilder.newBinaryOperator("=", lang.getCodeFromRawNode(node)) - binOp.lhs = lhs - binOp.rhs = rhs - - return binOp - } - - private fun handleCallNode(node: Node): Expression? { - if (node !is CallNode) { - return null - } - - val base = handle(node.receiverNode) as? Expression - val member = null - - val mce = - NodeBuilder.newMemberCallExpression( - node.name.asJavaString(), - node.name.asJavaString(), - base, - member, - ".", - lang.getCodeFromRawNode(node) - ) - - for (arg in node.argsNode?.childNodes() ?: emptyList()) { - mce.addArgument(handle(arg) as? Expression) - } - - // add the iterNode as last argument - node.iterNode?.let { mce.addArgument(handle(it) as? Expression) } - - return mce - } - - private fun handleIterNode(node: Node): Expression? { - if (node !is IterNode) { - return null - } - - // a complete hack, to handle iter nodes, which is sort of a lambda expression - // so we create an anonymous function declaration out of the bodyNode and varNode - // and a declared reference expressions to that anonymous function - val func = NodeBuilder.newFunctionDeclaration("", lang.getCodeFromRawNode(node)) - - lang.scopeManager.enterScope(func) - - for (arg in node.argsNode.args) { - val param = lang.declarationHandler.handle(arg) - lang.scopeManager.addDeclaration(param) - } - - func.body = lang.statementHandler.handle(node.bodyNode) - - lang.scopeManager.leaveScope(func) - - var def = NodeBuilder.newDeclarationStatement(lang.getCodeFromRawNode(node)) - def.singleDeclaration = func - - var cse = - NodeBuilder.newCompoundStatementExpression(lang.getCodeFromRawNode(node).toString()) - cse.statement = def - - return cse - } - - private fun handleStrNode(node: Node): Expression? { - if (node !is StrNode) { - return null - } - - val literal = - NodeBuilder.newLiteral( - String(node.value.bytes()), - TypeParser.createFrom("string", false), - lang.getCodeFromRawNode(node) - ) - - return literal - } -} diff --git a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguageFrontend.kt b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguageFrontend.kt deleted file mode 100644 index 122066b..0000000 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguageFrontend.kt +++ /dev/null @@ -1,78 +0,0 @@ -package io.clouditor.graph.frontends.ruby - -import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend -import de.fraunhofer.aisec.cpg.graph.NodeBuilder -import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.passes.scopes.ScopeManager -import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation -import java.io.File -import org.checkerframework.checker.nullness.qual.NonNull -import org.jruby.Ruby -import org.jruby.ast.BlockNode -import org.jruby.ast.RootNode -import org.jruby.parser.Parser -import org.jruby.parser.ParserConfiguration - -class RubyLanguageFrontend(config: @NonNull TranslationConfiguration, scopeManager: ScopeManager?) : - LanguageFrontend(config, scopeManager, "::") { - - companion object { - @kotlin.jvm.JvmField var RUBY_EXTENSIONS: List = listOf(".rb") - } - - val declarationHandler: DeclarationHandler = DeclarationHandler(this) - val expressionHandler: ExpressionHandler = ExpressionHandler(this) - val statementHandler: StatementHandler = StatementHandler(this) - - override fun parse(file: File): TranslationUnitDeclaration { - - val ruby = Ruby.getGlobalRuntime() - val parser = Parser(ruby) - - val node = - parser.parse( - file.path, - file.inputStream(), - null, - ParserConfiguration(ruby, 0, false, true, false) - ) as - RootNode - - return handleRootNode(node, file) - } - - private fun handleRootNode(node: RootNode, file: File): TranslationUnitDeclaration { - val tu = NodeBuilder.newTranslationUnitDeclaration(node.file, getCodeFromRawNode(node)) - - scopeManager.resetToGlobal(tu) - - // wrap everything into a virtual global function because we only have declarations on the - // top - val func = - NodeBuilder.newFunctionDeclaration( - file.nameWithoutExtension + "_global", - getCodeFromRawNode(node) - ) - - scopeManager.enterScope(func) - - func.body = statementHandler.handle(node.bodyNode as BlockNode) - - scopeManager.leaveScope(func) - - scopeManager.addDeclaration(func) - - return tu - } - - override fun getCodeFromRawNode(astNode: T): String? { - return "" - } - - override fun getLocationFromRawNode(astNode: T): PhysicalLocation? { - return null - } - - override fun setComment(s: S, ctx: T) {} -} diff --git a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/StatementHandler.kt b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/StatementHandler.kt deleted file mode 100644 index e9efd04..0000000 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/StatementHandler.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.clouditor.graph.frontends.ruby - -import de.fraunhofer.aisec.cpg.frontends.Handler -import de.fraunhofer.aisec.cpg.graph.NodeBuilder -import de.fraunhofer.aisec.cpg.graph.statements.CompoundStatement -import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement -import de.fraunhofer.aisec.cpg.graph.statements.Statement -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression -import org.jruby.ast.BlockNode -import org.jruby.ast.Node - -class StatementHandler(lang: RubyLanguageFrontend) : - Handler({ ProblemExpression() }, lang) { - - init { - map.put(BlockNode::class.java, ::handleBlockNode) - } - - private fun handleBlockNode(blockNode: Node): CompoundStatement? { - if (blockNode !is BlockNode) { - return null - } - - blockNode.containsVariableAssignment() - val compoundStatement = NodeBuilder.newCompoundStatement(lang.getCodeFromRawNode(blockNode)) - - for (node in blockNode) { - val statement = lang.expressionHandler.handle(node) - - statement?.let { compoundStatement.addStatement(it) } - } - - val statements = compoundStatement.statements - - // get the last statement - var lastStatement: Statement? = null - if (!statements.isEmpty()) { - lastStatement = statements.get(statements.size - 1) - } - - // add an implicit return statement, if there is none - if (lastStatement !is ReturnStatement) { - val returnStatement = NodeBuilder.newReturnStatement("return") - returnStatement.isImplicit = true - compoundStatement.addStatement(returnStatement) - } - - return compoundStatement - } -} diff --git a/cloudpg/src/main/java/io/clouditor/graph/github/WorkflowHandler.kt b/cloudpg/src/main/java/io/clouditor/graph/github/WorkflowHandler.kt index a1848c9..15f7cae 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/github/WorkflowHandler.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/github/WorkflowHandler.kt @@ -3,8 +3,10 @@ package io.clouditor.graph.github import com.azure.core.management.Region import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinFeature import com.fasterxml.jackson.module.kotlin.KotlinModule import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.Name import io.clouditor.graph.* import io.clouditor.graph.docker.DockerCompose import io.clouditor.graph.nodes.Builder @@ -13,9 +15,10 @@ import io.clouditor.graph.passes.locationForRegion import java.lang.IllegalArgumentException import java.nio.file.Files import java.nio.file.Path -import kotlin.io.path.name +import kotlin.streams.toList -class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) { +@Suppress("UNUSED_PARAMETER") +class WorkflowHandler(private val result: TranslationResult, private val rootPath: Path) { fun handleWorkflow(workflow: Workflow) { workflow.jobs.values.forEach { handleJob(it, workflow) } @@ -43,7 +46,7 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) ?.let { command -> val application = result.additionalNodes.filterIsInstance(Application::class.java) - .firstOrNull { it.name == Path.of(path).fileName.toString() } + .firstOrNull { it.name.localName == Path.of(path).fileName.toString() } val rr = command.split(" ") // look for the host @@ -65,13 +68,22 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) result.locationForRegion(Region.US_EAST), mutableMapOf() ) - compute.name = host + compute.name = Name(host) application?.runsOn?.plusAssign(compute) result += compute val mapper = ObjectMapper(YAMLFactory()) - mapper.registerModule(KotlinModule()) + mapper.registerModule( + KotlinModule.Builder() + .withReflectionCacheSize(512) + .configure(KotlinFeature.NullToEmptyCollection, false) + .configure(KotlinFeature.NullToEmptyMap, false) + .configure(KotlinFeature.NullIsSameAsDefault, false) + .configure(KotlinFeature.SingletonSupport, false) + .configure(KotlinFeature.StrictNullChecks, false) + .build() + ) Files.newBufferedReader(rootPath.resolve(composePath)).use { reader -> val compose = mapper.readValue(reader, DockerCompose::class.java) @@ -89,7 +101,7 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) compute.geoLocation, mutableMapOf() ) - networkService.name = host + networkService.name = Name(host) result += networkService } @@ -108,9 +120,11 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) // filter out the translation units belonging to these applications, until cpg#341 // is // solved + val translationUnits = + result.components.stream().flatMap { it.translationUnits.stream() }.toList() val tus = - result.translationUnits.filter { - val tuPath = Path.of(it.name) + translationUnits.filter { + val tuPath = Path.of(it.name.localName) try { tuPath.startsWith(Path.of(path).toAbsolutePath().normalize()) || @@ -121,6 +135,7 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) } // create a new application based on the path + // TODO: Use component from the new CPG API val application = Application( mutableListOf(), @@ -128,23 +143,23 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) mutableListOf(), tus, ) - application.name = Path.of(path).fileName.toString() + application.name = Name(Path.of(path).fileName.toString()) result.additionalNodes += application // we need to assume, that GH stores its images in the US val image = Image(application, result.location("US"), mapOf()) - image.name = name + image.name = Name(name) result.images += image val builder = Builder(mutableListOf(image)) - step.name?.let { builder.name = it } + step.name?.let { builder.name = Name(it) } result.builders += builder // build a DFG node from the builder to the image - builder.addNextDFG(image) + builder.nextDFGEdges.add(image) } } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/Database.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/Database.kt index efe560f..b45353c 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/Database.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/Database.kt @@ -1,19 +1,21 @@ package io.clouditor.graph.nodes +import de.fraunhofer.aisec.cpg.graph.Name import io.clouditor.graph.DatabaseService import io.clouditor.graph.DatabaseStorage fun DatabaseService.getStorageOrCreate(name: String, parentName: String? = null): DatabaseStorage { - var storage = this.storage.filterIsInstance().firstOrNull { it.name == name } + var storage = + this.storage.filterIsInstance().firstOrNull { it.name.localName == name } if (storage == null) { storage = DatabaseStorage(mutableListOf(), null, listOf(), this.geoLocation, mutableMapOf()) - storage.name = name + storage.name = Name(name) // if the parent name was specified, try to look it up and set the parent(s) // TODO: why exactly is parents a list? FIX in the ontology? if (parentName != null) { - storage.parent = this.storage.filter { it.name == parentName } + storage.parent = this.storage.filter { it.name.localName == parentName } } this.storage.add(storage) diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/Holder.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/Holder.kt index adabde6..ca06e98 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/Holder.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/Holder.kt @@ -1,6 +1,7 @@ package io.clouditor.graph.nodes import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import io.clouditor.graph.* @@ -19,11 +20,13 @@ class Holder( fun TranslationResult.location(locationName: String): GeoLocation { var location = - this.additionalNodes.firstOrNull { it is GeoLocation && it.name == locationName } as? + this.additionalNodes.firstOrNull { + it is GeoLocation && it.name.localName == locationName + } as? GeoLocation if (location == null) { location = GeoLocation(locationName) - location.name = location.region + location.name = Name(location.region) this += location } diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/Walkers.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/Walkers.kt index 87d8152..e50da98 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/Walkers.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/Walkers.kt @@ -1,10 +1,10 @@ package io.clouditor.graph.nodes import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge +import de.fraunhofer.aisec.cpg.graph.edges.Edge -fun Node.followEOG(predicate: (PropertyEdge<*>) -> Boolean): List>? { - val path = mutableListOf>() +fun Node.followEOG(predicate: (Edge<*>) -> Boolean): List>? { + val path = mutableListOf>() for (edge in this.nextEOGEdges) { path.add(edge) diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/AnonLabel.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/AnonLabel.kt index f112b2e..755a398 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/AnonLabel.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/AnonLabel.kt @@ -8,13 +8,13 @@ class AnonLabel(labeledNode: Node) : Label(labeledNode) { * In the future on label will be used to label multiple nodes, with the purpose of havein one * unique label of the same type and properties. */ - @field:Relationship(value = "ANONYMIZES", direction = "OUTGOING") + @field:Relationship(value = "ANONYMIZES", direction = Relationship.Direction.OUTGOING) var anonymizes: Label? = null // We can make this a list if we allow anonlabels to anonymize several // labels that are not mergeable between each other override fun areMergeable(l: Label): Boolean { - if (!(l::class == AnonLabel::class)) { + if (l::class != AnonLabel::class) { return false } (l as AnonLabel).anonymizes?.let { @@ -28,7 +28,7 @@ class AnonLabel(labeledNode: Node) : Label(labeledNode) { } fun addAnonymize(l: Label) { - if (anonymizes?.labeledNodes?.isEmpty() ?: true) { + if (anonymizes?.labeledNodes?.isEmpty() != false) { anonymizes = l } else { anonymizes!!.mergeWith(l) diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/Label.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/Label.kt index b5e6cc7..d706081 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/Label.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/Label.kt @@ -10,7 +10,7 @@ open class Label constructor(labeledNode: Node) : Node() { * In the future on label will be used to label multiple nodes, with the purpose of havein one * unique label of the same type and properties. */ - @field:Relationship(value = "LABELEDNODE", direction = "OUTGOING") + @field:Relationship(value = "LABELEDNODE", direction = Relationship.Direction.OUTGOING) var labeledNodes: MutableList = mutableListOf(labeledNode) open fun areMergeable(l: Label): Boolean { diff --git a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/PrivacyLabel.kt b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/PrivacyLabel.kt index cd7c00b..5549478 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/PrivacyLabel.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/nodes/labels/PrivacyLabel.kt @@ -9,7 +9,8 @@ import org.neo4j.ogm.annotation.Relationship */ class PrivacyLabel(labeledNode: Node) : DataLabel(labeledNode) { - @Relationship(value = "PROTECTION_LEVEL", direction = "OUTGOING") var protectionlevel: Int = 0 + @Relationship(value = "PROTECTION_LEVEL", direction = Relationship.Direction.OUTGOING) + var protectionlevel: Int = 0 override fun areMergeable(l: Label): Boolean { return l::class == PrivacyLabel::class && diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt index 1c6c663..920e01b 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt @@ -16,38 +16,37 @@ import com.azure.resourcemanager.loganalytics.LogAnalyticsManager import com.azure.resourcemanager.loganalytics.models.Workspace import com.azure.resourcemanager.storage.models.PublicAccess import com.azure.resourcemanager.storage.models.StorageAccount -import de.fraunhofer.aisec.cpg.ExperimentalGolang +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.declarations.ParamVariableDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration -import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import io.clouditor.graph.* import io.clouditor.graph.nodes.followDFGReverse import io.clouditor.graph.nodes.followEOG import io.clouditor.graph.nodes.location +import kotlin.streams.toList import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlin.time.toJavaDuration -class AzureClientSDKPass : Pass() { - override fun accept(t: TranslationResult) { - for (tu in t.translationUnits) { - val app = t.findApplicationByTU(tu) +@Suppress("UNUSED_PARAMETER") +class AzureClientSDKPass(ctx: TranslationContext) : TranslationResultPass(ctx) { + override fun accept(result: TranslationResult) { + val translationUnits = + result.components.stream().flatMap { it.translationUnits.stream() }.toList() + for (tu in translationUnits) { + val app = result.findApplicationByTU(tu) tu.accept( Strategy::AST_FORWARD, - object : IVisitor() { - /*fun visit(c: MemberCallExpression) { - handleCall(t, tu, c) - }*/ - fun visit(c: NewExpression) { - handleNewClient(t, tu, c, app) + object : IVisitor() { + fun visit(t: NewExpression) { + handleNewClient(result, tu, t, app) } } ) @@ -61,7 +60,7 @@ class AzureClientSDKPass : Pass() { app: Application? ) { try { - if (c.type.name == "com.azure.storage.blob.BlobContainerClientBuilder") { + if (c.type.name.toString() == "com.azure.storage.blob.BlobContainerClientBuilder") { // we need to follow the EOG until we have proper support for querying outgoing // edges in the graph because // we need to find call expressions which have the new expression as a base @@ -72,15 +71,14 @@ class AzureClientSDKPass : Pass() { var containerName: String var url = "" var client: ValueDeclaration? = null - var appendClient: ValueDeclaration var eog: Node = c var path = eog.followEOG { it.end is CallExpression && - it.end.name == "endpoint" && - (it.end as CallExpression).base == c + it.end.name.localName == "endpoint" && + (it.end as MemberCallExpression).base == c } path?.let { val call = it.last().end as CallExpression @@ -94,7 +92,7 @@ class AzureClientSDKPass : Pass() { path = eog.followEOG { it.end is CallExpression && - it.end.name == + it.end.name.localName == "containerName" /*&& (it.end as CallExpression).base == c*/ } path?.let { @@ -109,7 +107,8 @@ class AzureClientSDKPass : Pass() { path = eog.followEOG { it.end is CallExpression && - it.end.name == "buildClient" /*&& (it.end as CallExpression).base == c*/ + it.end.name.localName == + "buildClient" /*&& (it.end as CallExpression).base == c*/ } path?.let { val call = it.last().end as CallExpression @@ -119,7 +118,7 @@ class AzureClientSDKPass : Pass() { if (next is ValueDeclaration) { next } else { - (next as DeclaredReferenceExpression).refersTo as ValueDeclaration? + (next as Reference).refersTo as ValueDeclaration? } } @@ -138,12 +137,13 @@ class AzureClientSDKPass : Pass() { // AppendBlobClient path = eog.followEOG { - it.end is CallExpression && - it.end.name == "getAppendBlobClient" && - (it.end as CallExpression).base is CallExpression && - (it.end as CallExpression).base.name == "getBlobClient" && - (((it.end as CallExpression).base as CallExpression).base as - DeclaredReferenceExpression) + it.end is MemberCallExpression && + it.end.name.localName == "getAppendBlobClient" && + (it.end as MemberCallExpression).base is CallExpression && + (it.end as MemberCallExpression).base?.name?.localName == + "getBlobClient" && + (((it.end as CallExpression).callee as MemberCallExpression).base as + Reference) .refersTo == client } @@ -152,7 +152,7 @@ class AzureClientSDKPass : Pass() { if (next is ValueDeclaration) { next } else { - (next as DeclaredReferenceExpression).refersTo as ValueDeclaration? + (next as Reference).refersTo as ValueDeclaration? } append?.let { @@ -177,9 +177,8 @@ class AzureClientSDKPass : Pass() { base.followEOG { it.end is MemberCallExpression && ((it.end as MemberCallExpression).base == base || - ((it.end as MemberCallExpression).base is DeclaredReferenceExpression && - ((it.end as MemberCallExpression).base as DeclaredReferenceExpression) - .refersTo == base)) + ((it.end as MemberCallExpression).base is Reference && + ((it.end as MemberCallExpression).base as Reference).refersTo == base)) } return path?.last()?.end as? MemberCallExpression @@ -194,23 +193,23 @@ class AzureClientSDKPass : Pass() { storage: ObjectStorage, app: Application? ) { - if (c.name == "create") { + if (c.name.localName == "create") { println("We got an interesting call: create") val request = ObjectStorageRequest(c, listOf(storage), "create") - request.addNextDFG(storage) - request.name = request.type + request.nextDFG.add(storage) + request.name = Name(request.type, null) t += request app?.functionalities?.plusAssign(request) - } else if (c.name == "appendBlock") { + } else if (c.name.localName == "appendBlock") { println("We got an interesting call: appendBlock") // create an object storage request val request = ObjectStorageRequest(c, listOf(storage), "append") - request.addNextDFG(storage) - request.name = request.type + request.nextDFG.add(storage) + request.name = Name(request.type, null) t += request @@ -220,19 +219,18 @@ class AzureClientSDKPass : Pass() { // documented as a graph query in the paper // first parameter is always an input stream - val inputStreamRef = c.arguments[0] as DeclaredReferenceExpression + val inputStreamRef = c.arguments[0] as Reference val inputStream = inputStreamRef.refersTo as VariableDeclaration val newExpression = inputStream.initializer as NewExpression val construct = newExpression.initializer as ConstructExpression // this is very hacky, but we assume that it is always a new // ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)) - val sRef = - (construct.arguments[0] as MemberCallExpression).base as DeclaredReferenceExpression - val s = sRef.refersTo as ParamVariableDeclaration + val sRef = (construct.arguments[0] as MemberCallExpression).base as Reference + val s = sRef.refersTo as ParameterDeclaration // follow - val param = s.followDFGReverse { it.second.name == "password" } + val param = s.followDFGReverse { it.second.name.localName == "password" } if (param?.isEmpty() == false) { println("Dude, you are probably leaking a password.") @@ -241,10 +239,9 @@ class AzureClientSDKPass : Pass() { } } -class AzurePass : CloudResourceDiscoveryPass() { +class AzurePass(ctx: TranslationContext) : CloudResourceDiscoveryPass(ctx) { override fun cleanup() {} - @OptIn(ExperimentalGolang::class) override fun accept(t: TranslationResult) { val profile = AzureProfile(AzureEnvironment.AZURE) val credential: TokenCredential = @@ -287,16 +284,16 @@ class AzurePass : CloudResourceDiscoveryPass() { val storage = t.additionalNodes.filterIsInstance().firstOrNull { // TODO: unique names - it.name == "am-containerlog" + it.name.localName == "am-containerlog" } // model data export as ObjectStorageRequest val request = ObjectStorageRequest(log, listOf(storage), "append") - storage?.let { request.addNextDFG(it) } + storage?.let { request.nextDFG.add(it) } // add DFG from the source to the sink request.to.forEach { request.source.nextDFG.add(it) } - request.name = request.type + request.name = Name(request.type, null) t += request } @@ -314,10 +311,9 @@ class AzurePass : CloudResourceDiscoveryPass() { val compute = handleVirtualMachine(t, vm) // look for the image tag to connect services - val name = vm.tags().getOrDefault("image", null) - - val image = t.getImageByName(name) + // val name = vm.tags().getOrDefault("image", null) + // val image = t.getImageByName(name) // image?.implements?.forEach { it.deployedOn.add(compute) } t.computes += compute } @@ -352,7 +348,7 @@ class AzurePass : CloudResourceDiscoveryPass() { workspace.retentionInDays().toDuration(DurationUnit.DAYS).toJavaDuration(), loggingServices ) - logging.name = workspace.name() + logging.name = Name(workspace.name(), null) return logging } @@ -374,7 +370,7 @@ class AzurePass : CloudResourceDiscoveryPass() { log = t.additionalNodes.filterIsInstance(ResourceLogging::class.java).firstOrNull { - it.name == shortName + it.name.localName == shortName } } } @@ -453,7 +449,7 @@ class AzurePass : CloudResourceDiscoveryPass() { // loop through the containers (for our use case this is ok, for larger ones, probably not) val paged = azure.storageBlobContainers().listAsync(account.resourceGroupName(), account.name()) - for (blob in paged.collectList().block()) { + for (blob in paged.collectList().block() ?: listOf()) { // TODO: also include other endpoints @@ -461,15 +457,12 @@ class AzurePass : CloudResourceDiscoveryPass() { // accepts more methods // TODO: make methods an array? - val auth = - if (blob.publicAccess() == PublicAccess.NONE) { - SingleSignOn( - true - ) // this is closest to how auth works in Azure. TokenBased would - // be better - } else { - NoAuthentication() - } + if (blob.publicAccess() == PublicAccess.NONE) { + SingleSignOn(true) // this is closest to how auth works in Azure. TokenBased would + // be better + } else { + NoAuthentication() + } // at rest seems to be default anyway now val storage = @@ -480,7 +473,7 @@ class AzurePass : CloudResourceDiscoveryPass() { t.locationForRegion(account.region()), mapOf() ) - storage.name = blob.name() + storage.name = Name(blob.name(), null) storageList += storage } @@ -507,7 +500,7 @@ class AzurePass : CloudResourceDiscoveryPass() { val block = BlockStorage(null, mutableListOf(atRest), t.locationForRegion(disk.region()), mapOf()) - block.name = disk.name() + block.name = Name(disk.name(), null) return block } @@ -527,7 +520,7 @@ class AzurePass : CloudResourceDiscoveryPass() { t.locationForRegion(vm.region()), mapOf() ) - compute.name = vm.name() + compute.name = Name(vm.name(), null) compute.labels = mapOf() return compute @@ -535,9 +528,10 @@ class AzurePass : CloudResourceDiscoveryPass() { } fun TranslationResult.getImageByName(name: String?): Image? { - return this.images.firstOrNull { it.name == name } + return this.images.firstOrNull { it.name.localName == name } } +@Suppress("UNUSED_PARAMETER") fun TranslationResult.getObjectStorageByUrl(url: String?): ObjectStorage? { // return this.additionalNodes.firstOrNull { // // TODO(all): How to check that? diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/BidirectionalEdgesCachePass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/BidirectionalEdgesCachePass.kt index ae0d029..1a4e3f3 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/BidirectionalEdgesCachePass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/BidirectionalEdgesCachePass.kt @@ -1,23 +1,25 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass -class BidirectionalEdgesCachePass : Pass() { +class BidirectionalEdgesCachePass(ctx: TranslationContext) : TranslationResultPass(ctx) { enum class EdgeLabel { INSTANTIATES } - val predicatesToHandle: MutableMap<(Node) -> Boolean, Pair List>> = + private val predicatesToHandle: + MutableMap<(Node) -> Boolean, Pair List>> = mutableMapOf() - val outgoingEdgesCache: MutableMap>> = + private val outgoingEdgesCache: MutableMap>> = mutableMapOf() - val incomingEdgesCache: MutableMap>> = + private val incomingEdgesCache: MutableMap>> = mutableMapOf() override fun accept(t: TranslationResult) { @@ -27,24 +29,20 @@ class BidirectionalEdgesCachePass : Pass() { // Register default extractor that gets Label from Annotation predicatesToHandle.put( { node -> node is ConstructExpression }, - Pair( - EdgeLabel.INSTANTIATES, - { node: Node -> - (node as ConstructExpression).instantiates?.let { listOf(it) } - ?: emptyList() - } - ) + Pair(EdgeLabel.INSTANTIATES) { node: Node -> + (node as ConstructExpression).instantiates?.let { listOf(it) } ?: emptyList() + } ) nodes.forEach { node: Node -> - predicatesToHandle.forEach { predicate, pair -> + predicatesToHandle.forEach { (predicate, pair) -> val targets: List = if (predicate(node)) { pair.second(node) } else { emptyList() } - if (!targets.isEmpty()) { + if (targets.isNotEmpty()) { if (!outgoingEdgesCache.containsKey(node)) outgoingEdgesCache[node] = mutableMapOf>() @@ -65,11 +63,11 @@ class BidirectionalEdgesCachePass : Pass() { } fun getEdgeTargetOf(source: Node, edgeLabel: EdgeLabel): List? { - return incomingEdgesCache.get(source)?.get(edgeLabel) + return incomingEdgesCache[source]?.get(edgeLabel) } fun getEdgeSourceOf(target: Node, edgeLabel: EdgeLabel): List? { - return outgoingEdgesCache.get(target)?.get(edgeLabel) + return outgoingEdgesCache[target]?.get(edgeLabel) } override fun cleanup() {} diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/CloudResourceDiscoveryPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/CloudResourceDiscoveryPass.kt index 85c9c9b..9e5e305 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/CloudResourceDiscoveryPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/CloudResourceDiscoveryPass.kt @@ -1,5 +1,6 @@ package io.clouditor.graph.passes -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass -abstract class CloudResourceDiscoveryPass : Pass() {} +abstract class CloudResourceDiscoveryPass(ctx: TranslationContext) : TranslationResultPass(ctx) {} diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt index 0cad262..2405fd3 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt @@ -1,25 +1,30 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.graph.HasType import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.KeyValueExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression +import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.graph.types.recordDeclaration import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.Pass -import io.clouditor.graph.plusAssign +import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass +import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import java.util.* /** * The purpose of this Pass os to enhance the DFG graph with additional edges that are needed for * DF-Label tracking. One such case is the labeling of a variable that is the base of a member - * expression. Normally no data flows from the base to the member expression. FOr this use case, + * expression. Normally no data flows from the base to the member expression. For this use case, * however, the mere usage of a base causes labels to be relevant for the member expression. */ -class DFGExtensionPass : Pass() { +// TODO: We do have these partial flows now, so this pass is probably obsolete +@DependsOn(ControlFlowSensitiveDFGPass::class) +class DFGExtensionPass(ctx: TranslationContext) : TranslationResultPass(ctx) { override fun accept(t: TranslationResult) { // loop through services @@ -27,21 +32,19 @@ class DFGExtensionPass : Pass() { val memberExpressions = nodes.filterIsInstance() val stringifyFunctions = - nodes.filterIsInstance().filter { node -> node.name == "stringify" } + nodes.filterIsInstance().filter { node -> + node.name.localName == "stringify" + } // val stringFunctions: List = // nodes.filterIsInstance().filter { node -> node.name == "stringify" || // node.name == "toString" } - val keyValueExpressions: List = - nodes.filterIsInstance() - connectDFGValuesToKeyValueExpression(keyValueExpressions) - stringifyFunctions.forEach { redirectDFGThroughFunctionCall(it) - drawDFGEgesFromNestedFields(it) + drawDFGEdgesFromNestedFields(it) } - memberExpressions.forEach { it.addPrevDFG(it.base) } + memberExpressions.forEach { it.prevDFG.add(it.base) } } /** @@ -49,25 +52,27 @@ class DFGExtensionPass : Pass() { * unpacking operations although there is no explicit data flow through the key Value expression * to for example an InitializerListExpression */ - fun connectDFGValuesToKeyValueExpression(keyValueExpressions: List) { + private fun connectDFGValuesToKeyValueExpression( + keyValueExpressions: List + ) { keyValueExpressions.forEach { val keyValueExpression: KeyValueExpression = it - it.value?.let { keyValueExpression.addPrevDFG(it) } + it.value?.let { keyValueExpression.prevDFG.add(it) } } } - fun redirectDFGThroughFunctionCall(call: CallExpression) { - call.arguments.forEach { call.addPrevDFG(it) } + private fun redirectDFGThroughFunctionCall(call: CallExpression) { + call.arguments.forEach { call.prevDFG.add(it) } } - fun drawDFGEgesFromNestedFields(call: CallExpression) { - call.arguments.forEach { - var nestedFields: MutableSet = getNestedFields(it) - nestedFields.forEach { call.addPrevDFG(it) } + private fun drawDFGEdgesFromNestedFields(call: CallExpression) { + call.arguments.forEach { it -> + val nestedFields: MutableSet = getNestedFields(it) + nestedFields.forEach { call.prevDFG.add(it) } } } - fun dereferenceToObjectType(originalType: Type): ObjectType? { + private fun dereferenceToObjectType(originalType: Type): ObjectType? { var type: Type = originalType var derefType: Type = type.dereference() while (!Objects.equals(type, derefType) || type is ObjectType) { @@ -75,24 +80,14 @@ class DFGExtensionPass : Pass() { derefType = type.dereference() type = tmp } - - return (type as? ObjectType) ?: null + return type as? ObjectType } - fun getNestedFields( + private fun getNestedFields( node: HasType, visitedfields: MutableSet = mutableSetOf() ): MutableSet { - var fields: MutableSet = mutableSetOf() - node.possibleSubTypes.map { - val oType: ObjectType? = dereferenceToObjectType(it) - oType?.let { - fields = it.recordDeclaration.fields.toMutableSet() - if (!visitedfields.addAll(fields)) { - return visitedfields - } - } - } + var fields = node.type.recordDeclaration!!.fields.toMutableSet() fields.forEach { it.prevDFG.filterIsInstance().forEach { getNestedFields(it, visitedfields) } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/DatabaseOperationPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/DatabaseOperationPass.kt index ee08ad1..a6c54f3 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/DatabaseOperationPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/DatabaseOperationPass.kt @@ -1,14 +1,15 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.* -abstract class DatabaseOperationPass : Pass() { +abstract class DatabaseOperationPass(ctx: TranslationContext) : TranslationResultPass(ctx) { /** * Creates a new [DatabaseConnect]. This also takes care of adding the query to the @@ -48,9 +49,9 @@ abstract class DatabaseOperationPass : Pass() { storage.forEach { if (op.isModify) { - op.addNextDFG(it) + op.nextDFG.add(it) } else { - op.addPrevDFG(it) + op.prevDFG.add(it) } } @@ -64,7 +65,8 @@ abstract class DatabaseOperationPass : Pass() { log.info("Looking for databases hosted at {}", host) return t.additionalNodes.filterIsInstance(DatabaseService::class.java).filter { - it.name == host + // TODO IK should be localName? + it.name.toString() == host } } @@ -76,13 +78,13 @@ abstract class DatabaseOperationPass : Pass() { // them as well if (target is VariableDeclaration) { target.nextDFG.forEach { - if (it is DeclaredReferenceExpression && it.refersTo == target) { + if (it is Reference && it.refersTo == target) { map[it] = obj } } // sometimes there is only an EOG edge but not a DFG target.nextEOG.forEach { - if (it is DeclaredReferenceExpression && it.refersTo == target) { + if (it is Reference && it.refersTo == target) { map[it] = obj } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/GitHubWorkflowPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/GitHubWorkflowPass.kt index 3645a2a..60dfbdf 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/GitHubWorkflowPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/GitHubWorkflowPass.kt @@ -2,15 +2,17 @@ package io.clouditor.graph.passes import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinFeature import com.fasterxml.jackson.module.kotlin.KotlinModule +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.App import io.clouditor.graph.github.Workflow import io.clouditor.graph.github.WorkflowHandler import java.nio.file.Files -class GitHubWorkflowPass : Pass() { +class GitHubWorkflowPass(ctx: TranslationContext) : TranslationResultPass(ctx) { override fun cleanup() {} override fun accept(t: TranslationResult) { @@ -22,7 +24,16 @@ class GitHubWorkflowPass : Pass() { workflowPath.toFile().walkTopDown().iterator().forEach { file -> if (file.extension == "yml") { val mapper = ObjectMapper(YAMLFactory()) - mapper.registerModule(KotlinModule()) + mapper.registerModule( + KotlinModule.Builder() + .withReflectionCacheSize(512) + .configure(KotlinFeature.NullToEmptyCollection, false) + .configure(KotlinFeature.NullToEmptyMap, false) + .configure(KotlinFeature.NullIsSameAsDefault, false) + .configure(KotlinFeature.SingletonSupport, false) + .configure(KotlinFeature.StrictNullChecks, false) + .build() + ) Files.newBufferedReader(file.toPath()).use { val workflow = mapper.readValue(it, Workflow::class.java) diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt index c957832..dc2ba06 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt @@ -1,6 +1,8 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.analysis.ValueEvaluator import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression @@ -9,35 +11,46 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator import de.fraunhofer.aisec.cpg.graph.types.PointerType import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass +import de.fraunhofer.aisec.cpg.passes.SymbolResolver +import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import de.fraunhofer.aisec.cpg.processing.IVisitor import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import io.clouditor.graph.* import io.clouditor.graph.nodes.getStorageOrCreate - -class GormDatabasePass : DatabaseOperationPass() { - override fun accept(t: TranslationResult) { - for (tu in t.translationUnits) { - val app = t.findApplicationByTU(tu) +import io.clouditor.graph.testing.LocalTestingPass +import kotlin.streams.toList + +@Suppress("UNUSED_PARAMETER") +@DependsOn(SymbolResolver::class) +@DependsOn(ControlFlowSensitiveDFGPass::class) +@DependsOn(LocalTestingPass::class, softDependency = true) +class GormDatabasePass(ctx: TranslationContext) : DatabaseOperationPass(ctx) { + override fun accept(result: TranslationResult) { + val translationUnits = + result.components.stream().flatMap { it.translationUnits.stream() }.toList() + for (tu in translationUnits) { + val app = result.findApplicationByTU(tu) // we need to find the connect first tu.accept( Strategy::AST_FORWARD, - object : IVisitor() { - fun visit(call: CallExpression) { - findConnect(t, tu, call, app) + object : IVisitor() { + fun visit(t: CallExpression) { + findConnect(result, tu, t, app) } } ) } - for (tu in t.translationUnits) { - val app = t.findApplicationByTU(tu) + for (tu in translationUnits) { + val app = result.findApplicationByTU(tu) tu.accept( Strategy::AST_FORWARD, - object : IVisitor() { - fun visit(call: MemberCallExpression) { - findQuery(t, tu, call, app) + object : IVisitor() { + fun visit(t: MemberCallExpression) { + findQuery(result, tu, t, app) } } ) @@ -50,7 +63,7 @@ class GormDatabasePass : DatabaseOperationPass() { call: CallExpression, app: Application? ) { - if (call.fqn == "postgres.Open") { + if (call.name.toString() == "postgres.Open") { call.arguments.firstOrNull()?.let { expr -> val dsn = resolveDSN(expr, app) as? String @@ -61,7 +74,6 @@ class GormDatabasePass : DatabaseOperationPass() { } val host = map?.get("host") - var port = map?.get("port")?.toShortOrNull() ?: 5432 if (host != null) { createDatabaseConnect(result, host, call, app) @@ -77,10 +89,11 @@ class GormDatabasePass : DatabaseOperationPass() { app: Application? ) { // it can either be a direct call, without any chained selectors, such as Where - val directCall = call.base.type is PointerType && call.base.type.name == "gorm.DB*" + val directCall = + call.base?.type is PointerType && call.base?.type?.name?.localName == "gorm.DB*" // make sure, the base call is really to a gorm DB object - if (call.name == "First" || call.name == "Find") { + if (call.name.localName == "First" || call.name.localName == "Find") { val calls = mutableListOf(call) if (!directCall) { @@ -94,8 +107,8 @@ class GormDatabasePass : DatabaseOperationPass() { calls += memberCall // check, if its base is already of our database type - if (memberCall.base.type is PointerType && - memberCall.base.type.name == "gorm.DB*" + if (memberCall.base?.type is PointerType && + memberCall.base!!.type.name.localName == "gorm.DB*" ) { // found it, yay! found = true @@ -121,7 +134,7 @@ class GormDatabasePass : DatabaseOperationPass() { // loop through the calls and set DFG edges calls.forEach { - when (it.name) { + when (it.name.localName) { "First" -> handleFirst(it, op) "Where" -> handleWhere(it, op) "Find" -> handleFind(it, op) @@ -134,7 +147,7 @@ class GormDatabasePass : DatabaseOperationPass() { if (op != null) { op.location = call.location } - } else if (call.name == "Create") { + } else if (call.name.localName == "Create") { val op = app?.functionalities?.filterIsInstance()?.firstOrNull()?.let { val op = @@ -155,7 +168,7 @@ class GormDatabasePass : DatabaseOperationPass() { if (op != null) { op.location = call.location } - } else if (call.name == "Update") { + } else if (call.name.localName == "Update") { val op = app?.functionalities?.filterIsInstance()?.firstOrNull()?.let { val op = @@ -187,13 +200,13 @@ class GormDatabasePass : DatabaseOperationPass() { // then, check if we have a second argument call.arguments.getOrNull(1)?.let { // add it as an incoming DFG edge - op.addPrevDFG(it) + op.prevDFG.add(it) } } private fun handleWhere(call: CallExpression, op: DatabaseQuery) { // simply add all arguments as incoming DFG edges - call.arguments.forEach { op.addPrevDFG(it) } + call.arguments.forEach { op.prevDFG.add(it) } } private fun handleFirst(call: CallExpression, op: DatabaseQuery) { @@ -210,7 +223,7 @@ class GormDatabasePass : DatabaseOperationPass() { // add a DFG edge towards our target if (target != null) { - op.addNextDFG(target) + op.nextDFG.add(target) // add storage op.to.forEach { @@ -219,9 +232,9 @@ class GormDatabasePass : DatabaseOperationPass() { // also add DFG edges from or towards the storage, depending on the query type if (op.isModify) { - op.addNextDFG(storage) + op.nextDFG.add(storage) } else { - op.addPrevDFG(storage) + op.prevDFG.add(storage) } } } @@ -229,32 +242,32 @@ class GormDatabasePass : DatabaseOperationPass() { private fun handleCreate(call: CallExpression, op: DatabaseQuery) { // create should have one argument, that specifies the object which is stored - var target = call.arguments.firstOrNull() + val target = call.arguments.firstOrNull() // add a DFG edge towards our target if (target != null) { - target.addNextDFG(op) + target.nextDFG.add(op) // add storage op.to.forEach { val storage = it.getStorageOrCreate(deriveName(target.type)) op.storage.add(storage) - op.addNextDFG(storage) + op.nextDFG.add(storage) } } } - private fun handleUpdate(call: CallExpression, op: DatabaseQuery) { + private fun handleUpdate(call: MemberCallExpression, op: DatabaseQuery) { var target = call.arguments.firstOrNull() // for update calls, a model condition may be specified which has the actual target - if (call.base.name == "Model") { - target = (call.base as CallExpression).arguments.first() + if (call.base?.name?.localName == "Model") { + target = (call.callee as CallExpression).arguments.first() } // add a DFG edge towards our target if (target != null) { - target.addNextDFG(op) + target.nextDFG.add(op) // add storage op.to.forEach { @@ -262,16 +275,13 @@ class GormDatabasePass : DatabaseOperationPass() { op.storage.add(storage) // also add DFG edge towards the storage - op.addNextDFG(storage) + op.nextDFG.add(storage) } } } private fun deriveName(type: Type): String { - // short name - val shortName = type.name.substringAfterLast(".").substringBefore("*") - - return shortName.toLowerCase() + "s" + return type.name.localName } override fun cleanup() {} @@ -284,34 +294,34 @@ class GormDatabasePass : DatabaseOperationPass() { } ?: mutableMapOf() - return ValueResolver { node, resolver -> + return ValueEvaluator { node, resolver -> when (node) { is CallExpression -> { // support for some special calls, i.e. format - if (node.name == "Sprintf") { - val str = resolver.resolve(node.arguments.firstOrNull()) as String + if (node.name.localName == "Sprintf") { + val str = resolver.evaluate(node.arguments.firstOrNull()) as String val arguments = node.arguments.drop(1) - return@ValueResolver str.format( - *arguments.map { resolver.resolve(it) }.toTypedArray() + return@ValueEvaluator str.format( + *arguments.map { resolver.evaluate(it) }.toTypedArray() ) } // a little bit of a hack, this is specific to our use case, since the // stdlib doesnt have that built-in - if (node.name == "EnvOrDefault") { + if (node.name.localName == "EnvOrDefault") { // environment lookup on python - val key = resolver.resolve(node.arguments.firstOrNull()) + val key = resolver.evaluate(node.arguments.firstOrNull()) - return@ValueResolver env[key] ?: resolver.resolve(node.arguments[1]) + return@ValueEvaluator env[key] ?: resolver.evaluate(node.arguments[1]) } // return placeholder - return@ValueResolver "{${node.name}()}" + return@ValueEvaluator "{${node.name}()}" } - else -> return@ValueResolver "{${node?.name}}" + else -> return@ValueEvaluator "{${node?.name}}" } } - .resolve(expr) + .evaluate(expr) } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/HttpClientPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/HttpClientPass.kt index 4b9a016..9bc7c91 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/HttpClientPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/HttpClientPass.kt @@ -1,11 +1,13 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.* -abstract class HttpClientPass : Pass() { +abstract class HttpClientPass(ctx: TranslationContext) : TranslationResultPass(ctx) { protected fun createHttpRequest( t: TranslationResult, @@ -17,14 +19,14 @@ abstract class HttpClientPass : Pass() { ): HttpRequest { val endpoints = getEndpointsForUrl(t, url, method) val request = HttpRequest(call, body, endpoints) - request.name = method + request.name = Name(method) request.location = call.location - endpoints.forEach { request.addNextDFG(it) } - body?.addNextDFG(request) + endpoints.forEach { request.nextDFG.add(it) } + body?.nextDFG?.add(request) // call.invokes = listOf(request) - call.addPrevDFG(request) + call.prevDFG.add(request) val i = endpoints.firstOrNull() val f = i?.handler @@ -33,7 +35,7 @@ abstract class HttpClientPass : Pass() { // remote call happens f?.prevDFG?.forEach { // for each return node, connect it to the get-call - it.addNextDFG(call) + it.nextDFG.add(call) println("Connecting $it to $call") } @@ -49,7 +51,7 @@ abstract class HttpClientPass : Pass() { ): List { log.info("Looking for endpoints for {} request to {}", method, url) - return t.additionalNodes.filterIsInstance(HttpEndpoint::class.java).filter { + return t.additionalNodes.filterIsInstance().filter { endpointMatches(it, url) && (it.method == method || it.method == null) // TODO: make methods an array } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/HttpStatusCodesPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/HttpStatusCodesPass.kt index 7267b6b..d3d93f0 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/HttpStatusCodesPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/HttpStatusCodesPass.kt @@ -1,21 +1,23 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.graph.statements.CompoundStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.HttpEndpoint -import io.clouditor.graph.additionalNodes -class HttpStatusCodesPass : Pass() { +class HttpStatusCodesPass(ctx: TranslationContext) : TranslationResultPass(ctx) { override fun cleanup() {} - override fun accept(result: TranslationResult?) { + override fun accept(result: TranslationResult) { - result?.additionalNodes?.filterIsInstance(HttpEndpoint::class.java)?.forEach { - (it.handler?.body as CompoundStatement).statements.forEach { - if (it is ReturnStatement) {} + result.additionalNodes.filterIsInstance(HttpEndpoint::class.java).forEach { + (it.handler?.body as Block).statements.forEach { + if (it is ReturnStatement) { + // TODO + } } } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/IngressInvocationPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/IngressInvocationPass.kt index 7f0b913..29df29c 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/IngressInvocationPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/IngressInvocationPass.kt @@ -1,11 +1,12 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.* import io.clouditor.graph.passes.golang.appendPath -class IngressInvocationPass : Pass() { +class IngressInvocationPass(ctx: TranslationContext) : TranslationResultPass(ctx) { override fun accept(t: TranslationResult) { // loop through services val services = t.additionalNodes.filterIsInstance(NetworkService::class.java) @@ -64,7 +65,7 @@ class IngressInvocationPass : Pass() { transportEncryption, url ) - proxy.addNextDFG(endpoint) + proxy.nextDFG.add(endpoint) t += proxy } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/KubernetesPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/KubernetesPass.kt index 7d96f40..66ac002 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/KubernetesPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/KubernetesPass.kt @@ -1,8 +1,9 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.graph.Name import io.clouditor.graph.* -import io.clouditor.graph.nodes.isInSelector import io.kubernetes.client.openapi.ApiClient import io.kubernetes.client.openapi.Configuration import io.kubernetes.client.openapi.apis.CoreV1Api @@ -13,13 +14,10 @@ import io.kubernetes.client.openapi.models.V1Service import io.kubernetes.client.util.ClientBuilder import io.kubernetes.client.util.KubeConfig import java.io.FileReader -import java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.List -import kotlin.collections.Map import kotlin.collections.emptyList -import kotlin.collections.filter import kotlin.collections.filterIsInstance import kotlin.collections.first import kotlin.collections.firstOrNull @@ -32,7 +30,8 @@ import kotlin.collections.plusAssign import kotlin.collections.toCollection import kotlin.collections.toMap -class KubernetesPass : CloudResourceDiscoveryPass() { +@Suppress("UNUSED_PARAMETER") +class KubernetesPass(ctx: TranslationContext) : CloudResourceDiscoveryPass(ctx) { override fun cleanup() {} @@ -109,14 +108,14 @@ class KubernetesPass : CloudResourceDiscoveryPass() { val container = t.computes.filterIsInstance(Container::class.java).firstOrNull { // not sure why/when there are more addresses - it.name == subset.addresses?.get(0)?.targetRef?.name + it.name.localName == subset.addresses?.get(0)?.targetRef?.name } // look for the service val service = t.additionalNodes.filterIsInstance(NetworkService::class.java).firstOrNull() { // not sure if the name is really unique, probably need the namespace later - it.name == item.metadata?.name + it.name.localName == item.metadata?.name } // some quick and dirty database heuristics @@ -148,7 +147,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { } private fun heuristics(service: NetworkService?, container: Container?, t: TranslationResult) { - if (service?.name == "postgres") { + if (service?.name?.localName == "postgres") { val db = RelationalDatabaseService( mutableListOf(), @@ -164,7 +163,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { t += db } - if (service?.name == "mongo") { + if (service?.name?.localName == "mongo") { val db = DocumentDatabaseService( mutableListOf(), @@ -192,7 +191,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { val image: Image = t.getImageByName(name) ?: Image(null, null, mapOf()).let { - name?.let { n -> it.name = n } + name?.let { n -> it.name = Name(n) } t += it it } @@ -205,7 +204,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { cluster?.geoLocation ?: GeoLocation("Europe"), meta.labels?.toMap(HashMap()) ) - c.name = meta.name ?: "" + c.name = Name(meta.name ?: "") // add env to labels with env_ prefix pod.spec?.containers?.first()?.env?.forEach { @@ -223,7 +222,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { image.application?.runsOn?.add(container) // add dataflow from image to container - container?.let { image.addNextDFG(it) } + container?.let { image.nextDFG.add(it) } return container } @@ -250,7 +249,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { mapOf() ) service.metadata?.name?.let { - node.name = it + node.name = Name(it) // also add the name as IP / host node.ips.add(it) @@ -274,7 +273,7 @@ class KubernetesPass : CloudResourceDiscoveryPass() { // look for the service (TODO: add namespace to filter) val service = t.additionalNodes.filterIsInstance(NetworkService::class.java).firstOrNull { - it.name == path.backend?.serviceName + it.name.localName == path.backend?.serviceName } val hasTLS = ingress.spec?.tls?.isEmpty() ?: false @@ -310,13 +309,4 @@ class KubernetesPass : CloudResourceDiscoveryPass() { return list } - - private fun selectContainers( - t: TranslationResult, - selector: Map - ): List { - return t.computes.filter { it is Container && it.isInSelector(selector) }.map { - it as Container - } - } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/passes/LabelExtractionPass.kt b/cloudpg/src/main/java/io/clouditor/graph/passes/LabelExtractionPass.kt index ff4cf44..a4bd526 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/LabelExtractionPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/LabelExtractionPass.kt @@ -1,28 +1,35 @@ package io.clouditor.graph.passes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Annotation import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement +import de.fraunhofer.aisec.cpg.graph.statements.expressions.AssignExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.Pass -import io.clouditor.graph.additionalNodes +import de.fraunhofer.aisec.cpg.passes.SymbolResolver +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass +import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import io.clouditor.graph.nodes.labels.* import io.clouditor.graph.plusAssign import java.util.function.Consumer import java.util.function.Predicate import java.util.stream.Collectors -class LabelExtractionPass : Pass() { +@DependsOn(SymbolResolver::class) +@DependsOn(DFGExtensionPass::class) +class LabelExtractionPass(ctx: TranslationContext) : TranslationResultPass(ctx) { - val predicatesToHandle: MutableMap, Consumer> = mutableMapOf() + private val predicatesToHandle: MutableMap, Consumer> = mutableMapOf() - var edgesCachePass: BidirectionalEdgesCachePass? = null + private var edgesCachePass: BidirectionalEdgesCachePass = BidirectionalEdgesCachePass(ctx) init { // Todo Here you can add other predicates to add labels @@ -52,7 +59,7 @@ class LabelExtractionPass : Pass() { } } - // Labels that are equal to each other are merged to reduce them in size and and allow + // Labels that are equal to each other are merged to reduce them in size and allow // associating them to Anonymization label mergeEqualLabels(t) // Connects labels and anonymization labels such that anonymization of labels can be @@ -97,7 +104,7 @@ class LabelExtractionPass : Pass() { private fun handleAnnotations(t: TranslationResult, annotationParent: Node) { annotationParent.annotations.forEach { var label: Label? = null - when (it.name) { + when (it.name.localName) { "PrivacyLabel" -> label = handlePrivacyLabelAnnotation(annotationParent, it) "Identifier" -> label = labelCreationDispatcher(annotationParent) "PseudoIdentifier" -> @@ -113,7 +120,7 @@ class LabelExtractionPass : Pass() { } inline fun anonLabelCreationDispatcher(node: Node): AnonLabel { - var anonLabel = labelCreationDispatcher(node) + val anonLabel = labelCreationDispatcher(node) val anonymizedDummyLabel = initLabel(node) anonymizedDummyLabel.labeledNodes.clear() // To mark it as Dummy @@ -128,7 +135,7 @@ class LabelExtractionPass : Pass() { * Sub-AST */ private fun handleComment(t: TranslationResult, nodeWComment: Node) { - var regexes = + val regexes = mutableMapOf( Regex("@Identifier($|\\s)") to { node, _ -> @@ -152,19 +159,21 @@ class LabelExtractionPass : Pass() { this::handleAnonPrivacyLabelComments, ) - regexes.entries.forEach { - val matches = it.key.findAll(nodeWComment.comment!!) - if (matches.toList().isNotEmpty()) { - var labels = it.value(nodeWComment, matches) - labels.forEach { + regexes.entries.forEach { it -> + val matches = nodeWComment.comment?.let { it1 -> it.key.findAll(it1) } + if (matches?.toList()?.isNotEmpty() == true) { + val labels = matches?.let { it1 -> it.value(nodeWComment, it1) } + labels?.forEach { t += it // Adding Labels to the supplementary nodes of a translation unit } } } } - inline fun anonCommentLabelCreationDispatcher(node: Node): List { - var anonLabel = labelCreationDispatcher(node) + private inline fun anonCommentLabelCreationDispatcher( + node: Node + ): List { + val anonLabel = labelCreationDispatcher(node) val anonymizedDummyLabel = initLabel(node) anonymizedDummyLabel.labeledNodes.clear() // To mark it as Dummy @@ -176,7 +185,7 @@ class LabelExtractionPass : Pass() { node: Node, matches: Sequence ): List