toolkit.model.35.0.0.source-code.AdvisorRun.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of model Show documentation
Show all versions of model Show documentation
Part of the OSS Review Toolkit (ORT), a suite to automate software compliance checks.
/*
* Copyright (C) 2020 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
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonPropertyOrder
import java.time.Instant
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability
import org.ossreviewtoolkit.utils.ort.Environment
/**
* Type alias for a function that allows filtering of [AdvisorResult]s.
*/
typealias AdvisorResultFilter = (AdvisorResult) -> Boolean
/**
* The summary of a single run of the advisor.
*/
data class AdvisorRun(
/**
* The [Instant] the advisor was started.
*/
val startTime: Instant,
/**
* The [Instant] the advisor has finished.
*/
val endTime: Instant,
/**
* The [Environment] in which the advisor was executed.
*/
val environment: Environment,
/**
* The [AdvisorConfiguration] used for this run.
*/
val config: AdvisorConfiguration,
/**
* The [AdvisorResult]s for all [Package]s.
*/
@JsonPropertyOrder(alphabetic = true)
val results: Map>
) {
companion object {
val EMPTY = AdvisorRun(
startTime = Instant.EPOCH,
endTime = Instant.EPOCH,
environment = Environment(),
config = AdvisorConfiguration(),
results = emptyMap()
)
/**
* A filter for [AdvisorResult]s that matches only results that contain vulnerabilities.
*/
val RESULTS_WITH_VULNERABILITIES: AdvisorResultFilter = { it.vulnerabilities.isNotEmpty() }
/**
* A filter for [AdvisorResult]s that matches only results that contain defects.
*/
val RESULTS_WITH_DEFECTS: AdvisorResultFilter = { it.defects.isNotEmpty() }
/**
* Return a filter for [AdvisorResult]s that contain issues. Match only results with an issue whose severity
* is greater or equal than [minSeverity]. Often, issues are only relevant for certain types of advisors. For
* instance, when processing vulnerability information, it is not of interest if an advisor for defects had
* encountered problems. Therefore, support an optional filter for a [capability] of the advisor that produced
* a result.
*/
fun resultsWithIssues(
minSeverity: Severity = Severity.HINT,
capability: AdvisorCapability? = null
): AdvisorResultFilter =
{ result ->
(capability == null || capability in result.advisor.capabilities) && result.summary.issues.any {
it.severity >= minSeverity
}
}
}
@JsonIgnore
fun getIssues(): Map> =
buildMap> {
results.forEach { (id, results) ->
results.forEach { result ->
if (result.summary.issues.isNotEmpty()) {
getOrPut(id) { mutableSetOf() } += result.summary.issues
}
}
}
}
/**
* Return a map of all [Package]s and the associated [Vulnerabilities][Vulnerability].
*/
@JsonIgnore
fun getVulnerabilities(): Map> =
results.mapValues { (_, results) ->
results.flatMap { it.vulnerabilities }.mergeVulnerabilities()
}
/**
* Return a list with all [Vulnerability] objects that have been found for the given [package][pkgId]. Results
* from different advisors are merged if necessary.
*/
fun getVulnerabilities(pkgId: Identifier): List =
getFindings(pkgId) { it.vulnerabilities }.mergeVulnerabilities()
/**
* Return a list with all [Defect] objects that have been found for the given [package][pkgId]. If there are
* results from different advisors, a union list is constructed. No merging is done, as it is expected that the
* results from different advisors cannot be combined.
*/
fun getDefects(pkgId: Identifier): List = getFindings(pkgId) { it.defects }
/**
* Apply the given [filter] to the results stored in this record and return a map with the results that pass the
* filter. When processing advisor results, often specific criteria are relevant, e.g. whether security
* vulnerabilities were found or certain issues were detected. Using this function, it is easy to filter out only
* those results matching such criteria.
*/
fun filterResults(filter: AdvisorResultFilter): Map> =
results.mapNotNull { (id, results) ->
results.filter(filter).takeIf { it.isNotEmpty() }?.let { id to it }
}.toMap()
/**
* Helper function to obtain the findings of type [T] for the given [package][pkgId] using a [selector] function
* to extract the desired field.
*/
private fun getFindings(pkgId: Identifier, selector: (AdvisorResult) -> List): List =
results[pkgId].orEmpty().flatMap(selector)
}
/**
* Merge this collection of [Vulnerability] objects by combining vulnerabilities with the same ID and merging their
* references. Other [Vulnerability] properties are taken from the first object which has any such property set.
*/
private fun Collection.mergeVulnerabilities(): List {
val vulnerabilitiesById = groupBy { it.id }
return vulnerabilitiesById.map { it.value.mergeReferences() }
}
/**
* Merge this (non-empty) collection of [Vulnerability] objects (which are expected to have the same ID) to a single
* [Vulnerability] that contains all the references from the original vulnerabilities (with duplicates removed). Other
* [Vulnerability] properties are taken from the first object which has any such property set.
*/
private fun Collection.mergeReferences(): Vulnerability {
val references = flatMapTo(mutableSetOf()) { it.references }
val entry = find { it.summary != null || it.description != null } ?: first()
return entry.copy(references = references.toList())
}