Skip to content

Commit 9a99958

Browse files
lamppusschuberth
authored andcommitted
feat(vulnerable-code): Populate firstFixedVersions for v3 API
Collect first fixed versions for the vulnerability from the `fixed_by_packages` array from the `v3/packages` response. Signed-off-by: Johanna Lamppu <johanna.lamppu@doubleopen.io>
1 parent 829f364 commit 9a99958

4 files changed

Lines changed: 37 additions & 13 deletions

File tree

plugins/advisors/vulnerable-code/src/funTest/kotlin/VulnerableCodeFunTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package org.ossreviewtoolkit.plugins.advisors.vulnerablecode
2222
import io.kotest.core.spec.style.WordSpec
2323
import io.kotest.matchers.collections.beEmpty
2424
import io.kotest.matchers.collections.containAll
25+
import io.kotest.matchers.collections.containExactly
2526
import io.kotest.matchers.nulls.shouldNotBeNull
2627
import io.kotest.matchers.should
2728
import io.kotest.matchers.shouldBe
@@ -199,6 +200,8 @@ class VulnerableCodeFunTest : WordSpec({
199200
score shouldBe 5.5f
200201
vector shouldBe "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"
201202
}
203+
204+
vulnerability.firstFixedVersions should containExactly("32.0.0-android")
202205
}
203206
}
204207

@@ -225,6 +228,8 @@ class VulnerableCodeFunTest : WordSpec({
225228
score shouldBe 5.5f
226229
vector shouldBe "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"
227230
}
231+
232+
vulnerability.firstFixedVersions should containExactly("1.24.0")
228233
}
229234
}
230235

@@ -251,6 +256,8 @@ class VulnerableCodeFunTest : WordSpec({
251256
score shouldBe 4.8f
252257
vector shouldBe "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L"
253258
}
259+
260+
vulnerability.firstFixedVersions should containExactly("6.6.0")
254261
}
255262
}
256263
}

