Skip to content

Commit

Permalink
Add Plugin Support (#794)
Browse files Browse the repository at this point in the history
Co-authored-by: Florian Wendland <[email protected]>
  • Loading branch information
CodingDepot and fwendland authored Apr 15, 2024
1 parent ec8c63b commit 386a800
Show file tree
Hide file tree
Showing 43 changed files with 2,677 additions and 30 deletions.
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,13 @@ subprojects {
}
}
}

/*
* Optional and experimental features
*/
// this code block also exists in `settings.gradle.kts`
val enablePluginSupport: Boolean by extra {
val enablePluginSupport: String? by project
enablePluginSupport.toBoolean()
}
project.logger.lifecycle("Plugin feature is ${if (enablePluginSupport) "enabled" else "disabled"}")
9 changes: 9 additions & 0 deletions buildSrc/src/main/kotlin/features.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
kotlin("jvm")
}

val enablePluginSupport: Boolean by rootProject.extra

dependencies {
if (enablePluginSupport) runtimeOnly(project(":codyze-plugins"))
}
6 changes: 6 additions & 0 deletions code-coverage-report/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ reporting {
}
}

val enablePluginSupport: Boolean by rootProject.extra
project.logger.lifecycle("Plugin feature is ${if (enablePluginSupport) "enabled" else "disabled"}")

dependencies {
jacocoAggregation(projects.codyzeBackends.cpg)
jacocoAggregation(projects.codyzeCli)
jacocoAggregation(projects.codyzeCore)
jacocoAggregation(projects.codyzeSpecificationLanguages.coko.cokoCore)
jacocoAggregation(projects.codyzeSpecificationLanguages.coko.cokoDsl)

// Optional and experimental features
if (enablePluginSupport) jacocoAggregation(project(":codyze-plugins"))
}

