Skip to content

Commit

Permalink
Add MergeSarifTask to combine SARIF files
Browse files Browse the repository at this point in the history
Implemented a new task in MergeSarifTask.kt for merging multiple SARIF files into a single file, to give a consolidated view of static analysis results. Updated jewel.gradle.kts to register the new task and include/exclude necessary files.
  • Loading branch information
lamba92 committed Jun 26, 2023
1 parent 30d1c0f commit 7b60e00
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
97 changes: 97 additions & 0 deletions buildSrc/src/main/kotlin/MergeSarifTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import com.contrastsecurity.sarif.Run
import com.contrastsecurity.sarif.SarifSchema210
import com.fasterxml.jackson.databind.ObjectMapper
import java.io.File
import java.net.URI
import org.gradle.api.file.FileTree
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.IgnoreEmptyDirectories
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction
import com.contrastsecurity.sarif.Result

@CacheableTask
open class MergeSarifTask : SourceTask() {

@InputFiles
@SkipWhenEmpty
@IgnoreEmptyDirectories
@PathSensitive(PathSensitivity.RELATIVE)
override fun getSource(): FileTree = super.getSource()

@get:OutputFile
val mergedSarifPath: File
get() = project.rootProject.file("build/reports/static-analysis.sarif")

@TaskAction
fun merge() {
val sarifFiles = source.files

logger.lifecycle("Merging ${sarifFiles.size} SARIF file(s)...")
logger.debug(sarifFiles.joinToString("\n") { " * ${it.path}" })

val objectMapper = ObjectMapper()
val merged = SarifSchema210()
.`with$schema`(URI.create("https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"))
.withVersion(SarifSchema210.Version._2_1_0)
.withRuns(mutableListOf())

sarifFiles.map { file -> objectMapper.readValue(file.readText(), SarifSchema210::class.java) }
.flatMap { report -> report.runs }
.groupBy { run -> run.tool.driver.guid ?: run.tool.driver.name }
.values
.filter { it.isNotEmpty() }
.forEach { runs ->
val mergedResults = mutableListOf<Result>()
runs.forEach { mergedResults.addAll(it.results) }
val mergedRun = runs.first().copy(mergedResults)
merged.runs.add(mergedRun)
}

logger.lifecycle("Merged SARIF file contains ${merged.runs.size} run(s)")
logger.info("Writing merged SARIF file to $mergedSarifPath...")
objectMapper.writerWithDefaultPrettyPrinter()
.writeValue(mergedSarifPath, merged)
}

/**
* Poor man's copying of a Run.
*
* Note that this is NOT a deep copy; we're not defensively cloning field values,
* so mutating the copy will also mutate the original.
*/
private fun Run.copy(newResults: List<Result> = this.results) = Run()
.withAddresses(addresses)
.withArtifacts(artifacts)
.withAutomationDetails(automationDetails)
.withBaselineGuid(baselineGuid)
.withColumnKind(columnKind)
.withConversion(conversion)
.withDefaultEncoding(defaultEncoding)
.withDefaultSourceLanguage(defaultSourceLanguage)
.withExternalPropertyFileReferences(externalPropertyFileReferences)
.withGraphs(graphs)
.withInvocations(invocations)
.withLanguage(language)
.withLogicalLocations(logicalLocations)
.withNewlineSequences(newlineSequences)
.withOriginalUriBaseIds(originalUriBaseIds)
.withPolicies(policies)
.withProperties(properties)
.withRedactionTokens(redactionTokens)
.withResults(newResults)
.withRunAggregates(runAggregates)
.withSpecialLocations(specialLocations)
.withTaxonomies(taxonomies)
.withThreadFlowLocations(threadFlowLocations)
.withTool(tool)
.withTranslations(translations)
.withVersionControlProvenance(versionControlProvenance)
.withWebRequests(webRequests)
.withWebResponses(webResponses)
}
8 changes: 8 additions & 0 deletions buildSrc/src/main/kotlin/jewel.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ detekt {
}

tasks {
register<MergeSarifTask>("mergeSarifReports") {
dependsOn(check)
source = rootProject.fileTree("build/reports") {
include("*.sarif")
exclude("static-analysis.sarif")
}
outputs.file(rootProject.file("build/reports/static-analysis.sarif"))
}
withType<Detekt> {
reports {
sarif.required.set(true)
Expand Down

0 comments on commit 7b60e00

Please sign in to comment.