@@ -57,6 +57,7 @@ import org.ossreviewtoolkit.model.Issue
5757import org.ossreviewtoolkit.model.Package
5858import org.ossreviewtoolkit.model.Severity
5959import org.ossreviewtoolkit.model.createAndLogIssue
60+ import org.ossreviewtoolkit.model.utils.toPackageUrl
6061import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability
6162import org.ossreviewtoolkit.model.vulnerabilities.VulnerabilityReference
6263import 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 /* *
0 commit comments