diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/Reporter.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/Reporter.kt index 73f6eab96c..1441c5e6a0 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/Reporter.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/Reporter.kt @@ -33,6 +33,7 @@ import kotlin.io.path.createDirectories import kotlin.io.path.writeText interface Reporter { + /** * Generates a report for the given rule * @@ -41,7 +42,11 @@ interface Reporter { * @param minify if true, a minified version of the report is generated * @return the report as a string that can be written to a file */ - fun report(rules: Collection, minify: Boolean = false): String + fun report( + rules: Collection, + minify: Boolean = false, + arguments: List = ArrayList(0), + ): String /** * Maps a level to the respective format diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/SarifReporter.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/SarifReporter.kt index b15c2619bf..c494187244 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/SarifReporter.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/query/SarifReporter.kt @@ -25,7 +25,7 @@ */ package de.fraunhofer.aisec.cpg.query -import de.fraunhofer.aisec.cpg.graph.Node as CpgGraphNode +import de.fraunhofer.aisec.cpg.graph.Node import io.github.detekt.sarif4k.* import java.nio.file.Paths @@ -39,7 +39,7 @@ class SarifReporter : Reporter { * @param rules the [Rule]s to generate the report for * @param minify if true, the output json will be minified to reduce file size */ - override fun report(rules: Collection, minify: Boolean): String { + override fun report(rules: Collection, minify: Boolean, arguments: List): String { // TODO: consider validation of rule fields val sarifObj = SarifSchema210( @@ -91,9 +91,12 @@ class SarifReporter : Reporter { ) ) ), - // TODO: automationDetails, invocation - // automationDetails is definitely possible if used with the [RuleRunner] - results = createResults(rules) + // TODO: heuristic for executionSuccessful needed + invocations = + listOf( + Invocation(executionSuccessful = true, arguments = arguments) + ), + results = results(rules) ) ) ) @@ -101,8 +104,7 @@ class SarifReporter : Reporter { else SarifSerializer.toJson(sarifObj) } - private fun createResults(rules: Collection): List { - + private fun results(rules: Collection): List { val results = mutableListOf() for ((i, rule) in rules.withIndex()) { results.addAll(results(rule, i.toLong())) @@ -131,39 +133,41 @@ class SarifReporter : Reporter { id = if (rule.cweId != null) "CWE-${rule.cweId}" else null ) ), - locations = locations(threadFlowLocations), - codeFlows = codeFlows(threadFlowLocations) + locations = + findReasonableLocation(threadFlowLocations).let { + if (it != null) listOf(it) else null + }, + codeFlows = codeFlows(threadFlowLocations), ) ) } return results } - private fun codeFlows(threadFlowLocations: MutableList) = - if (threadFlowLocations.isEmpty()) null - else listOf(CodeFlow(threadFlows = listOf(ThreadFlow(locations = threadFlowLocations)))) - - private fun locations(threadFlowLocations: MutableList) = - listOf( - Location( + private fun findReasonableLocation(threadFlowLocations: List): Location? { + threadFlowLocations.getOrNull(threadFlowLocations.lastIndex)?.let { + val physicalLocation = it.location?.physicalLocation + return@findReasonableLocation Location( physicalLocation = PhysicalLocation( artifactLocation = - ArtifactLocation( - // TODO: Hacky but idk a better way - uri = - threadFlowLocations - .getOrNull(0) - ?.location - ?.physicalLocation - ?.artifactLocation - ?.uri - // TODO: no baseId rn even though the spec suggests its use bcs of - // editor extension support - ), + ArtifactLocation(uri = physicalLocation?.artifactLocation?.uri), + region = + Region( + startLine = physicalLocation?.region?.startLine, + endLine = physicalLocation?.region?.endLine, + startColumn = physicalLocation?.region?.startColumn, + endColumn = physicalLocation?.region?.endColumn + ) ) ) - ) + } + return null + } + + private fun codeFlows(threadFlowLocations: MutableList) = + if (threadFlowLocations.isEmpty()) null + else listOf(CodeFlow(threadFlows = listOf(ThreadFlow(locations = threadFlowLocations)))) private fun threadFlows(root: QueryTree<*>): MutableList { var initDepth: Long = -1 @@ -171,8 +175,8 @@ class SarifReporter : Reporter { val threadFlowLocations = mutableListOf() root.inOrder({ (node, depth): Pair, Long> -> - if (node.value is CpgGraphNode) { - nodeValueLocation = (node.value as CpgGraphNode).location + if (node.value is Node) { + nodeValueLocation = (node.value as Node).location if (nodeValueLocation != null) { threadFlowLocations.add( ThreadFlowLocation( diff --git a/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/RunPluginTest.kt b/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/RunPluginTest.kt deleted file mode 100644 index 00c48d31e9..0000000000 --- a/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/RunPluginTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022, 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.analysis - -import de.fraunhofer.aisec.cpg.console.RunPlugin -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import org.jetbrains.kotlinx.ki.shell.Command -import org.jetbrains.kotlinx.ki.shell.configuration.ReplConfigurationBase - -class RunPluginTest { - object TestConfig : ReplConfigurationBase() - - @Test - fun testExecute() { - val plugin = RunPlugin().Load(TestConfig) - - val result = plugin.execute(":run") - assertTrue(result is Command.Result.RunSnippets) - assertEquals(4, result.snippetsToRun.toList().size) - } -}