plugins/advisors/vulnerable-code/src/main/kotlin/VulnerableCodeApiV3.kt

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import org.ossreviewtoolkit.model.Issue
5757
import org.ossreviewtoolkit.model.Package
5858
import org.ossreviewtoolkit.model.Severity
5959
import org.ossreviewtoolkit.model.createAndLogIssue
60+
import org.ossreviewtoolkit.model.utils.toPackageUrl
6061
import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability
6162
import org.ossreviewtoolkit.model.vulnerabilities.VulnerabilityReference
6263
import org.ossreviewtoolkit.plugins.advisors.api.AdviceProvider
@@ -113,7 +114,7 @@ internal class VulnerableCodeApiV3(
113114
val purls = packages.mapNotNull { pkg -> pkg.purl.ifEmpty { null } }
114115
val chunks = purls.chunked(BULK_REQUEST_SIZE)
115116

116-
val allVulnerabilities = mutableMapOf<String, List<AdvisoryV3>>()
117+
val allVulnerabilities = mutableMapOf<String, List<Vulnerability>>()
117118
val issues = mutableListOf<Issue>()
118119

119120
chunks.forEachIndexed { index, chunk ->
@@ -135,7 +136,7 @@ internal class VulnerableCodeApiV3(
135136

136137
return packages.mapNotNullTo(mutableListOf()) { pkg ->
137138
allVulnerabilities[pkg.purl]?.let { packageVulnerabilities ->
138-
val vulnerabilities = packageVulnerabilities.map { it.toModel(issues) }.mergeVulnerabilities()
139+
val vulnerabilities = packageVulnerabilities.mergeVulnerabilities()
139140
val summary = AdvisorSummary(startTime, endTime, issues)
140141
pkg to AdvisorResult(details, summary, vulnerabilities = vulnerabilities)
141142
}
@@ -150,7 +151,7 @@ internal class VulnerableCodeApiV3(
150151
private suspend fun HttpClient.getAdvisoriesByPackage(
151152
purls: List<String>,
152153
issues: MutableList<Issue>
153-
): Map<String, List<AdvisoryV3>> {
154+
): Map<String, List<Vulnerability>> {
154155
val advisoryUidsByPurl = getAllPackageDetails(purls).associate { packageDetails ->
155156
if (packageDetails.affected_by_vulnerabilities.size >= DEFAULT_MAX_ADVISORIES) {
156157
issues += createAndLogIssue(
@@ -161,15 +162,17 @@ internal class VulnerableCodeApiV3(
161162
)
162163
}
163164

164-
packageDetails.purl to packageDetails.affected_by_vulnerabilities.mapTo(mutableSetOf()) {
165-
it.advisory_uid
165+
packageDetails.purl to packageDetails.affected_by_vulnerabilities.associate {
166+
it.advisory_uid to it.fixed_by_packages.mapNotNullTo(mutableSetOf()) { purl ->
167+
purl.toPackageUrl()?.version
168+
}
166169
}
167170
}.filterValues { advisoryUids -> advisoryUids.isNotEmpty() }
168171

169172
if (advisoryUidsByPurl.isEmpty()) return emptyMap()
170173

171174
val advisoriesByUid = getAllAdvisories(advisoryUidsByPurl.keys.toList()).associateBy { it.advisory_uid }
172-
val missingAdvisoryUids = advisoryUidsByPurl.values.flatten().toSet() - advisoriesByUid.keys
175+
val missingAdvisoryUids = advisoryUidsByPurl.values.flatMapTo(mutableSetOf()) { it.keys } - advisoriesByUid.keys
173176

174177
missingAdvisoryUids.forEach { advisoryUid ->
175178
issues += createAndLogIssue(
@@ -180,7 +183,9 @@ internal class VulnerableCodeApiV3(
180183
}
181184

182185
return advisoryUidsByPurl.mapValues { (_, advisoryUids) ->
183-
advisoryUids.mapNotNull { advisoriesByUid[it] }
186+
advisoryUids.mapNotNull { (advisoryUid, fixedVersions) ->
187+
advisoriesByUid[advisoryUid]?.toModel(issues, fixedVersions)
188+
}
184189
}
185190
}
186191

@@ -232,7 +237,7 @@ internal class VulnerableCodeApiV3(
232237
* Convert this advisory from the VulnerableCode data model to a [Vulnerability]. Populate [issues] if this
233238
* fails.
234239
*/
235-
private fun AdvisoryV3.toModel(issues: MutableList<Issue>): Vulnerability {
240+
private fun AdvisoryV3.toModel(issues: MutableList<Issue>, firstFixedVersions: Set<String>): Vulnerability {
236241
val normalizedSummary = summary?.ifBlank { null }
237242

238243
return Vulnerability(
@@ -241,7 +246,8 @@ internal class VulnerableCodeApiV3(
241246
// as description and derive a shorter summary from it.
242247
summary = normalizedSummary.deriveSummary(),
243248
description = normalizedSummary,
244-
references = toReferences(issues)
249+
references = toReferences(issues),
250+
firstFixedVersions = firstFixedVersions
245251
)
246252
}
247253

@@ -323,10 +329,11 @@ internal class VulnerableCodeApiV3(
323329
private fun Collection<Vulnerability>.mergeVulnerabilities(): List<Vulnerability> =
324330
groupBy { it.id }.values.map { vulnerabilitiesWithSameId ->
325331
val references = vulnerabilitiesWithSameId.flatMapTo(mutableSetOf()) { it.references }
332+
val fixedVersions = vulnerabilitiesWithSameId.flatMapTo(mutableSetOf()) { it.firstFixedVersions }
326333
val entry = vulnerabilitiesWithSameId.find { it.summary != null || it.description != null }
327334
?: vulnerabilitiesWithSameId.first()
328335

329-
entry.copy(references = references.toList())
336+
entry.copy(references = references.toList(), firstFixedVersions = fixedVersions)
330337
}
331338

332339
/**

plugins/advisors/vulnerable-code/src/test/kotlin/VulnerableCodeApiV3Test.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ class VulnerableCodeApiV3Test : WordSpec({
117117
score = null,
118118
vector = null
119119
)
120-
)
120+
),
121+
firstFixedVersions = setOf("3.7", "4.1", "5.3")
121122
)
122123
langResult.vulnerabilities.normalizeVulnerabilityData() should containExactly(expLangVulnerability)
123124

plugins/advisors/vulnerable-code/src/test/resources/__files/packages_response_packages.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,21 @@
99
{
1010
"advisory_id": "GHSA-2cxf-6567-7pp6",
1111
"advisory_uid": "github_osv_importer_v2/GHSA-2cxf-6567-7pp6",
12-
"resource_url": "https://example.org/advisories/github_osv_importer_v2/GHSA-2cxf-6567-7pp6"
12+
"resource_url": "https://example.org/advisories/github_osv_importer_v2/GHSA-2cxf-6567-7pp6",
13+
"fixed_by_packages": [
14+
"pkg:maven/org.apache.commons/commons-lang3@3.7",
15+
"pkg:maven/org.apache.commons/commons-lang3@4.1"
16+
]
1317
},
1418
{
1519
"advisory_id": "CVE-2014-8242",
1620
"advisory_uid": "gitlab_importer_v2/maven/org.apache.commons/commons-lang3/CVE-2014-8242",
17-
"resource_url": "https://example.org/advisories/gitlab_importer_v2/maven/org.apache.commons/commons-lang3/CVE-2014-8242"
21+
"resource_url": "https://example.org/advisories/gitlab_importer_v2/maven/org.apache.commons/commons-lang3/CVE-2014-8242",
22+
"fixed_by_packages": [
23+
"pkg:maven/org.apache.commons/commons-lang3@3.7",
24+
"pkg:maven/org.apache.commons/commons-lang3@4.1",
25+
"pkg:maven/org.apache.commons/commons-lang3@5.3"
26+
]
1827
}
1928
],
2029
"fixing_vulnerabilities": []

0 commit comments

Comments
 (0)