tasks.check {
Expand Down
3 changes: 3 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
ignore:
- "**/cli/Main.kt"
- "**/codyze/core/plugin/*.kt"
coverage:
status:
project:
Expand Down
1 change: 1 addition & 0 deletions codyze-cli/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("documented-module")
id("features")
application
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package de.fraunhofer.aisec.codyze.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.NoOpCliktCommand
import com.github.ajalt.clikt.core.context
import com.github.ajalt.clikt.core.findOrSetObject
import com.github.ajalt.clikt.output.MordantHelpFormatter
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.multiple
Expand All @@ -27,6 +28,7 @@ import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.versionOption
import com.github.ajalt.clikt.parameters.types.path
import de.fraunhofer.aisec.codyze.core.VersionProvider
import de.fraunhofer.aisec.codyze.core.executor.ExecutorCommand
import java.nio.file.Path
import kotlin.io.path.Path

Expand Down Expand Up @@ -68,7 +70,11 @@ class ConfigFileParser : CliktCommand(treatUnknownOptionsAsArgs = true) {
*/
@Suppress("Unused", "UnusedPrivateMember")
class CodyzeCli(val configFile: Path?) :
NoOpCliktCommand(help = "Codyze finds security flaws in source code", printHelpOnEmptyArgs = true) {
NoOpCliktCommand(
help = "Codyze finds security flaws in source code",
printHelpOnEmptyArgs = true,
allowMultipleSubcommands = true
) {

init {
versionOption(
Expand All @@ -86,4 +92,11 @@ class CodyzeCli(val configFile: Path?) :
private val unusedConfigFile: Path? by configFileOption()

val codyzeOptions by CodyzeOptionGroup()

val usedExecutors by findOrSetObject { mutableListOf<ExecutorCommand<*>>() }

// This run method is only necessary to correctly set the "usedExecutors" variable
override fun run() {
usedExecutors
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ 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.core.plugin.Plugin
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.cli.CokoSubcommand
import org.koin.core.module.dsl.factoryOf
import org.koin.dsl.bind
import org.koin.dsl.module
import java.util.ServiceLoader

/**
* Every [Backend] must provide [BackendCommand] to be selectable in the CLI.
Expand All @@ -49,3 +51,8 @@ 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 = ServiceLoader.load(Plugin::class.java).map { it.module() }
42 changes: 23 additions & 19 deletions codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,19 @@ package de.fraunhofer.aisec.codyze.cli
import com.github.ajalt.clikt.core.subcommands
import de.fraunhofer.aisec.codyze.core.backend.BackendCommand
import de.fraunhofer.aisec.codyze.core.executor.ExecutorCommand
import io.github.oshai.kotlinlogging.KotlinLogging
import de.fraunhofer.aisec.codyze.core.output.aggregator.Aggregate
import de.fraunhofer.aisec.codyze.core.plugin.Plugin
import org.koin.core.context.startKoin
import org.koin.java.KoinJavaComponent.getKoin
import java.nio.file.Path

private val logger = KotlinLogging.logger {}

/** Entry point for Codyze. */
fun main(args: Array<String>) {
startKoin { // Initialize the koin dependency injection
// use Koin logger
printLogger()
// declare modules
modules(executorCommands, backendCommands, outputBuilders)
modules(listOf(executorCommands, backendCommands, outputBuilders) + plugins)
}

// parse the CMD arguments
Expand All @@ -44,26 +43,31 @@ fun main(args: Array<String>) {
} finally {
// parse the arguments based on the codyze options and the executorOptions/backendOptions
codyzeCli = CodyzeCli(configFile = configFile)
codyzeCli.subcommands(getKoin().getAll<ExecutorCommand<*>>())
codyzeCli.subcommands(getKoin().getAll<ExecutorCommand<*>>() + getKoin().getAll<Plugin>())
codyzeCli.main(args)
}

// get the used subcommands
val executorCommand = codyzeCli.currentContext.invokedSubcommand as? ExecutorCommand<*>

// allow backendCommand to be null in order to allow executors that do not use backends
val backendCommand = executorCommand?.currentContext?.invokedSubcommand as? BackendCommand<*>
// Following code will be executed after all commands' "run" functions complete

// this should already be checked by clikt in [codyzeCli.main(args)]
requireNotNull(executorCommand) { "UsageError! Please select one of the available executors." }
require(codyzeCli.usedExecutors.isNotEmpty()) { "UsageError! Please select one of the available executors." }
for (executorCommand in codyzeCli.usedExecutors) {
// allow backendCommand to be null in order to allow executors that do not use backends
val backendCommand = executorCommand.currentContext.invokedSubcommand as? BackendCommand<*>

val codyzeConfiguration = codyzeCli.codyzeOptions.asConfiguration()
// the subcommands know how to instantiate their respective backend/executor
val backend = backendCommand?.getBackend() // [null] if the chosen executor does not support modular backends
val executor = executorCommand.getExecutor(codyzeConfiguration.goodFindings, codyzeConfiguration.pedantic, backend)
val codyzeConfiguration = codyzeCli.codyzeOptions.asConfiguration()

val run = executor.evaluate()
// the subcommands know how to instantiate their respective backend/executor
val backend = backendCommand?.getBackend() // [null] if the chosen executor does not support modular backends
val executor = executorCommand.getExecutor(
codyzeConfiguration.goodFindings,
codyzeConfiguration.pedantic,
backend
)

// 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)
val run = executor.evaluate()
Aggregate.addRun(run)

// use the chosen OutputBuilder to convert the SARIF format (a SARIF Run) from the executor to the chosen format
codyzeConfiguration.outputBuilder.toFile(Aggregate.createRun() ?: run, codyzeConfiguration.output)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@
package de.fraunhofer.aisec.codyze.core.config

import de.fraunhofer.aisec.codyze.core.output.OutputBuilder
import io.github.oshai.kotlinlogging.KotlinLogging
import java.nio.file.Path

private val logger = KotlinLogging.logger {}

/**
* Holds the main configuration to run Codyze with
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
*/
package de.fraunhofer.aisec.codyze.core.executor

import com.github.ajalt.clikt.core.NoOpCliktCommand
import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.core.*
import de.fraunhofer.aisec.codyze.core.backend.Backend
import de.fraunhofer.aisec.codyze.core.backend.BackendCommand
import de.fraunhofer.aisec.codyze.core.backend.BackendOptions
Expand All @@ -29,6 +28,9 @@ import org.koin.java.KoinJavaComponent.getKoin
abstract class ExecutorCommand<T : Executor>(cliName: String? = null) :
NoOpCliktCommand(hidden = true, name = cliName) {

/** Use the global context set in [CodyzeCli] */
private val usedExecutors by findOrSetObject { mutableListOf<ExecutorCommand<*>>() }

abstract fun getExecutor(goodFindings: Boolean, pedantic: Boolean, backend: Backend?): T

/**
Expand All @@ -39,4 +41,8 @@ abstract class ExecutorCommand<T : Executor>(cliName: String? = null) :
inline fun <reified T> registerBackendOptions() {
subcommands(getKoin().getAll<BackendCommand<*>>().filter { it.backend == T::class })
}

override fun run() {
usedExecutors += this
}
}
Loading

0 comments on commit 386a800

Please sign in to comment.