From b4d7c01c4eb85a5158fd75c01729f33fb8933bfc Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Tue, 14 Jan 2025 17:49:52 +0100 Subject: [PATCH 1/9] Working on new Codyze --- cpg-codyze/build.gradle.kts | 52 +++++++++++++++++++ .../aisec/cpg/codyze/Application.kt | 30 +++++++++++ settings.gradle.kts | 2 + 3 files changed, 84 insertions(+) create mode 100644 cpg-codyze/build.gradle.kts create mode 100644 cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt diff --git a/cpg-codyze/build.gradle.kts b/cpg-codyze/build.gradle.kts new file mode 100644 index 00000000000..84db605766d --- /dev/null +++ b/cpg-codyze/build.gradle.kts @@ -0,0 +1,52 @@ +import groovy.util.Node +import groovy.util.NodeList + +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +plugins { + id("cpg.application-conventions") + id("cpg.frontend-dependency-conventions") +} + +application { + mainClass.set("de.fraunhofer.aisec.cpg.codyze.ApplicationKt") +} + +publishing { + publications { + named("cpg-codyze") { + pom { + artifactId = "cpg-codyze" + name.set("Code Property Graph - Codyze") + description.set("The one-stop shop to the code property graph") + } + } + } +} + +dependencies { + +} diff --git a/cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt b/cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt new file mode 100644 index 00000000000..b6c14ff2767 --- /dev/null +++ b/cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.codyze + +fun main() { + println("Hello, Codyze!") +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 91edf9c911e..b491ddc92c0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,8 @@ include(":cpg-neo4j") include(":cpg-console") include(":cpg-concepts") +include(":cpg-codyze") + // this code block also exists in the root build.gradle.kts val enableJavaFrontend: Boolean by extra { val enableJavaFrontend: String? by settings From 4559f12d15348eecb4d7e7057f73370cf88c5c3d Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Tue, 14 Jan 2025 22:16:08 +0100 Subject: [PATCH 2/9] Adding classes for security goals --- buildSrc/build.gradle.kts | 1 + .../kotlin/cpg.common-conventions.gradle.kts | 1 + codyze-compliance/build.gradle.kts | 46 +++++++++++++ .../aisec/cpg/codyze/compliance/Command.kt | 49 ++++++++++++++ .../cpg/codyze/compliance/SecurityGoal.kt | 67 +++++++++++++++++++ {cpg-codyze => codyze}/build.gradle.kts | 12 ++-- .../aisec/cpg/codyze/Application.kt | 15 ++++- .../aisec/cpg/frontends/cxx/.gitignore | 1 - gradle/libs.versions.toml | 3 + settings.gradle.kts | 3 +- 10 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 codyze-compliance/build.gradle.kts create mode 100644 codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt create mode 100644 codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt rename {cpg-codyze => codyze}/build.gradle.kts (87%) rename {cpg-codyze => codyze}/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt (73%) delete mode 100644 cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6418a1982ad..cee4efd64bb 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -8,6 +8,7 @@ repositories { dependencies { implementation(libs.kotlin.gradle) + implementation(libs.kotlin.serialization) implementation(libs.dokka.gradle) implementation(libs.kover.gradle) implementation(libs.spotless.gradle) diff --git a/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts b/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts index c3ffde19690..c4972fa575c 100644 --- a/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/cpg.common-conventions.gradle.kts @@ -12,6 +12,7 @@ plugins { signing `maven-publish` kotlin("jvm") + kotlin("plugin.serialization") id("org.jetbrains.dokka") } diff --git a/codyze-compliance/build.gradle.kts b/codyze-compliance/build.gradle.kts new file mode 100644 index 00000000000..b726773f068 --- /dev/null +++ b/codyze-compliance/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +plugins { + id("cpg.common-conventions") +} + +publishing { + publications { + named("codyze-compliance") { + pom { + artifactId = "codyze" + name.set("Codyze - Compliance Module") + description.set("The compliance module of Codyze") + } + } + } +} + +dependencies { + implementation(libs.clikt) + implementation(project(":cpg-core")) + implementation("com.charleskorn.kaml:kaml:0.67.0") +} diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt new file mode 100644 index 00000000000..1803e727e99 --- /dev/null +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.codyze.compliance + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands + +class Command : CliktCommand() { + override fun run() { + echo("Hello from compliance") + } +} + +class ScanCommand : CliktCommand() { + override fun run() { + echo("Hello from scan") + } +} + +class ListSecurityGoals : CliktCommand() { + override fun run() { + echo("Hello from listSecurityGoals") + } +} + +var ComplianceCommand = Command().subcommands(ScanCommand(), ListSecurityGoals()) diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt new file mode 100644 index 00000000000..b4cef6ccb2a --- /dev/null +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt @@ -0,0 +1,67 @@ +package de.fraunhofer.aisec.cpg.codyze.compliance + +import com.charleskorn.kaml.Yaml +import de.fraunhofer.aisec.cpg.graph.Name +import de.fraunhofer.aisec.cpg.graph.OverlayNode +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.extension +import kotlin.io.path.readText +import kotlin.io.path.walk + +@Serializable +class SecurityGoal( + @Serializable(with = NameSerializer::class) + override var name: Name +) : OverlayNode() { +} + +class SecurityObjective : OverlayNode() { + +} + +class SecurityStatement : OverlayNode() { + +} + +/** + * A custom serializer for the Name class. + */ +class NameSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(Name::class.qualifiedName!!, PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Name) { + encoder.encodeString(value.localName) + } + + override fun deserialize(decoder: Decoder): Name { + return Name(decoder.decodeString()) + } + +} + +@OptIn(ExperimentalPathApi::class) +fun loadSecurityGoals(directory: String): List { + // Walk the directory and load all YAML files + Path(directory).walk().filter { it.extension == "yaml" }.forEach { + val goals = loadSecurityGoal(it) + } + + return listOf() +} + +/** + * Load a single security goal from a file. + */ +fun loadSecurityGoal(file: Path): SecurityGoal { + return Yaml.default.decodeFromString(file.readText()) +} diff --git a/cpg-codyze/build.gradle.kts b/codyze/build.gradle.kts similarity index 87% rename from cpg-codyze/build.gradle.kts rename to codyze/build.gradle.kts index 84db605766d..e3bfa2fd521 100644 --- a/cpg-codyze/build.gradle.kts +++ b/codyze/build.gradle.kts @@ -1,6 +1,3 @@ -import groovy.util.Node -import groovy.util.NodeList - /* * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. * @@ -37,10 +34,10 @@ application { publishing { publications { - named("cpg-codyze") { + named("codyze") { pom { - artifactId = "cpg-codyze" - name.set("Code Property Graph - Codyze") + artifactId = "codyze" + name.set("Codyze") description.set("The one-stop shop to the code property graph") } } @@ -48,5 +45,6 @@ publishing { } dependencies { - + implementation(libs.clikt) + implementation(project(":codyze-compliance")) } diff --git a/cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt b/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt similarity index 73% rename from cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt rename to codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt index b6c14ff2767..cd2ff8585b1 100644 --- a/cpg-codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt +++ b/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt @@ -25,6 +25,17 @@ */ package de.fraunhofer.aisec.cpg.codyze -fun main() { - println("Hello, Codyze!") +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.main +import com.github.ajalt.clikt.core.subcommands +import de.fraunhofer.aisec.cpg.codyze.compliance.ComplianceCommand + +class Codyze : CliktCommand() { + override fun run() { + echo("Hello, World!") + } +} + +fun main(args: Array) { + Codyze().subcommands(ComplianceCommand).main(args) } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore deleted file mode 100644 index 173243b0d37..00000000000 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore +++ /dev/null @@ -1 +0,0 @@ -LibrariesTest.kt \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f43fae63e12..ab8a80e2d99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,7 @@ spotless = "7.0.1" nexus-publish = "2.0.0" sootup = "1.3.0" slf4j = "2.0.16" +clikt = "5.0.2" [libraries] kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} @@ -48,6 +49,7 @@ jruby = { module = "org.jruby:jruby-core", version = "9.4.3.0" } jline = { module = "org.jline:jline", version = "3.28.0" } antlr-runtime = { module = "org.antlr:antlr4-runtime", version = "4.8-1" } # we cannot upgrade until ki-shell upgrades this! ini4j = { module = "org.ini4j:ini4j", version = "0.5.4" } +clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" } # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.11.0"} @@ -55,6 +57,7 @@ mockito = { module = "org.mockito:mockito-core", version = "5.15.2"} # plugins needed for build.gradle.kts in buildSrc kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } dokka-gradle = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version = "2.0.0" } # the dokka plugin is slightly behind the main Kotlin release cycle dokka-versioning = { module = "org.jetbrains.dokka:versioning-plugin", version = "2.0.0"} kover-gradle = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version = "0.9.0" } diff --git a/settings.gradle.kts b/settings.gradle.kts index b491ddc92c0..a201d4f3ca2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,8 @@ include(":cpg-neo4j") include(":cpg-console") include(":cpg-concepts") -include(":cpg-codyze") +include(":codyze") +include(":codyze-compliance") // this code block also exists in the root build.gradle.kts val enableJavaFrontend: Boolean by extra { From fe5dcd810b5cfde1a76562fb0a7628a45b28b7a1 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 14:15:22 +0100 Subject: [PATCH 3/9] Added compliance module with security-goals --- codyze-compliance/build.gradle.kts | 2 +- .../cpg/codyze/compliance/SecurityGoal.kt | 109 +++++++++++++----- .../cpg/codyze/compliance/SecurityGoalTest.kt | 41 +++++++ .../test/resources/security-goals/goal1.yaml | 12 ++ .../fraunhofer/aisec/cpg/graph/OverlayNode.kt | 2 +- 5 files changed, 134 insertions(+), 32 deletions(-) create mode 100644 codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt create mode 100644 codyze-compliance/src/test/resources/security-goals/goal1.yaml diff --git a/codyze-compliance/build.gradle.kts b/codyze-compliance/build.gradle.kts index b726773f068..9c5489524f5 100644 --- a/codyze-compliance/build.gradle.kts +++ b/codyze-compliance/build.gradle.kts @@ -24,7 +24,7 @@ * */ plugins { - id("cpg.common-conventions") + id("cpg.frontend-conventions") } publishing { diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt index b4cef6ccb2a..65854f1d5af 100644 --- a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt @@ -1,8 +1,40 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ package de.fraunhofer.aisec.cpg.codyze.compliance import com.charleskorn.kaml.Yaml +import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.OverlayNode +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.extension +import kotlin.io.path.readText +import kotlin.io.path.walk import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString @@ -11,33 +43,31 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import java.nio.file.Path -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.Path -import kotlin.io.path.extension -import kotlin.io.path.readText -import kotlin.io.path.walk @Serializable -class SecurityGoal( - @Serializable(with = NameSerializer::class) - override var name: Name -) : OverlayNode() { -} - -class SecurityObjective : OverlayNode() { - -} - -class SecurityStatement : OverlayNode() { +data class SecurityGoal( + @Serializable(with = NameSerializer::class) override var name: Name, + val description: String, + val components: List<@Serializable(with = ComponentSerializer::class) Component?> = listOf(), + val assumptions: List = listOf(), + val restrictions: List = listOf(), + val objectives: List, +) : OverlayNode() -} +@Serializable +class SecurityObjective( + @Serializable(with = NameSerializer::class) override var name: Name, + val description: String, + val statements: List, + val components: List<@Serializable(with = ComponentSerializer::class) Component?> = listOf(), + val assumptions: List = listOf(), + val restrictions: List = listOf(), +) : OverlayNode() -/** - * A custom serializer for the Name class. - */ +/** A custom serializer for the [Name] class. */ class NameSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(Name::class.qualifiedName!!, PrimitiveKind.STRING) + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor(Name::class.qualifiedName!!, PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: Name) { encoder.encodeString(value.localName) @@ -46,22 +76,41 @@ class NameSerializer : KSerializer { override fun deserialize(decoder: Decoder): Name { return Name(decoder.decodeString()) } - } -@OptIn(ExperimentalPathApi::class) -fun loadSecurityGoals(directory: String): List { - // Walk the directory and load all YAML files - Path(directory).walk().filter { it.extension == "yaml" }.forEach { - val goals = loadSecurityGoal(it) +/** + * A custom serializer for the [Component] class. + */ +class ComponentSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor(Component::class.qualifiedName!!, PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Component?) { + if (value != null) { + encoder.encodeString(value.name.toString()) + } } - return listOf() + override fun deserialize(decoder: Decoder): Component? { + // TODO: find component by name somehow + return null + } } /** - * Load a single security goal from a file. + * Load all security goals from a directory. */ +@OptIn(ExperimentalPathApi::class) +fun loadSecurityGoals(directory: String): List { + // Walk the directory and load all YAML files + return Path(directory) + .walk() + .filter { it.extension == "yaml" } + .toList() + .map { loadSecurityGoal(it) } +} + +/** Load a single security goal from a file. */ fun loadSecurityGoal(file: Path): SecurityGoal { return Yaml.default.decodeFromString(file.readText()) } diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt new file mode 100644 index 00000000000..c93454e42d5 --- /dev/null +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.codyze.compliance + +import kotlin.test.* + +class SecurityGoalTest { + @Test + fun testLoad() { + val goals = loadSecurityGoals("src/test/resources/security-goals") + val goal1 = goals.firstOrNull() + assertNotNull(goal1) + + val objective1 = goal1.objectives.firstOrNull() + assertNotNull(objective1) + assertEquals("Good encryption", objective1.name.localName) + } +} diff --git a/codyze-compliance/src/test/resources/security-goals/goal1.yaml b/codyze-compliance/src/test/resources/security-goals/goal1.yaml new file mode 100644 index 00000000000..de0f02ad068 --- /dev/null +++ b/codyze-compliance/src/test/resources/security-goals/goal1.yaml @@ -0,0 +1,12 @@ +name: Goal1 +description: Make it very secure +components: + - auth + - webserver +assumptions: + - Third party code is very good +objectives: + - name: Good encryption + description: Encryption used is very good + statements: + - For each algorithm A, if A is used, then A must be a very good cryptographic algorithm diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt index 28d0ac998ed..4cab892b898 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt @@ -34,7 +34,7 @@ import org.neo4j.ogm.annotation.Relationship * Represents an extra node added to the CPG. These nodes can live next to the regular nodes, * typically having shared edges to extend the original graph. */ -abstract class OverlayNode() : Node() { +abstract class OverlayNode : Node() { init { this.language = NoLanguage From 73db45cfa9229744cb148d551f938e7625593013 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 14:51:29 +0100 Subject: [PATCH 4/9] Added CODEOWNERS --- .github/CODEOWNERS | 4 ++++ .../aisec/cpg/codyze/compliance/SecurityGoal.kt | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1299bb80d1d..4dae62f6ce0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,4 +30,8 @@ build.gradle.kts @oxisto .github @oxisto cpg-language-ini @maximiliankaul + cpg-concepts @maximiliankaul + +codyze @fwendland +codyze-compliance @fwendland @oxisto diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt index 65854f1d5af..a1bd13e1a0c 100644 --- a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt @@ -78,9 +78,7 @@ class NameSerializer : KSerializer { } } -/** - * A custom serializer for the [Component] class. - */ +/** A custom serializer for the [Component] class. */ class ComponentSerializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(Component::class.qualifiedName!!, PrimitiveKind.STRING) @@ -97,9 +95,7 @@ class ComponentSerializer : KSerializer { } } -/** - * Load all security goals from a directory. - */ +/** Load all security goals from a directory. */ @OptIn(ExperimentalPathApi::class) fun loadSecurityGoals(directory: String): List { // Walk the directory and load all YAML files From 6cbab60e5097914416fe31ee98c2959f69665abc Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 15:15:42 +0100 Subject: [PATCH 5/9] Reverted weird changes in cpg modules --- .../main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt | 2 +- .../kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt index 4cab892b898..28d0ac998ed 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/OverlayNode.kt @@ -34,7 +34,7 @@ import org.neo4j.ogm.annotation.Relationship * Represents an extra node added to the CPG. These nodes can live next to the regular nodes, * typically having shared edges to extend the original graph. */ -abstract class OverlayNode : Node() { +abstract class OverlayNode() : Node() { init { this.language = NoLanguage diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore new file mode 100644 index 00000000000..173243b0d37 --- /dev/null +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/.gitignore @@ -0,0 +1 @@ +LibrariesTest.kt \ No newline at end of file From 8606ad7e9b023c826f9a436799ddfb3f54b83557 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 15:37:04 +0100 Subject: [PATCH 6/9] Added test for compliance command --- .../aisec/cpg/codyze/compliance/Command.kt | 36 +++++++++++++---- .../cpg/codyze/compliance/SecurityGoal.kt | 10 ++--- .../cpg/codyze/compliance/CommandTest.kt | 40 +++++++++++++++++++ .../cpg/codyze/compliance/SecurityGoalTest.kt | 3 +- .../aisec/cpg/codyze/Application.kt | 7 +--- 5 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt index 1803e727e99..e67e78d4786 100644 --- a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/Command.kt @@ -27,23 +27,45 @@ package de.fraunhofer.aisec.cpg.codyze.compliance import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.groups.OptionGroup +import com.github.ajalt.clikt.parameters.groups.provideDelegate +import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.option +import kotlin.io.path.Path -class Command : CliktCommand() { - override fun run() { - echo("Hello from compliance") - } +/** Options common to all subcommands. */ +class ProjectOptions : OptionGroup("Project Options:") { + val directory by option("--project-dir", help = "The project directory").default(".") +} + +/** The main `compliance` command. */ +class ComplianceCommand : CliktCommand() { + override fun run() {} } +/** The `scan` command. This will scan the project for compliance violations in the future. */ class ScanCommand : CliktCommand() { + private val projectOptions by ProjectOptions() + override fun run() { - echo("Hello from scan") + TODO() } } +/** + * The `list-security-goals` command. This will list the names of all security goals in the + * specified project. + * + * This command assumes that the project contains a folder named `security-goals` that contains YAML + * files with the security goals. + */ class ListSecurityGoals : CliktCommand() { + private val projectOptions by ProjectOptions() + override fun run() { - echo("Hello from listSecurityGoals") + val goals = loadSecurityGoals(Path(projectOptions.directory).resolve("security-goals")) + goals.forEach { echo(it.name.localName) } } } -var ComplianceCommand = Command().subcommands(ScanCommand(), ListSecurityGoals()) +var Command = ComplianceCommand().subcommands(ScanCommand(), ListSecurityGoals()) diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt index a1bd13e1a0c..49cf3ccb17f 100644 --- a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt @@ -29,8 +29,8 @@ import com.charleskorn.kaml.Yaml import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.OverlayNode +import java.io.File import java.nio.file.Path -import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path import kotlin.io.path.extension import kotlin.io.path.readText @@ -96,10 +96,10 @@ class ComponentSerializer : KSerializer { } /** Load all security goals from a directory. */ -@OptIn(ExperimentalPathApi::class) -fun loadSecurityGoals(directory: String): List { +fun loadSecurityGoals(directory: Path): List { // Walk the directory and load all YAML files - return Path(directory) + return directory + .toFile() .walk() .filter { it.extension == "yaml" } .toList() @@ -107,6 +107,6 @@ fun loadSecurityGoals(directory: String): List { } /** Load a single security goal from a file. */ -fun loadSecurityGoal(file: Path): SecurityGoal { +fun loadSecurityGoal(file: File): SecurityGoal { return Yaml.default.decodeFromString(file.readText()) } diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt new file mode 100644 index 00000000000..bf235df4691 --- /dev/null +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.codyze.compliance + +import com.github.ajalt.clikt.testing.test +import kotlin.test.Test +import kotlin.test.assertEquals + +class CommandTest { + @Test + fun testListSecurityGoals() { + val command = ListSecurityGoals() + val result = command.test("--project-dir src/test/resources/") + assertEquals(result.statusCode, 0) + assertEquals("Goal1\n", result.stdout) + } +} diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt index c93454e42d5..1e765cf0d5b 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt @@ -25,12 +25,13 @@ */ package de.fraunhofer.aisec.cpg.codyze.compliance +import kotlin.io.path.Path import kotlin.test.* class SecurityGoalTest { @Test fun testLoad() { - val goals = loadSecurityGoals("src/test/resources/security-goals") + val goals = loadSecurityGoals(Path("src/test/resources/security-goals")) val goal1 = goals.firstOrNull() assertNotNull(goal1) diff --git a/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt b/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt index cd2ff8585b1..23764bfb0dd 100644 --- a/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt +++ b/codyze/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/Application.kt @@ -28,14 +28,11 @@ package de.fraunhofer.aisec.cpg.codyze import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.main import com.github.ajalt.clikt.core.subcommands -import de.fraunhofer.aisec.cpg.codyze.compliance.ComplianceCommand class Codyze : CliktCommand() { - override fun run() { - echo("Hello, World!") - } + override fun run() {} } fun main(args: Array) { - Codyze().subcommands(ComplianceCommand).main(args) + Codyze().subcommands(de.fraunhofer.aisec.cpg.codyze.compliance.Command).main(args) } From ad30ca34ede0971f475f86a16c21aba6eb483b3a Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 16:27:10 +0100 Subject: [PATCH 7/9] Added more testing --- .../cpg/codyze/compliance/CommandTest.kt | 23 ++++++++++++++++--- .../cpg/codyze/compliance/SecurityGoalTest.kt | 3 +++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt index bf235df4691..8a3b93aebe8 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt @@ -26,15 +26,32 @@ package de.fraunhofer.aisec.cpg.codyze.compliance import com.github.ajalt.clikt.testing.test -import kotlin.test.Test -import kotlin.test.assertEquals +import kotlin.test.* class CommandTest { + + @Test + fun testComplianceCommand() { + val command = ComplianceCommand() + val result = command.test() + assertEquals(result.statusCode, 0) + } + @Test - fun testListSecurityGoals() { + fun testListSecurityGoalsCommand() { val command = ListSecurityGoals() val result = command.test("--project-dir src/test/resources/") assertEquals(result.statusCode, 0) assertEquals("Goal1\n", result.stdout) } + + @Test + fun testScanCommand() { + val command = ScanCommand() + val ex = assertFails { + val result = command.test("--project-dir src/test/resources/") + assertEquals(result.statusCode, 0) + } + assertIs(ex) + } } diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt index 1e765cf0d5b..c49ce4a8ba5 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt @@ -34,9 +34,12 @@ class SecurityGoalTest { val goals = loadSecurityGoals(Path("src/test/resources/security-goals")) val goal1 = goals.firstOrNull() assertNotNull(goal1) + assertEquals("Goal1", goal1.name.localName) + assertEquals("Make it very secure", goal1.description) val objective1 = goal1.objectives.firstOrNull() assertNotNull(objective1) assertEquals("Good encryption", objective1.name.localName) + assertEquals("Encryption used is very good", objective1.description) } } From 79d8831553dae2d18242bfb63590d1b0b735ce9b Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 16:33:45 +0100 Subject: [PATCH 8/9] Added kaml to gradle version catalog --- codyze-compliance/build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/codyze-compliance/build.gradle.kts b/codyze-compliance/build.gradle.kts index 9c5489524f5..b7e3933c9ab 100644 --- a/codyze-compliance/build.gradle.kts +++ b/codyze-compliance/build.gradle.kts @@ -40,7 +40,7 @@ publishing { } dependencies { + implementation(projects.cpgCore) implementation(libs.clikt) - implementation(project(":cpg-core")) - implementation("com.charleskorn.kaml:kaml:0.67.0") + implementation(libs.kaml) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab8a80e2d99..740fa717065 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ nexus-publish = "2.0.0" sootup = "1.3.0" slf4j = "2.0.16" clikt = "5.0.2" +kaml = "0.67.0" [libraries] kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} @@ -50,6 +51,7 @@ jline = { module = "org.jline:jline", version = "3.28.0" } antlr-runtime = { module = "org.antlr:antlr4-runtime", version = "4.8-1" } # we cannot upgrade until ki-shell upgrades this! ini4j = { module = "org.ini4j:ini4j", version = "0.5.4" } clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" } +kaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kaml" } # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.11.0"} From 48864a8e7c3408c963d6b161701b75fbd8e87df3 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 16 Jan 2025 20:33:25 +0100 Subject: [PATCH 9/9] Added more testing --- .../cpg/codyze/compliance/SecurityGoal.kt | 74 +++++++++++++------ .../cpg/codyze/compliance/CommandTest.kt | 6 +- .../cpg/codyze/compliance/SecurityGoalTest.kt | 54 +++++++++++++- .../test/resources/security-goals/goal1.yaml | 2 + 4 files changed, 111 insertions(+), 25 deletions(-) diff --git a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt index 49cf3ccb17f..2cc408483d8 100644 --- a/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt +++ b/codyze-compliance/src/main/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoal.kt @@ -26,15 +26,15 @@ package de.fraunhofer.aisec.cpg.codyze.compliance import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.decodeFromStream +import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.OverlayNode import java.io.File +import java.io.InputStream import java.nio.file.Path -import kotlin.io.path.Path -import kotlin.io.path.extension -import kotlin.io.path.readText -import kotlin.io.path.walk +import kotlinx.serialization.Contextual import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString @@ -43,12 +43,13 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.modules.SerializersModule @Serializable data class SecurityGoal( @Serializable(with = NameSerializer::class) override var name: Name, val description: String, - val components: List<@Serializable(with = ComponentSerializer::class) Component?> = listOf(), + val components: List<@Contextual Component?> = listOf(), val assumptions: List = listOf(), val restrictions: List = listOf(), val objectives: List, @@ -59,7 +60,7 @@ class SecurityObjective( @Serializable(with = NameSerializer::class) override var name: Name, val description: String, val statements: List, - val components: List<@Serializable(with = ComponentSerializer::class) Component?> = listOf(), + val components: List<@Contextual Component?> = listOf(), val assumptions: List = listOf(), val restrictions: List = listOf(), ) : OverlayNode() @@ -78,35 +79,66 @@ class NameSerializer : KSerializer { } } -/** A custom serializer for the [Component] class. */ -class ComponentSerializer : KSerializer { +/** + * A custom serializer for the [Component] class. If the [result] is non-null, it is used to resolve + * the component name in an actual [Component] of the [result]. Otherwise, a new [Component] with + * the given name is returned. + */ +class ComponentSerializer(val result: TranslationResult?) : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(Component::class.qualifiedName!!, PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, value: Component?) { - if (value != null) { - encoder.encodeString(value.name.toString()) - } + override fun serialize(encoder: Encoder, value: Component) { + encoder.encodeString(value.name.toString()) } - override fun deserialize(decoder: Decoder): Component? { - // TODO: find component by name somehow - return null + override fun deserialize(decoder: Decoder): Component { + // Use the context to find the component by name + val componentName = decoder.decodeString() + + return if (result != null) { + result.components.first { it.name.localName == componentName } + } else { + Component().also { it.name = Name(componentName) } + } } } -/** Load all security goals from a directory. */ -fun loadSecurityGoals(directory: Path): List { +/** + * Load all security goals from a directory. If a [result] is given, it will be used to resolve + * component names. + */ +fun loadSecurityGoals(directory: Path, result: TranslationResult? = null): List { // Walk the directory and load all YAML files return directory .toFile() .walk() .filter { it.extension == "yaml" } .toList() - .map { loadSecurityGoal(it) } + .map { loadSecurityGoal(it, result) } +} + +/** + * Load a single security goal from a file. If a [result] is given, it will be used to resolve + * component names. + */ +fun loadSecurityGoal(file: File, result: TranslationResult? = null): SecurityGoal { + return yaml(result).decodeFromString(file.readText()) +} + +/** + * Load a single security goal from an input stream. If a [result] is given, it will be used to + * resolve component names. + */ +fun loadSecurityGoal(stream: InputStream, result: TranslationResult? = null): SecurityGoal { + return yaml(result).decodeFromStream(stream) } -/** Load a single security goal from a file. */ -fun loadSecurityGoal(file: File): SecurityGoal { - return Yaml.default.decodeFromString(file.readText()) +/** + * This function returns a [Yaml] instance that is configured to use the given [result] to resolve + * components. + */ +private fun yaml(result: TranslationResult?): Yaml { + val module = SerializersModule { contextual(Component::class, ComponentSerializer(result)) } + return Yaml(serializersModule = module) } diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt index 8a3b93aebe8..f953c62f3b9 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/CommandTest.kt @@ -34,14 +34,14 @@ class CommandTest { fun testComplianceCommand() { val command = ComplianceCommand() val result = command.test() - assertEquals(result.statusCode, 0) + assertEquals(0, result.statusCode) } @Test fun testListSecurityGoalsCommand() { val command = ListSecurityGoals() val result = command.test("--project-dir src/test/resources/") - assertEquals(result.statusCode, 0) + assertEquals(0, result.statusCode) assertEquals("Goal1\n", result.stdout) } @@ -50,7 +50,7 @@ class CommandTest { val command = ScanCommand() val ex = assertFails { val result = command.test("--project-dir src/test/resources/") - assertEquals(result.statusCode, 0) + assertEquals(0, result.statusCode) } assertIs(ex) } diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt index c49ce4a8ba5..cf6483c854c 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/cpg/codyze/compliance/SecurityGoalTest.kt @@ -25,12 +25,20 @@ */ package de.fraunhofer.aisec.cpg.codyze.compliance +import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.TranslationManager +import de.fraunhofer.aisec.cpg.TranslationResult +import de.fraunhofer.aisec.cpg.TypeManager +import de.fraunhofer.aisec.cpg.graph.Component +import de.fraunhofer.aisec.cpg.graph.Name import kotlin.io.path.Path import kotlin.test.* class SecurityGoalTest { @Test - fun testLoad() { + fun testLoadSecurityGoals() { val goals = loadSecurityGoals(Path("src/test/resources/security-goals")) val goal1 = goals.firstOrNull() assertNotNull(goal1) @@ -42,4 +50,48 @@ class SecurityGoalTest { assertEquals("Good encryption", objective1.name.localName) assertEquals("Encryption used is very good", objective1.description) } + + @Test + fun testLoadSecurityGoal() { + val stream = this::class.java.getResourceAsStream("/security-goals/goal1.yaml") + assertNotNull(stream) + + val goal1 = loadSecurityGoal(stream) + assertNotNull(goal1) + assertEquals("Goal1", goal1.name.localName) + assertEquals("Make it very secure", goal1.description) + + val objective1 = goal1.objectives.firstOrNull() + assertNotNull(objective1) + assertEquals("Good encryption", objective1.name.localName) + assertEquals("Encryption used is very good", objective1.description) + } + + @Test + fun testLoadSecurityGoalWithResult() { + val stream = this::class.java.getResourceAsStream("/security-goals/goal1.yaml") + assertNotNull(stream) + + val result = + TranslationResult( + translationManager = TranslationManager.builder().build(), + TranslationContext( + config = TranslationConfiguration.builder().build(), + scopeManager = ScopeManager(), + typeManager = TypeManager(), + ), + ) + val auth = Component().also { it.name = Name("auth") } + result.components += auth + val webserver = Component().also { it.name = Name("webserver") } + result.components += webserver + + val goal1 = loadSecurityGoal(stream, result) + assertNotNull(goal1) + assertEquals(listOf(auth, webserver), goal1.components) + + val objective1 = goal1.objectives.firstOrNull() + assertNotNull(objective1) + assertEquals(listOf(auth), objective1.components) + } } diff --git a/codyze-compliance/src/test/resources/security-goals/goal1.yaml b/codyze-compliance/src/test/resources/security-goals/goal1.yaml index de0f02ad068..767f8973dd9 100644 --- a/codyze-compliance/src/test/resources/security-goals/goal1.yaml +++ b/codyze-compliance/src/test/resources/security-goals/goal1.yaml @@ -10,3 +10,5 @@ objectives: description: Encryption used is very good statements: - For each algorithm A, if A is used, then A must be a very good cryptographic algorithm + components: + - auth