All Downloads are FREE. Search and download functionalities are using the official Maven repository.

toolkit.model.37.0.0.source-code.AdvisorRun.kt Maven / Gradle / Ivy

/*
 * 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())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy