Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions advisor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ plugins {

dependencies {
api(projects.model)
api(projects.plugins.api)
api(projects.utils.commonUtils)

implementation(projects.utils.ortUtils)
Expand Down
7 changes: 4 additions & 3 deletions advisor/src/main/kotlin/AdviceProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@ package org.ossreviewtoolkit.advisor
import org.ossreviewtoolkit.model.AdvisorDetails
import org.ossreviewtoolkit.model.AdvisorResult
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.plugins.api.Plugin

/**
* An abstract class that represents a service that can retrieve any kind of advice information
* for a list of given [Package]s. Examples of such information can be security vulnerabilities, known defects,
* or code analysis results.
*/
abstract class AdviceProvider(val providerName: String) {
interface AdviceProvider : Plugin {
/**
* For a given set of [Package]s, retrieve findings and return a map that associates packages with [AdvisorResult]s.
*/
abstract suspend fun retrievePackageFindings(packages: Set<Package>): Map<Package, AdvisorResult>
suspend fun retrievePackageFindings(packages: Set<Package>): Map<Package, AdvisorResult>

/**
* An object with detail information about this [AdviceProvider].
*/
abstract val details: AdvisorDetails
val details: AdvisorDetails
}
14 changes: 3 additions & 11 deletions advisor/src/main/kotlin/AdviceProviderFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,16 @@ package org.ossreviewtoolkit.advisor

import java.util.ServiceLoader

import org.ossreviewtoolkit.utils.common.Plugin
import org.ossreviewtoolkit.utils.common.TypedConfigurablePluginFactory
import org.ossreviewtoolkit.plugins.api.PluginFactory

/**
* A common abstract class for use with [ServiceLoader] that all [AdviceProviderFactory] classes need to implement.
*/
abstract class AdviceProviderFactory<CONFIG>(override val type: String) :
TypedConfigurablePluginFactory<CONFIG, AdviceProvider> {
interface AdviceProviderFactory : PluginFactory<AdviceProvider> {
companion object {
/**
* All [advice provider factories][AdviceProviderFactory] available in the classpath, associated by their names.
*/
val ALL by lazy { Plugin.getAll<AdviceProviderFactory<*>>() }
val ALL by lazy { PluginFactory.getAll<AdviceProviderFactory, AdviceProvider>() }
}

/**
* Return the provider's type here to allow Clikt to display something meaningful when listing the advisors which
* are enabled by default via their factories.
*/
override fun toString() = type
}
9 changes: 5 additions & 4 deletions advisor/src/main/kotlin/Advisor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.OrtResult
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
import org.ossreviewtoolkit.plugins.api.PluginConfig
import org.ossreviewtoolkit.utils.ort.Environment

/**
* The class to manage [AdviceProvider]s. It invokes the configured providers and adds their findings to the current
* [OrtResult].
*/
class Advisor(
private val providerFactories: List<AdviceProviderFactory<*>>,
private val providerFactories: List<AdviceProviderFactory>,
private val config: AdvisorConfiguration
) {
/**
Expand Down Expand Up @@ -75,8 +76,8 @@ class Advisor(
logger.info { "There are no packages to give advice for." }
} else {
val providers = providerFactories.map {
val providerConfig = config.config?.get(it.type)
it.create(providerConfig?.options.orEmpty(), providerConfig?.secrets.orEmpty())
val providerConfig = config.config?.get(it.descriptor.className)
it.create(PluginConfig(providerConfig?.options.orEmpty(), providerConfig?.secrets.orEmpty()))
}

providers.map { provider ->
Expand All @@ -85,7 +86,7 @@ class Advisor(

logger.info {
"Found ${providerResults.values.flatMap { it.vulnerabilities }.distinct().size} distinct " +
"vulnerabilities via ${provider.providerName}. "
"vulnerabilities via ${provider.descriptor.name}. "
}

providerResults.keys.takeIf { it.isNotEmpty() }?.let { pkgs ->
Expand Down
8 changes: 5 additions & 3 deletions advisor/src/test/kotlin/AdvisorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import org.ossreviewtoolkit.model.OrtResult
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.Project
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
import org.ossreviewtoolkit.plugins.api.PluginConfig
import org.ossreviewtoolkit.plugins.api.PluginDescriptor

class AdvisorTest : WordSpec({
"retrieveFindings" should {
Expand Down Expand Up @@ -117,8 +119,8 @@ private fun createAdvisor(providers: List<AdviceProvider>): Advisor {
val advisorConfig = AdvisorConfiguration()

val factories = providers.map { provider ->
val factory = mockk<AdviceProviderFactory<*>>()
every { factory.create(emptyMap(), emptyMap()) } returns provider
val factory = mockk<AdviceProviderFactory>()
every { factory.create(PluginConfig(emptyMap(), emptyMap())) } returns provider
factory
}

Expand Down Expand Up @@ -146,7 +148,7 @@ private fun createPackage(index: Int): Package =

private fun mockkAdviceProvider(): AdviceProvider =
mockk<AdviceProvider>().apply {
every { providerName } returns "provider"
every { descriptor } returns PluginDescriptor("provider", "Provider", "", emptyList())
}

private fun mockkAdvisorResult(): AdvisorResult =
Expand Down
1 change: 1 addition & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ dependencies {
implementation(libs.plugin.dokkatoo)
implementation(libs.plugin.graalVmNativeImage)
implementation(libs.plugin.kotlin)
implementation(libs.plugin.ksp)
implementation(libs.plugin.mavenPublish)
}
31 changes: 31 additions & 0 deletions buildSrc/src/main/kotlin/ort-plugin-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* 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
*/

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-publication-conventions")

// Apply third-party plugins.
id("com.google.devtools.ksp")
}

dependencies {
ksp(project(":plugins:compiler"))
}
26 changes: 7 additions & 19 deletions clients/osv/src/main/kotlin/OsvService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,26 @@
companion object {
const val BATCH_REQUEST_MAX_SIZE = 1000

val JSON = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
/** The URL of the production server. */
const val PRODUCTION_SERVER_URL = "https://api.osv.dev"

/** The URL of the staging server. */
const val STAGING_SERVER_URL = "https://api-staging.osv.dev"

Check warning on line 54 in clients/osv/src/main/kotlin/OsvService.kt

View workflow job for this annotation

GitHub Actions / qodana-scan

Unused symbol

Property "STAGING_SERVER_URL" is never used

/**
* Create a service instance for communicating with the given [server], optionally using a pre-built OkHttp
* [client].
*/
fun create(server: Server, client: OkHttpClient? = null): OsvService = create(server.url, client)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, does this removal make the following implementations easier? Because if not, I'd prefer to keep it, and later on refactor it differently, to have functions with the following signatures (and do the same for ClearlyDefinedService):

fun create(server: Server = Server.PRODUCTION, client: OkHttpClient? = null): OsvService
fun create(url: String, client: OkHttpClient? = null): OsvService

Because to me it makes more sense to use the URL constructor only in cases where you want a non-default URL.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not used anywhere and I don't think the OSV advisor which is the only consumer of the library would ever use it. However, the important part is happening in the next commit and moving the URL value to a constant could happen without removing the enum. It just appeared to me that removing unused code along the way would be a good idea.

val JSON = Json { namingStrategy = JsonNamingStrategy.SnakeCase }

fun create(serverUrl: String? = null, client: OkHttpClient? = null): OsvService {
val converterFactory = JSON.asConverterFactory(contentType = "application/json".toMediaType())

return Retrofit.Builder()
.apply { client(client ?: defaultHttpClient()) }
.baseUrl(serverUrl ?: Server.PRODUCTION.url)
.baseUrl(serverUrl ?: PRODUCTION_SERVER_URL)
.addConverterFactory(converterFactory)
.build()
.create(OsvService::class.java)
}
}

enum class Server(val url: String) {
/**
* The production API server.
*/
PRODUCTION("https://api.osv.dev"),

/**
* The staging API server.
*/
STAGING("https://api-staging.osv.dev")
}

/**
* Get the vulnerabilities for the package matched by the given [request].
*/
Expand Down
6 changes: 6 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ gitSemverPlugin = "0.12.10"
graalVmNativeImagePlugin = "0.10.2"
ideaExtPlugin = "1.1.8"
kotlinPlugin = "2.0.20"
ksp = "2.0.20-1.0.24"
mavenPublishPlugin = "0.29.0"
versionsPlugin = "0.51.0"

Expand All @@ -33,6 +34,7 @@ jslt = "0.1.14"
jsonSchemaValidator = "1.5.1"
kaml = "0.61.0"
kotest = "5.9.1"
kotlinPoet = "1.18.1"
kotlinxCoroutines = "1.8.1"
kotlinxHtml = "0.11.0"
kotlinxSerialization = "1.7.1"
Expand Down Expand Up @@ -78,6 +80,7 @@ plugin-detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-format
plugin-dokkatoo = { module = "dev.adamko.dokkatoo:dokkatoo-plugin", version.ref = "dokkatooPlugin" }
plugin-graalVmNativeImage = { module = "org.graalvm.buildtools:native-gradle-plugin", version.ref = "graalVmNativeImagePlugin" }
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinPlugin" }
plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
plugin-mavenPublish = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "mavenPublishPlugin" }

asciidoctorj = { module = "org.asciidoctor:asciidoctorj", version.ref = "asciidoctorj" }
Expand Down Expand Up @@ -124,6 +127,8 @@ kotest-framework-api = { module = "io.kotest:kotest-framework-api", version.ref
kotest-framework-datatest = { module = "io.kotest:kotest-framework-datatest", version.ref = "kotest" }
kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" }
kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoet"}
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet"}
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
kotlinx-html = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinxHtml" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerialization" }
Expand All @@ -133,6 +138,7 @@ kotlinx-serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization
kotlinx-serialization-yaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kaml" }
ks3-jdk = { module = "io.ks3:ks3-jdk", version.ref = "ks3" }
ks3-standard = { module = "io.ks3:ks3-standard", version.ref = "ks3" }
ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp"}
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4jApi" }
Expand Down
2 changes: 1 addition & 1 deletion integrations/completions/ort-completion.fish
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ complete -c ort -n "__fish_seen_subcommand_from advise" -l output-dir -s o -r -F
complete -c ort -n "__fish_seen_subcommand_from advise" -l output-formats -s f -r -fa "JSON YAML" -d 'The list of output formats to be used for the ORT result file(s).'
complete -c ort -n "__fish_seen_subcommand_from advise" -l label -s l -r -d 'Set a label in the ORT result, overwriting any existing label of the same name. Can be used multiple times. For example: --label distribution=external'
complete -c ort -n "__fish_seen_subcommand_from advise" -l resolutions-file -r -F -d 'A file containing issue and rule violation resolutions.'
complete -c ort -n "__fish_seen_subcommand_from advise" -l advisors -s a -r -d 'The comma-separated advisors to use, any of [NexusIQ, OssIndex, OSV, VulnerableCode].'
complete -c ort -n "__fish_seen_subcommand_from advise" -l advisors -s a -r -d 'The comma-separated advisors to use, any of [NexusIq, OssIndex, Osv, VulnerableCode].'
complete -c ort -n "__fish_seen_subcommand_from advise" -l skip-excluded -d 'Do not check excluded projects or packages.'
complete -c ort -n "__fish_seen_subcommand_from advise" -s h -l help -d 'Show this message and exit'

Expand Down
2 changes: 1 addition & 1 deletion plugins/advisors/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@

plugins {
// Apply precompiled plugins.
id("ort-plugins-conventions")
id("ort-plugin-parent-conventions")
}
4 changes: 3 additions & 1 deletion plugins/advisors/nexus-iq/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-plugin-conventions")
}

dependencies {
Expand All @@ -29,4 +29,6 @@ dependencies {
implementation(projects.clients.nexusIqClient)
implementation(projects.utils.commonUtils)
implementation(projects.utils.ortUtils)

ksp(projects.advisor)
}
29 changes: 10 additions & 19 deletions plugins/advisors/nexus-iq/src/main/kotlin/NexusIq.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import org.ossreviewtoolkit.model.utils.PurlType
import org.ossreviewtoolkit.model.utils.getPurlType
import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability
import org.ossreviewtoolkit.model.vulnerabilities.VulnerabilityReference
import org.ossreviewtoolkit.utils.common.Options
import org.ossreviewtoolkit.plugins.api.OrtPlugin
import org.ossreviewtoolkit.plugins.api.PluginDescriptor
import org.ossreviewtoolkit.utils.common.collectMessages
import org.ossreviewtoolkit.utils.common.enumSetOf
import org.ossreviewtoolkit.utils.ort.OkHttpClientHelper
Expand Down Expand Up @@ -77,23 +78,13 @@ private val READ_TIMEOUT = Duration.ofSeconds(60)
*
* If not both `username` and `password` are provided, authentication is disabled.
*/
class NexusIq(name: String, private val config: NexusIqConfiguration) : AdviceProvider(name) {
class Factory : AdviceProviderFactory<NexusIqConfiguration>("NexusIQ") {
override fun create(config: NexusIqConfiguration) = NexusIq(type, config)

override fun parseConfig(options: Options, secrets: Options): NexusIqConfiguration {
val serverUrl = options.getValue("serverUrl")

return NexusIqConfiguration(
serverUrl = serverUrl,
browseUrl = options["browseUrl"] ?: serverUrl,
username = secrets["username"],
password = secrets["password"]
)
}
}

override val details: AdvisorDetails = AdvisorDetails(providerName, enumSetOf(AdvisorCapability.VULNERABILITIES))
@OrtPlugin(
name = "Nexus IQ",
description = "An advisor that uses Sonatype's Nexus IQ Server to determine vulnerabilities in dependencies.",
factory = AdviceProviderFactory::class
)
class NexusIq(override val descriptor: PluginDescriptor, private val config: NexusIqConfiguration) : AdviceProvider {
override val details = AdvisorDetails(descriptor.className, enumSetOf(AdvisorCapability.VULNERABILITIES))

private val service by lazy {
NexusIqService.create(
Expand Down Expand Up @@ -141,7 +132,7 @@ class NexusIq(name: String, private val config: NexusIqConfiguration) : AdvicePr
component.packageUrl to ComponentDetails(component, SecurityData(emptyList()))
}

issues += Issue(source = providerName, message = it.collectMessages())
issues += Issue(source = descriptor.name, message = it.collectMessages())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ data class NexusIqConfiguration(
val serverUrl: String,

/**
* A URL to use as a base for browsing vulnerability details. Defaults to the server URL.
* A URL to use as a base for browsing vulnerability details. If not set, the [serverUrl] is used.
*/
val browseUrl: String = serverUrl,
val browseUrl: String?,

/**
* The username to use for authentication. If not both [username] and [password] are provided, authentication is
* disabled.
*/
val username: String? = null,
val username: String?,

/**
* The password to use for authentication. If not both [username] and [password] are provided, authentication is
* disabled.
*/
val password: String? = null
val password: String?
)

This file was deleted.

4 changes: 3 additions & 1 deletion plugins/advisors/oss-index/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-plugin-conventions")
}

dependencies {
Expand All @@ -30,5 +30,7 @@ dependencies {
implementation(projects.utils.commonUtils)
implementation(projects.utils.ortUtils)

ksp(projects.advisor)

testImplementation(libs.wiremock)
}
Loading
Loading