From e1f89b737b6619beca148915a0cb359ea222b54e Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Wed, 10 Jan 2024 12:17:39 +0100 Subject: [PATCH] add plugins to codyze configuration --- codyze-cli/build.gradle.kts | 1 + .../aisec/codyze/cli/CodyzeOptionGroup.kt | 23 ++++++++++++++++ .../aisec/codyze/cli/KoinModules.kt | 11 ++++++++ .../de/fraunhofer/aisec/codyze/cli/Main.kt | 12 ++++++++- codyze-core/build.gradle.kts | 2 ++ .../aisec/codyze/core/config/Configuration.kt | 2 ++ .../codyze/plugin/plugins/EmptyPlugin.kt | 15 +++++++++++ .../plugin/plugins/FindSecBugsPlugin.kt | 3 +-- .../aisec/codyze/plugin/plugins/PMDPlugin.kt | 2 +- .../aisec/codyze/plugin/plugins/Plugin.kt | 26 ++++++++++++++++--- 10 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/EmptyPlugin.kt diff --git a/codyze-cli/build.gradle.kts b/codyze-cli/build.gradle.kts index fd75af525..c2b51fa4e 100644 --- a/codyze-cli/build.gradle.kts +++ b/codyze-cli/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { implementation(projects.codyzeCore) implementation(projects.codyzeBackends.cpg) implementation(projects.codyzeSpecificationLanguages.coko.cokoDsl) + implementation(projects.codyzePlugins) implementation(libs.clikt) implementation(libs.koin) diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt index f07f3c382..87ec9d168 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt @@ -19,15 +19,23 @@ import com.github.ajalt.clikt.parameters.groups.OptionGroup import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.convert +import com.github.ajalt.clikt.parameters.options.multiple +import com.github.ajalt.clikt.parameters.options.unique import com.github.ajalt.clikt.parameters.types.choice import com.github.ajalt.clikt.parameters.types.path import de.fraunhofer.aisec.codyze.core.config.Configuration import de.fraunhofer.aisec.codyze.core.output.OutputBuilder import de.fraunhofer.aisec.codyze.core.output.SarifBuilder +import de.fraunhofer.aisec.codyze.plugin.plugins.EmptyPlugin +import de.fraunhofer.aisec.codyze.plugin.plugins.Plugin +import io.github.oshai.kotlinlogging.KotlinLogging import org.koin.java.KoinJavaComponent.getKoin import java.nio.file.Path import kotlin.io.path.Path +private val logger = KotlinLogging.logger {} + @Suppress("UNUSED") class CodyzeOptionGroup : OptionGroup(name = null) { val output: Path by option("-o", "--output", help = "Write results to file. Use - for stdout.") @@ -41,6 +49,20 @@ class CodyzeOptionGroup : OptionGroup(name = null) { .choice(getKoin().getAll().associateBy { it.cliName }, ignoreCase = true) .default(SarifBuilder(), defaultForHelp = "sarif") + val plugins: Set by option( + "--plugin", + help = "Plugin to be used for the analysis." + ) + .convert { name -> + val pluginMap = getKoin().getAll().associateBy { it.cliName.lowercase() } + pluginMap[name.lowercase()] ?: run { + logger.warn { "Plugin \"$name\" could not be resolved." } + EmptyPlugin() + } + } + .multiple(default = listOf(), required = false) + .unique() + val goodFindings: Boolean by option( "--good-findings", help = @@ -65,6 +87,7 @@ class CodyzeOptionGroup : OptionGroup(name = null) { fun asConfiguration() = Configuration( output = output, outputBuilder = outputBuilder, + plugins = plugins, goodFindings = goodFindings, pedantic = pedantic ) diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt index c455e943e..2f7462734 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt @@ -23,6 +23,9 @@ import de.fraunhofer.aisec.codyze.core.executor.Executor import de.fraunhofer.aisec.codyze.core.executor.ExecutorCommand import de.fraunhofer.aisec.codyze.core.output.OutputBuilder import de.fraunhofer.aisec.codyze.core.output.SarifBuilder +import de.fraunhofer.aisec.codyze.plugin.plugins.FindSecBugsPlugin +import de.fraunhofer.aisec.codyze.plugin.plugins.PMDPlugin +import de.fraunhofer.aisec.codyze.plugin.plugins.Plugin import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.cli.CokoSubcommand import org.koin.core.module.dsl.factoryOf import org.koin.dsl.bind @@ -49,3 +52,11 @@ val executorCommands = module { val outputBuilders = module { factoryOf(::SarifBuilder) bind(OutputBuilder::class) } + +/** + * List all available [Plugin]s. They use external tools to extend the analysis. + */ +val plugins = module { + factoryOf(::FindSecBugsPlugin) bind(Plugin::class) + factoryOf(::PMDPlugin) bind(Plugin::class) +} \ No newline at end of file diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt index 10a4689d3..d957fd50a 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt @@ -31,7 +31,7 @@ fun main(args: Array) { // use Koin logger printLogger() // declare modules - modules(executorCommands, backendCommands, outputBuilders) + modules(executorCommands, backendCommands, outputBuilders, plugins) } // parse the CMD arguments @@ -66,4 +66,14 @@ fun main(args: Array) { // use the chosen [OutputBuilder] to convert the SARIF format (a SARIF RUN) from the executor to the chosen format codyzeConfiguration.outputBuilder.toFile(run, codyzeConfiguration.output) + + // run each plugin + for (plugin in codyzeConfiguration.plugins) { + logger.info { "Executing Plugin \"${plugin.cliName}\"" } + TODO("run all plugins - both source and compiled") + //plugin.execute() + } + + // aggregate into one SARIF + TODO("take the separate sarif files and aggregate them") } diff --git a/codyze-core/build.gradle.kts b/codyze-core/build.gradle.kts index b48b77f7b..59321fe08 100644 --- a/codyze-core/build.gradle.kts +++ b/codyze-core/build.gradle.kts @@ -12,6 +12,8 @@ dependencies { implementation(libs.koin) implementation(libs.bundles.sarif) + + implementation(projects.codyzePlugins) } publishing { diff --git a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt index 78b0cd2b0..ba5900cff 100644 --- a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt +++ b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt @@ -16,6 +16,7 @@ package de.fraunhofer.aisec.codyze.core.config import de.fraunhofer.aisec.codyze.core.output.OutputBuilder +import de.fraunhofer.aisec.codyze.plugin.plugins.Plugin import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path @@ -33,6 +34,7 @@ private val logger = KotlinLogging.logger {} data class Configuration( val output: Path, val outputBuilder: OutputBuilder, + val plugins: Set, val goodFindings: Boolean, val pedantic: Boolean, ) diff --git a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/EmptyPlugin.kt b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/EmptyPlugin.kt new file mode 100644 index 000000000..f5aa1281e --- /dev/null +++ b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/EmptyPlugin.kt @@ -0,0 +1,15 @@ +package de.fraunhofer.aisec.codyze.plugin.plugins + +import java.io.File +import java.nio.file.Path + +/** + * This class is mapped to unresolved plugin parameters in the [CodyzeOptionGroup]. + * It should not do anything and just exist so the mapping can succeed + */ +class EmptyPlugin : Plugin() { + override val cliName: String = "none" + override fun execute(target: List, output: File) { + return + } +} \ No newline at end of file diff --git a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/FindSecBugsPlugin.kt b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/FindSecBugsPlugin.kt index 9bb1e2510..afada4733 100644 --- a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/FindSecBugsPlugin.kt +++ b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/FindSecBugsPlugin.kt @@ -28,8 +28,7 @@ import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.absolute -// FIXME: copy-paste from SpotBugs-Executor with added FindSecBugs-Plugin -class FindSecBugsPlugin: de.fraunhofer.aisec.codyze.plugin.plugins.Plugin { +class FindSecBugsPlugin: de.fraunhofer.aisec.codyze.plugin.plugins.Plugin() { override val cliName = "findsecbugs" val pluginFile = File("src/main/resources/spotbugs-plugins/findsecbugs-plugin-1.12.0.jar") diff --git a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/PMDPlugin.kt b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/PMDPlugin.kt index e3187b3b2..50a106be2 100644 --- a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/PMDPlugin.kt +++ b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/PMDPlugin.kt @@ -21,7 +21,7 @@ import java.nio.file.Path import net.sourceforge.pmd.PMDConfiguration import net.sourceforge.pmd.PmdAnalysis -class PMDPlugin: Plugin { +class PMDPlugin: Plugin() { override val cliName = "pmd" override fun execute(target: List, output: File) { val config = PMDConfiguration() diff --git a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/Plugin.kt b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/Plugin.kt index 9dde332de..ffd1aabd9 100644 --- a/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/Plugin.kt +++ b/codyze-plugins/src/main/kotlin/de/fraunhofer/aisec/codyze/plugin/plugins/Plugin.kt @@ -18,14 +18,34 @@ package de.fraunhofer.aisec.codyze.plugin.plugins import java.io.File import java.nio.file.Path -interface Plugin { +/** + * Plugins perform a standalone analysis independent of the Codyze Executors. + * They usually use already developed libraries from open-source analysis tools. + * When developing a new Plugin, do not forget to add it to the respective [KoinModules], + * otherwise it will not be selectable in the configuration. + */ +abstract class Plugin { /** the name this output format has in the codyze-cli. */ - val cliName: String + abstract val cliName: String /** * Executes the respective analysis tool. * @param target The files to be analyzed * @param output The location of the results */ - fun execute(target: List, output: File = File("$cliName.sarif")) + abstract fun execute(target: List, output: File = File("$cliName.sarif")) + + /** + * Define two plugins as equal if they are of the same type and therefore have the same CLI name. + * This is necessary to filter out duplicate Plugins when parsing the cli arguments + */ + override fun equals(other: Any?): Boolean { + if (other is Plugin) + return this.cliName == other.cliName + return false + } + + override fun hashCode(): Int { + return cliName.hashCode() + } } \ No newline at end of file