From 0548ba90da50fa76278b36e4bc3f15a07a962939 Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Mon, 17 Jul 2023 10:49:56 +0200 Subject: [PATCH 01/44] Merging work done in forked Repository to own CloudPG branch (#61) * updated version in build.gradle.kts * added new typescript module * bump java target form 11 to 17 * remove experimental opt-ins and comment out various error sources for now * replace String representation by Direction value and remove warning in check * simplify equality check * replace String value by Direction * replace String value by Direction * removed imports * fixed many incompatibilities between Name and String by reading the localName and creating new Name objects without parents where needed * added FIXME statements * fix all remaining errors regarding Name and String types * fix the creation of new Statements in all Handlers * apply spotless * fixed rest of the references in the handlers * bumped CPG from 6.2.0 to 6.2.2 to solve issue of TranslationResult.additionalNodes not being mutable * fix inheritance of the frontend * trying to fix Azure.kt by replacing every instance of CallExpression.base by CallExpression.callee * immediately bump CPG version to 7.0.0 in order to save redundant work on passes * adapt all passes to the new structure in CPG 7.0.0 * fix pass registration to work with CPG 7.0.0 * add RubyLanguage.kt * bump neo4j to version 4.0.5 to fix NoSuchMethod-Errors * manually register the default Languages * correcly overwrite visit in all IVisitors. Also stop trying to get "additionalNodes" from scratch since they are now a part of the result * split the searches again to prevent any order errors * make spotless * add comments * apply various fixes from oxisto's branch "cpg-v6" * fix order of evaluation leading to NPE * made code resistant against unsafe casts, null values, empty lists. Also added comments in these places. * remove unused import * replace calls of deprecated TranslationResult.translationUnits * remove heaps of warnings concerning unused variables/parameters and deprecated calls * correctly override additional "visit" functions * fix typo * manage debugging comments * add full request name parsing that was not working in previous versions * add comments regarding (missing) functionality * return all visitors into its original form, this prevents any subclasses from being visited as well * manually register the GoExtraPass * move defaultPasses after language registrations --- build.gradle.kts | 4 +- cloudpg/build.gradle.kts | 22 +-- .../src/main/java/io/clouditor/graph/App.kt | 119 +++++++--------- .../java/io/clouditor/graph/ValueResolver.kt | 2 + .../frontends/ruby/DeclarationHandler.kt | 17 +-- .../graph/frontends/ruby/ExpressionHandler.kt | 107 +++++++-------- .../graph/frontends/ruby/RubyLanguage.kt | 61 +++++++++ .../frontends/ruby/RubyLanguageFrontend.kt | 26 ++-- .../graph/frontends/ruby/StatementHandler.kt | 14 +- .../clouditor/graph/github/WorkflowHandler.kt | 36 +++-- .../java/io/clouditor/graph/nodes/Database.kt | 8 +- .../java/io/clouditor/graph/nodes/Holder.kt | 7 +- .../clouditor/graph/nodes/labels/AnonLabel.kt | 6 +- .../io/clouditor/graph/nodes/labels/Label.kt | 2 +- .../graph/nodes/labels/PrivacyLabel.kt | 3 +- .../java/io/clouditor/graph/passes/Azure.kt | 100 +++++++------- .../passes/BidirectionalEdgesCachePass.kt | 30 ++-- .../passes/CloudResourceDiscoveryPass.kt | 5 +- .../graph/passes/DFGExtensionPass.kt | 33 +++-- .../graph/passes/DatabaseOperationPass.kt | 7 +- .../graph/passes/GitHubWorkflowPass.kt | 17 ++- .../graph/passes/GormDatabasePass.kt | 63 ++++----- .../clouditor/graph/passes/HttpClientPass.kt | 11 +- .../graph/passes/HttpStatusCodesPass.kt | 14 +- .../graph/passes/IngressInvocationPass.kt | 5 +- .../clouditor/graph/passes/KubernetesPass.kt | 34 ++--- .../graph/passes/LabelExtractionPass.kt | 86 ++++++------ .../java/io/clouditor/graph/passes/LogPass.kt | 8 +- .../graph/passes/golang/GinGonicPass.kt | 128 +++++++++++------- .../graph/passes/golang/GoCryptoPass.kt | 38 +++--- .../graph/passes/golang/GolangHttpPass.kt | 57 ++++---- .../passes/golang/GolangHttpRequestPass.kt | 45 +++--- .../graph/passes/golang/GolangLogPass.kt | 59 ++++---- .../graph/passes/java/JaxRsClientPass.kt | 59 ++++---- .../clouditor/graph/passes/java/JaxRsPass.kt | 43 +++--- .../graph/passes/java/SpringBootPass.kt | 64 +++++---- .../io/clouditor/graph/passes/js/FetchPass.kt | 23 ++-- .../clouditor/graph/passes/js/JSHttpPass.kt | 62 +++++---- .../graph/passes/js/JSValueResolver.kt | 5 +- .../graph/passes/python/CryptographyPass.kt | 38 +++--- .../graph/passes/python/FlaskPass.kt | 60 ++++---- .../graph/passes/python/Psycopg2Pass.kt | 67 ++++----- .../graph/passes/python/PyMongoPass.kt | 70 +++++----- .../graph/passes/python/PythonLogPass.kt | 18 +-- .../passes/python/PythonValueResolver.kt | 9 +- .../graph/passes/python/RequestsPass.kt | 37 +++-- .../graph/passes/ruby/WebBrickPass.kt | 44 +++--- .../graph/testing/LocalTestingPass.kt | 30 ++-- .../io/clouditor/graph/DetectabilityTest.kt | 5 + 49 files changed, 1004 insertions(+), 804 deletions(-) create mode 100644 cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguage.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0c664b1..5c4b437 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.8.0" apply false + kotlin("plugin.serialization") version "1.8.0" apply false id("com.diffplug.spotless") version "5.12.1" } diff --git a/cloudpg/build.gradle.kts b/cloudpg/build.gradle.kts index 1aab640..fb414b7 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. * @@ -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,7 +58,7 @@ 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/11.0/cdt-11.0.0/plugins") metadataSources { artifact() } @@ -71,20 +70,23 @@ repositories { dependencies { implementation("org.junit.jupiter:junit-jupiter:5.7.0") - val version = "4.6.0" + val version = "7.0.0" 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 ("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.5") + api("org.neo4j", "neo4j-ogm", "4.0.5") + api("org.neo4j", "neo4j-ogm-bolt-driver", "4.0.5") implementation(platform("org.jetbrains.kotlin:kotlin-bom")) diff --git a/cloudpg/src/main/java/io/clouditor/graph/App.kt b/cloudpg/src/main/java/io/clouditor/graph/App.kt index 658ab9f..26c7bbf 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/App.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/App.kt @@ -4,14 +4,18 @@ 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 de.fraunhofer.aisec.cpg.passes.GoExtraPass +import io.clouditor.graph.frontends.ruby.RubyLanguage import io.clouditor.graph.nodes.Builder import io.clouditor.graph.passes.* import io.clouditor.graph.passes.golang.* @@ -35,12 +39,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"], @@ -93,12 +91,13 @@ object App : Callable { 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 -> @@ -123,67 +122,55 @@ object App : Callable { 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 - ) + .registerLanguage(RubyLanguage()) + .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() + // This pass needs to be executed to have all VariableDeclarations + .registerPass(GoExtraPass::class) + .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 +183,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,11 +198,6 @@ 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 { 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..e5b160b 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( /** 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 index 012daf4..4ecbb7e 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/DeclarationHandler.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/DeclarationHandler.kt @@ -1,9 +1,9 @@ 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.newParamVariableDeclaration import de.fraunhofer.aisec.cpg.graph.types.UnknownType import org.jruby.ast.* @@ -19,14 +19,11 @@ class DeclarationHandler(lang: RubyLanguageFrontend) : return null } - val param = - NodeBuilder.newMethodParameterIn( - node.name.idString(), - UnknownType.getUnknownType(), - false, - lang.getCodeFromRawNode(node) - ) - - return param + return newParamVariableDeclaration( + node.name.idString(), + UnknownType.getUnknownType(frontend.language), + false, + frontend.getCodeFromRawNode(node) + ) } } 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 index 047e090..ddb5413 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/ExpressionHandler.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/ExpressionHandler.kt @@ -1,13 +1,13 @@ 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.* 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.* +import org.jruby.ast.Node class ExpressionHandler(lang: RubyLanguageFrontend) : Handler({ ProblemExpression() }, lang) { @@ -26,7 +26,7 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : if (node !is FCallNode) { return null } - + // FIXME: what is this? Unimplemented or intentional? return null } @@ -35,20 +35,22 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : return null } - var binOp = NodeBuilder.newBinaryOperator("=", lang.getCodeFromRawNode(node)) + val binOp = newBinaryOperator("=", frontend.getCodeFromRawNode(node)) - var base = this.handle(node.receiverNode) as? Expression - var expr = - NodeBuilder.newMemberExpression( - base, - UnknownType.getUnknownType(), + val base = + this.handle(node.receiverNode) as? Expression + ?: return ProblemExpression("could not parse base") + val expr = + newMemberExpression( node.name.idString(), + base, + UnknownType.getUnknownType(frontend.language), "=", - lang.getCodeFromRawNode(base) + frontend.getCodeFromRawNode(base) ) binOp.lhs = expr - binOp.rhs = this.handle(node.argsNode) as? Expression + (this.handle(node.argsNode) as? Expression)?.let { binOp.rhs = it } return expr } @@ -58,14 +60,11 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : return null } - val ref = - NodeBuilder.newDeclaredReferenceExpression( - node.name.idString(), - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(node) - ) - - return ref + return newDeclaredReferenceExpression( + node.name.idString(), + UnknownType.getUnknownType(language), + frontend.getCodeFromRawNode(node) + ) } private fun handleAssignableNode(node: Node?): Statement? { @@ -82,23 +81,23 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : // either a binary operator or a variable declaration val lhs = - NodeBuilder.newDeclaredReferenceExpression( + newDeclaredReferenceExpression( name.idString(), - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(node) + UnknownType.getUnknownType(language), + frontend.getCodeFromRawNode(node) ) val rhs = this.handle((node as AssignableNode).valueNode) as? Expression // can we resolve it? - var decl = this.lang.scopeManager.resolveReference(lhs) + var decl = frontend.scopeManager.resolveReference(lhs) if (decl == null) { - val stmt = NodeBuilder.newDeclarationStatement(lang.getCodeFromRawNode(lhs)) + val stmt = newDeclarationStatement(frontend.getCodeFromRawNode(node)) decl = - NodeBuilder.newVariableDeclaration( + newVariableDeclaration( lhs.name, - UnknownType.getUnknownType(), - lang.getCodeFromRawNode(lhs), + UnknownType.getUnknownType(language), + frontend.getCodeFromRawNode(node), false ) decl.initializer = rhs @@ -108,9 +107,9 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : return stmt } - val binOp = NodeBuilder.newBinaryOperator("=", lang.getCodeFromRawNode(node)) + val binOp = newBinaryOperator("=", frontend.getCodeFromRawNode(node)) binOp.lhs = lhs - binOp.rhs = rhs + rhs?.let { binOp.rhs = it } return binOp } @@ -120,25 +119,19 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : return null } - val base = handle(node.receiverNode) as? Expression - val member = null + val base = + handle(node.receiverNode) as? Expression + ?: return ProblemExpression("could not parse base") + val callee = newMemberExpression(node.name.asJavaString(), base) - val mce = - NodeBuilder.newMemberCallExpression( - node.name.asJavaString(), - node.name.asJavaString(), - base, - member, - ".", - lang.getCodeFromRawNode(node) - ) + val mce = newMemberCallExpression(callee, false, frontend.getCodeFromRawNode(node)) for (arg in node.argsNode?.childNodes() ?: emptyList()) { - mce.addArgument(handle(arg) as? Expression) + mce.addArgument(handle(arg) as Expression) } // add the iterNode as last argument - node.iterNode?.let { mce.addArgument(handle(it) as? Expression) } + node.iterNode?.let { mce.addArgument(handle(it) as Expression) } return mce } @@ -151,24 +144,23 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : // 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)) + val func = newFunctionDeclaration("", frontend.getCodeFromRawNode(node)) - lang.scopeManager.enterScope(func) + frontend.scopeManager.enterScope(func) for (arg in node.argsNode.args) { - val param = lang.declarationHandler.handle(arg) - lang.scopeManager.addDeclaration(param) + val param = frontend.declarationHandler.handle(arg) + frontend.scopeManager.addDeclaration(param) } - func.body = lang.statementHandler.handle(node.bodyNode) + func.body = frontend.statementHandler.handle(node.bodyNode) - lang.scopeManager.leaveScope(func) + frontend.scopeManager.leaveScope(func) - var def = NodeBuilder.newDeclarationStatement(lang.getCodeFromRawNode(node)) + val def = newDeclarationStatement(frontend.getCodeFromRawNode(node)) def.singleDeclaration = func - var cse = - NodeBuilder.newCompoundStatementExpression(lang.getCodeFromRawNode(node).toString()) + val cse = newCompoundStatementExpression(frontend.getCodeFromRawNode(node)) cse.statement = def return cse @@ -179,13 +171,10 @@ class ExpressionHandler(lang: RubyLanguageFrontend) : return null } - val literal = - NodeBuilder.newLiteral( - String(node.value.bytes()), - TypeParser.createFrom("string", false), - lang.getCodeFromRawNode(node) - ) - - return literal + return newLiteral( + String(node.value.bytes()), + parseType("string"), + frontend.getCodeFromRawNode(node) + ) } } diff --git a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguage.kt b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguage.kt new file mode 100644 index 0000000..9d26683 --- /dev/null +++ b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguage.kt @@ -0,0 +1,61 @@ +package io.clouditor.graph.frontends.ruby + +import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.graph.Name +import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression +import de.fraunhofer.aisec.cpg.graph.types.* +import kotlin.reflect.KClass + +/** The Ruby Language */ +class RubyLanguage() : + Language(), + HasDefaultArguments, + HasClasses, + HasSuperClasses, + HasShortCircuitOperators { + override val fileExtensions = listOf("rb") + override val namespaceDelimiter = "::" + @Transient override val frontend: KClass = RubyLanguageFrontend::class + override val superClassKeyword = "super" + override val conjunctiveOperators = listOf("&&") + override val disjunctiveOperators = listOf("||") + + @Transient + /** See [The RubySpec](https://github.com/ruby/spec) */ + override val builtInTypes = + mapOf( + // The bit width of the Integer type in Ruby is only limited by your memory + "Integer" to IntegerType("Integer", null, this, NumericType.Modifier.SIGNED), + "Float" to FloatingPointType("Float", 64, this, NumericType.Modifier.SIGNED), + "String" to StringType("String", this), + // The bit width of Booleans is not defined in the specification and + // implementation-dependant + "Boolean" to BooleanType("Boolean", null, this, NumericType.Modifier.NOT_APPLICABLE) + ) + + override val compoundAssignmentOperators = + setOf( + "+=", // Addition assignment + "-=", // Subtraction assignment + "*=", // Multiplication assignment + "/=", // Division assignment + "%=", // Modulo assignment + "**=", // Exponentiation assignment + "<<=", // Left shift assignment + ">>=", // Right shift assignment + "&=", // Bitwise AND assignment + "|=", // Bitwise OR assignment + "^=" // Bitwise XOR assignment + ) + + override fun handleSuperCall( + callee: MemberExpression, + curClass: RecordDeclaration, + scopeManager: ScopeManager, + recordMap: Map + ): Boolean { + TODO("Not yet implemented") + } +} 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 index 122066b..4d43c98 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguageFrontend.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/RubyLanguageFrontend.kt @@ -1,10 +1,11 @@ package io.clouditor.graph.frontends.ruby -import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.Language 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.graph.newFunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.newTranslationUnitDeclaration import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import org.checkerframework.checker.nullness.qual.NonNull @@ -14,19 +15,15 @@ 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") - } - +class RubyLanguageFrontend( + language: Language, + ctx: @NonNull TranslationContext +) : LanguageFrontend(language, ctx) { 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) @@ -43,17 +40,14 @@ class RubyLanguageFrontend(config: @NonNull TranslationConfiguration, scopeManag } private fun handleRootNode(node: RootNode, file: File): TranslationUnitDeclaration { - val tu = NodeBuilder.newTranslationUnitDeclaration(node.file, getCodeFromRawNode(node)) + val tu = 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) - ) + newFunctionDeclaration(file.nameWithoutExtension + "_global", getCodeFromRawNode(node)) scopeManager.enterScope(func) 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 index e9efd04..c561ecd 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/StatementHandler.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/frontends/ruby/StatementHandler.kt @@ -1,7 +1,8 @@ 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.newCompoundStatement +import de.fraunhofer.aisec.cpg.graph.newReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.CompoundStatement import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.Statement @@ -22,11 +23,10 @@ class StatementHandler(lang: RubyLanguageFrontend) : } blockNode.containsVariableAssignment() - val compoundStatement = NodeBuilder.newCompoundStatement(lang.getCodeFromRawNode(blockNode)) + val compoundStatement = newCompoundStatement(frontend.getCodeFromRawNode(blockNode)) for (node in blockNode) { - val statement = lang.expressionHandler.handle(node) - + val statement = frontend.expressionHandler.handle(node) statement?.let { compoundStatement.addStatement(it) } } @@ -34,13 +34,13 @@ class StatementHandler(lang: RubyLanguageFrontend) : // get the last statement var lastStatement: Statement? = null - if (!statements.isEmpty()) { - lastStatement = statements.get(statements.size - 1) + if (statements.isNotEmpty()) { + lastStatement = statements[statements.size - 1] } // add an implicit return statement, if there is none if (lastStatement !is ReturnStatement) { - val returnStatement = NodeBuilder.newReturnStatement("return") + val returnStatement = newReturnStatement("return") returnStatement.isImplicit = true compoundStatement.addStatement(returnStatement) } 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..b3bd297 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,9 @@ 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 -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 +45,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 +67,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 +100,7 @@ class WorkflowHandler(private val result: TranslationResult, val rootPath: Path) compute.geoLocation, mutableMapOf() ) - networkService.name = host + networkService.name = Name(host) result += networkService } @@ -108,9 +119,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 +134,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,18 +142,18 @@ 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 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/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..61b6622 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/Azure.kt @@ -16,15 +16,16 @@ 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.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.* @@ -35,19 +36,19 @@ 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 +62,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 +73,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 +94,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 +109,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 @@ -138,11 +139,12 @@ 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 + 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 DeclaredReferenceExpression) .refersTo == client } @@ -194,23 +196,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.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.name = Name(request.type, null) t += request @@ -232,7 +234,7 @@ class AzureClientSDKPass : Pass() { val s = sRef.refersTo as ParamVariableDeclaration // 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 +243,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,7 +288,7 @@ 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 @@ -296,7 +297,7 @@ class AzurePass : CloudResourceDiscoveryPass() { // 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 +315,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 +352,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 +374,7 @@ class AzurePass : CloudResourceDiscoveryPass() { log = t.additionalNodes.filterIsInstance(ResourceLogging::class.java).firstOrNull { - it.name == shortName + it.name.localName == shortName } } } @@ -453,7 +453,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 +461,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 +477,7 @@ class AzurePass : CloudResourceDiscoveryPass() { t.locationForRegion(account.region()), mapOf() ) - storage.name = blob.name() + storage.name = Name(blob.name(), null) storageList += storage } @@ -507,7 +504,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 +524,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 +532,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..8e86651 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/DFGExtensionPass.kt @@ -1,5 +1,6 @@ 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 @@ -9,7 +10,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.TranslationResultPass import io.clouditor.graph.plusAssign import java.util.* @@ -19,7 +20,7 @@ import java.util.* * 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() { +class DFGExtensionPass(ctx: TranslationContext) : TranslationResultPass(ctx) { override fun accept(t: TranslationResult) { // loop through services @@ -27,7 +28,9 @@ 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" } @@ -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) } } } - fun redirectDFGThroughFunctionCall(call: CallExpression) { + private fun redirectDFGThroughFunctionCall(call: CallExpression) { call.arguments.forEach { call.addPrevDFG(it) } } - fun drawDFGEgesFromNestedFields(call: CallExpression) { - call.arguments.forEach { - var nestedFields: MutableSet = getNestedFields(it) + private fun drawDFGEgesFromNestedFields(call: CallExpression) { + call.arguments.forEach { it -> + val nestedFields: MutableSet = getNestedFields(it) nestedFields.forEach { call.addPrevDFG(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,19 +80,19 @@ class DFGExtensionPass : Pass() { derefType = type.dereference() type = tmp } - - return (type as? ObjectType) ?: null + // FIXME: always null + return type as? ObjectType } - fun getNestedFields( + private fun getNestedFields( node: HasType, visitedfields: MutableSet = mutableSetOf() ): MutableSet { var fields: MutableSet = mutableSetOf() - node.possibleSubTypes.map { + node.possibleSubTypes.map { it -> val oType: ObjectType? = dereferenceToObjectType(it) oType?.let { - fields = it.recordDeclaration.fields.toMutableSet() + fields = it.recordDeclaration!!.fields.toMutableSet() if (!visitedfields.addAll(fields)) { return 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..f429917 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.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 @@ -64,7 +65,7 @@ abstract class DatabaseOperationPass : Pass() { log.info("Looking for databases hosted at {}", host) return t.additionalNodes.filterIsInstance(DatabaseService::class.java).filter { - it.name == host + it.name.toString() == host } } 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..d569847 100644 --- a/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt +++ b/cloudpg/src/main/java/io/clouditor/graph/passes/GormDatabasePass.kt @@ -1,5 +1,6 @@ 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.TranslationUnitDeclaration @@ -14,30 +15,33 @@ 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) +@Suppress("UNUSED_PARAMETER") +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 +54,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 +65,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 +80,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 +98,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 +125,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 +138,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 +159,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 = @@ -229,7 +233,7 @@ 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) { @@ -245,11 +249,11 @@ class GormDatabasePass : DatabaseOperationPass() { } } - 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 @@ -268,10 +272,7 @@ class GormDatabasePass : DatabaseOperationPass() { } 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() {} @@ -288,7 +289,7 @@ class GormDatabasePass : DatabaseOperationPass() { when (node) { is CallExpression -> { // support for some special calls, i.e. format - if (node.name == "Sprintf") { + if (node.name.localName == "Sprintf") { val str = resolver.resolve(node.arguments.firstOrNull()) as String val arguments = node.arguments.drop(1) @@ -299,7 +300,7 @@ class GormDatabasePass : DatabaseOperationPass() { // 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()) 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..429ba3b 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, @@ -15,9 +17,12 @@ abstract class HttpClientPass : Pass() { body: Expression?, app: Application? ): HttpRequest { + // FIXME (TODO): url = null! + // FIXME: body.refersTo = null! (DeclaredReferenceExpression) ä + // -> named argument "json = personal_data" 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) } 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..ea24a21 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.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 { + result.additionalNodes.filterIsInstance(HttpEndpoint::class.java).forEach { (it.handler?.body as CompoundStatement).statements.forEach { - if (it is ReturnStatement) {} + if (it is ReturnStatement) { + // FIXME I think this is missing something + } } } } 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..e7bce0f 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) 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..d974a7d 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 { @@ -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..c6013ae 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,29 @@ 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.Expression 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.TranslationResultPass 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() { +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 @@ -97,7 +98,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 +114,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 +129,7 @@ class LabelExtractionPass : Pass() { * Sub-AST */ private fun handleComment(t: TranslationResult, nodeWComment: Node) { - var regexes = + val regexes = mutableMapOf( Regex("@Identifier($|\\s)") to { node, _ -> @@ -152,10 +153,10 @@ class LabelExtractionPass : Pass() { this::handleAnonPrivacyLabelComments, ) - regexes.entries.forEach { + regexes.entries.forEach { it -> val matches = it.key.findAll(nodeWComment.comment!!) if (matches.toList().isNotEmpty()) { - var labels = it.value(nodeWComment, matches) + val labels = it.value(nodeWComment, matches) labels.forEach { t += it // Adding Labels to the supplementary nodes of a translation unit } @@ -163,8 +164,10 @@ class LabelExtractionPass : Pass() { } } - 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 +179,7 @@ class LabelExtractionPass : Pass() { node: Node, matches: Sequence ): List