From 2416358a574d85a053438c1d8746478086f47f26 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Wed, 1 Nov 2023 22:04:37 +0100 Subject: [PATCH] feat(vulnerabilities): Support the CVSS 4 qualitative severity rating scale Note that the rating scale did not actually change from CVSS 3. Still add a separate class for cleaner semantics. Signed-off-by: Sebastian Schuberth --- .../kotlin/vulnerabilities/Cvss4Rating.kt | 53 +++++++++++++++++++ .../vulnerabilities/VulnerabilityReference.kt | 1 + .../kotlin/vulnerabilities/Cvss4RatingTest.kt | 47 ++++++++++++++++ .../VulnerabilityReferenceTest.kt | 8 +++ 4 files changed, 109 insertions(+) create mode 100644 model/src/main/kotlin/vulnerabilities/Cvss4Rating.kt create mode 100644 model/src/test/kotlin/vulnerabilities/Cvss4RatingTest.kt diff --git a/model/src/main/kotlin/vulnerabilities/Cvss4Rating.kt b/model/src/main/kotlin/vulnerabilities/Cvss4Rating.kt new file mode 100644 index 0000000000000..440f0af8a3bba --- /dev/null +++ b/model/src/main/kotlin/vulnerabilities/Cvss4Rating.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 The ORT Project Authors (see ) + * + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package org.ossreviewtoolkit.model.vulnerabilities + +/** + * The rating attaches human-readable semantics to the score number according to CVSS version 4, see + * https://www.first.org/cvss/v4.0/specification-document#Qualitative-Severity-Rating-Scale. + */ +enum class Cvss4Rating(private val upperBound: Float) { + NONE(0.0f), + LOW(4.0f), + MEDIUM(7.0f), + HIGH(9.0f), + CRITICAL(10.0f); + + companion object { + /** + * A set of names that refer to the CVSS version 4 scoring system. + */ + val NAMES = setOf("CVSS4", "CVSSV4", "CVSS:4.0") + + /** + * Get the [Cvss4Rating] from a [score], or null if the [score] does not map to any [Cvss4Rating]. + */ + fun fromScore(score: Float): Cvss4Rating? = + when { + score < 0.0f || score > CRITICAL.upperBound -> null + score == NONE.upperBound -> NONE + score < LOW.upperBound -> LOW + score < MEDIUM.upperBound -> MEDIUM + score < HIGH.upperBound -> HIGH + score <= CRITICAL.upperBound -> CRITICAL + else -> null + } + } +} diff --git a/model/src/main/kotlin/vulnerabilities/VulnerabilityReference.kt b/model/src/main/kotlin/vulnerabilities/VulnerabilityReference.kt index e4ab291b32809..f0be2aac66c15 100644 --- a/model/src/main/kotlin/vulnerabilities/VulnerabilityReference.kt +++ b/model/src/main/kotlin/vulnerabilities/VulnerabilityReference.kt @@ -63,6 +63,7 @@ data class VulnerabilityReference( when (scoringSystem?.uppercase()) { in Cvss2Rating.NAMES -> severity?.toFloatOrNull()?.let { Cvss2Rating.fromScore(it) }?.toString() in Cvss3Rating.NAMES -> severity?.toFloatOrNull()?.let { Cvss3Rating.fromScore(it) }?.toString() + in Cvss4Rating.NAMES -> severity?.toFloatOrNull()?.let { Cvss4Rating.fromScore(it) }?.toString() else -> severity?.uppercase()?.takeIf { it in CVSS3_SEVERITIES } } ?: "UNKNOWN" } diff --git a/model/src/test/kotlin/vulnerabilities/Cvss4RatingTest.kt b/model/src/test/kotlin/vulnerabilities/Cvss4RatingTest.kt new file mode 100644 index 0000000000000..5781fce5be7a0 --- /dev/null +++ b/model/src/test/kotlin/vulnerabilities/Cvss4RatingTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 The ORT Project Authors (see ) + * + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package org.ossreviewtoolkit.model.vulnerabilities + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.nulls.beNull +import io.kotest.matchers.should +import io.kotest.matchers.shouldBe + +class Cvss4RatingTest : StringSpec({ + "The CVSS 4 rating should be correct for a given score" { + Cvss4Rating.fromScore(-0.1f) should beNull() + + Cvss4Rating.fromScore(0.0f) shouldBe Cvss4Rating.NONE + + Cvss4Rating.fromScore(0.1f) shouldBe Cvss4Rating.LOW + Cvss4Rating.fromScore(3.9f) shouldBe Cvss4Rating.LOW + + Cvss4Rating.fromScore(4.0f) shouldBe Cvss4Rating.MEDIUM + Cvss4Rating.fromScore(6.9f) shouldBe Cvss4Rating.MEDIUM + + Cvss4Rating.fromScore(7.0f) shouldBe Cvss4Rating.HIGH + Cvss4Rating.fromScore(8.9f) shouldBe Cvss4Rating.HIGH + + Cvss4Rating.fromScore(9.0f) shouldBe Cvss4Rating.CRITICAL + Cvss4Rating.fromScore(10.0f) shouldBe Cvss4Rating.CRITICAL + + Cvss4Rating.fromScore(10.1f) should beNull() + } +}) diff --git a/model/src/test/kotlin/vulnerabilities/VulnerabilityReferenceTest.kt b/model/src/test/kotlin/vulnerabilities/VulnerabilityReferenceTest.kt index 43c3fbc035148..69c1836cab55d 100644 --- a/model/src/test/kotlin/vulnerabilities/VulnerabilityReferenceTest.kt +++ b/model/src/test/kotlin/vulnerabilities/VulnerabilityReferenceTest.kt @@ -37,6 +37,14 @@ class VulnerabilityReferenceTest : StringSpec({ VulnerabilityReference.getSeverityString("CVSS3", "9.0") shouldBe "CRITICAL" } + "The severity string should be correct for a given CVSS 4 score" { + VulnerabilityReference.getSeverityString("CVSS4", "0.0") shouldBe "NONE" + VulnerabilityReference.getSeverityString("CVSSV4", "1.0") shouldBe "LOW" + VulnerabilityReference.getSeverityString("CVSS:4.0", "5.0") shouldBe "MEDIUM" + VulnerabilityReference.getSeverityString("CVSS4", "8.0") shouldBe "HIGH" + VulnerabilityReference.getSeverityString("CVSS4", "9.0") shouldBe "CRITICAL" + } + "The severity string should be correct for a given qualitative rating from an unknown scoring system" { VulnerabilityReference.getSeverityString("", "NONE") shouldBe "NONE" VulnerabilityReference.getSeverityString("", "LOW") shouldBe "LOW"