Skip to content

Commit

Permalink
Report rule's default severity in sarif
Browse files Browse the repository at this point in the history
  • Loading branch information
matejdro committed Jan 29, 2024
1 parent fe4b3d5 commit fa64adb
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal fun toResults(detektion: Detektion): List<io.github.detekt.sarif4k.Resu
findings.map { it.toResult(ruleSetId) }
}

private fun SeverityLevel.toResultLevel() = when (this) {
internal fun SeverityLevel.toResultLevel() = when (this) {
SeverityLevel.ERROR -> Level.Error
SeverityLevel.WARNING -> Level.Warning
SeverityLevel.INFO -> Level.Note
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.github.detekt.report.sarif

import io.github.detekt.sarif4k.MultiformatMessageString
import io.github.detekt.sarif4k.ReportingConfiguration
import io.github.detekt.sarif4k.ReportingDescriptor
import io.gitlab.arturbosch.detekt.api.BaseRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.RuleSetId
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
import io.gitlab.arturbosch.detekt.api.SeverityLevel
import java.util.Locale
import java.util.ServiceLoader

Expand All @@ -16,38 +19,75 @@ internal fun toReportingDescriptors(config: Config): List<ReportingDescriptor> {
val sets =
ServiceLoader.load(RuleSetProvider::class.java, SarifOutputReport::class.java.classLoader)
.map { it.instance(config.subConfig(it.ruleSetId)) }

val ruleSetIdAndRules = sets.flatMap { ruleSet ->
val ruleSetConfig = config.subConfig(ruleSet.id)

ruleSet.rules.map { rule ->
ruleSet.id to rule
RuleInfo(ruleSet.id, rule, ruleSetConfig)
}
}

val descriptors = mutableListOf<ReportingDescriptor>()
ruleSetIdAndRules.forEach { (ruleSetId, rule) ->
ruleSetIdAndRules.forEach { (ruleSetId, rule, ruleSetConfig) ->
@Suppress("DEPRECATION")
when (rule) {
is io.gitlab.arturbosch.detekt.api.MultiRule -> descriptors.addAll(
rule.toDescriptors(
ruleSetId
is io.gitlab.arturbosch.detekt.api.MultiRule -> {
val multiRuleConfig = ruleSetConfig.subConfig(rule.ruleId)

descriptors.addAll(
rule.toDescriptors(
ruleSetId,
multiRuleConfig
)
)
)
is Rule -> descriptors.add(rule.toDescriptor(ruleSetId))
}
is Rule -> {
val ruleConfig = ruleSetConfig.subConfig(rule.ruleId)
descriptors.add(rule.toDescriptor(ruleSetId, ruleConfig))
}
}
}
return descriptors
}

@Suppress("DEPRECATION")
private fun io.gitlab.arturbosch.detekt.api.MultiRule.toDescriptors(ruleSetId: RuleSetId): List<ReportingDescriptor> =
this.rules.map { it.toDescriptor(ruleSetId) }
private fun io.gitlab.arturbosch.detekt.api.MultiRule.toDescriptors(ruleSetId: RuleSetId, multiRuleConfig: Config): List<ReportingDescriptor> =

Check warning

Code scanning / detekt

Parameter should start on a newline Warning

Parameter should start on a newline

Check warning

Code scanning / detekt

Parameter should start on a newline Warning

Parameter should start on a newline

Check warning

Code scanning / detekt

Missing newline before ")" Warning

Missing newline before ")"

Check warning

Code scanning / detekt

Line detected, which is longer than the defined maximum line length in the code style. Warning

Line detected, which is longer than the defined maximum line length in the code style.
this.rules.map {
val config = multiRuleConfig.subConfig(it.ruleId)
it.toDescriptor(ruleSetId, config)
}

private fun Rule.toDescriptor(ruleSetId: RuleSetId): ReportingDescriptor {
private fun Rule.toDescriptor(ruleSetId: RuleSetId, config: Config): ReportingDescriptor {
val formattedRuleSetId = ruleSetId.lowercase(Locale.US)
val formattedRuleId = ruleId.lowercase(Locale.US)
val severity = computeSeverity(config)

return ReportingDescriptor(
id = "detekt.$ruleSetId.$ruleId",
name = ruleId,
shortDescription = MultiformatMessageString(text = issue.description),
helpURI = "https://detekt.dev/$formattedRuleSetId.html#$formattedRuleId"
helpURI = "https://detekt.dev/$formattedRuleSetId.html#$formattedRuleId",
defaultConfiguration = ReportingConfiguration(
level = severity.toResultLevel()
)
)
}

private data class RuleInfo(
val ruleSetId: String,
val rule: BaseRule,
val parentConfig: Config
)

private fun Rule.computeSeverity(config: Config): SeverityLevel {
val configValue: String = config.valueOrNull(Config.SEVERITY_KEY)
?: return SeverityLevel.ERROR
return parseToSeverity(configValue)
}

internal fun parseToSeverity(severity: String): SeverityLevel {
val lowercase = severity.lowercase()
return SeverityLevel.entries.find { it.name.lowercase() == lowercase }
?: error("$severity is not a valid Severity. Allowed values are ${SeverityLevel.entries}")
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package io.github.detekt.report.sarif

import io.github.detekt.test.utils.readResourceContent
import io.gitlab.arturbosch.detekt.api.BaseRule

Check warning

Code scanning / detekt

Unused import Warning

Unused import
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Finding
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Location
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.RuleId

Check warning

Code scanning / detekt

Unused import Warning

Unused import
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
import io.gitlab.arturbosch.detekt.api.SetupContext
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.SeverityLevel
import io.gitlab.arturbosch.detekt.api.SourceLocation
Expand All @@ -20,9 +26,12 @@ import io.gitlab.arturbosch.detekt.test.compileAndLint
import io.gitlab.arturbosch.detekt.test.createEntity
import io.gitlab.arturbosch.detekt.test.createFindingForRelativePath
import io.gitlab.arturbosch.detekt.test.createIssue
import io.gitlab.arturbosch.detekt.test.yamlConfig
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.junit.jupiter.api.Test
import java.net.URI
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString

Expand Down Expand Up @@ -153,6 +162,38 @@ class SarifOutputReportSpec {
assertThat(report)
.containsIgnoringWhitespaces(constrainRegion(startLine, startColumn, endLine, endColumn))
}

@Test
fun `renders multiple issues with rule set to warning by default`() {
val result = TestDetektion(
createFinding(ruleName = "TestSmellA", severity = SeverityLevel.ERROR),
createFinding(ruleName = "TestSmellB", severity = SeverityLevel.WARNING),
createFinding(ruleName = "TestSmellC", severity = SeverityLevel.INFO)
)

val testConfig = yamlConfig("config_with_rule_set_to_warning.yml")

val report = SarifOutputReport()
.apply {
init(object : SetupContext {
override val configUris: Collection<URI>
get() = emptyList()
override val config: Config = testConfig
override val outputChannel: Appendable = StringBuilder()
override val errorChannel: Appendable = StringBuilder()
override val properties: MutableMap<String, Any?> = HashMap()
override fun register(key: String, value: Any) {
properties[key] = value
}
})
}
.render(result)

val expectedReport = readResourceContent("rule_warning.sarif.json")
.replace("<PREFIX>", Path(System.getProperty("user.dir")).toUri().toString())

assertThat(report).isEqualToIgnoringWhitespace(expectedReport)
}
}

private object Snippet {
Expand All @@ -176,9 +217,22 @@ private fun constrainRegion(startLine: Int, startColumn: Int, endLine: Int, endC
}
""".trimIndent()

class TestProvider : RuleSetProvider {
override val ruleSetId: String
get() = "test"

override fun instance(config: Config): RuleSet {
return RuleSet(ruleSetId, listOf(TestRule()))
}
}

class TestRule : Rule() {
override val issue = Issue(javaClass.simpleName, Severity.Warning, "", Debt.FIVE_MINS)

override fun visitCondition(root: KtFile): Boolean {
return true
}

override fun visitClassOrObject(classOrObject: KtClassOrObject) {
report(CodeSmell(issue, Entity.atName(classOrObject), message = "Error"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.github.detekt.report.sarif.TestProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test:
TestRule:
severity: 'warning'
11 changes: 11 additions & 0 deletions detekt-report-sarif/src/test/resources/relative_path.sarif.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@
"name": "detekt",
"organization": "detekt",
"rules": [
{
"defaultConfiguration": {
"level": "error"
},
"helpUri": "https://detekt.dev/test.html#testrule",
"id": "detekt.test.TestRule",
"name": "TestRule",
"shortDescription": {
"text": ""
}
}
],
"semanticVersion": "1.16.0",
"version": "1.16.0"
Expand Down
96 changes: 96 additions & 0 deletions detekt-report-sarif/src/test/resources/rule_warning.sarif.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"results": [
{
"level": "error",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "<PREFIX>TestFile.kt"
},
"region": {
"startColumn": 1,
"startLine": 1
}
}
}
],
"message": {
"text": "TestMessage"
},
"ruleId": "detekt.TestSmellA.TestSmellA"
},
{
"level": "warning",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "<PREFIX>TestFile.kt"
},
"region": {
"startColumn": 1,
"startLine": 1
}
}
}
],
"message": {
"text": "TestMessage"
},
"ruleId": "detekt.TestSmellB.TestSmellB"
},
{
"level": "note",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "<PREFIX>TestFile.kt"
},
"region": {
"startColumn": 1,
"startLine": 1
}
}
}
],
"message": {
"text": "TestMessage"
},
"ruleId": "detekt.TestSmellC.TestSmellC"
}
],
"tool": {
"driver": {
"downloadUri": "https://github.com/detekt/detekt/releases/download/v1.16.0/detekt",
"fullName": "detekt",
"guid": "022ca8c2-f6a2-4c95-b107-bb72c43263f3",
"informationUri": "https://detekt.dev",
"language": "en",
"name": "detekt",
"organization": "detekt",
"rules": [
{
"defaultConfiguration": {
"level": "warning"
},
"helpUri": "https://detekt.dev/test.html#testrule",
"id": "detekt.test.TestRule",
"name": "TestRule",
"shortDescription": {
"text": ""
}
}
],
"semanticVersion": "1.16.0",
"version": "1.16.0"
}
}
}
]
}
11 changes: 11 additions & 0 deletions detekt-report-sarif/src/test/resources/vanilla.sarif.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@
"name": "detekt",
"organization": "detekt",
"rules": [
{
"defaultConfiguration": {
"level": "error"
},
"helpUri": "https://detekt.dev/test.html#testrule",
"id": "detekt.test.TestRule",
"name": "TestRule",
"shortDescription": {
"text": ""
}
}
],
"semanticVersion": "1.16.0",
"version": "1.16.0"
Expand Down

0 comments on commit fa64adb

Please sign in to comment.