Skip to content

Commit 14286dd

Browse files
Korbinian SinghammerKorbinian Singhammer
Korbinian Singhammer
authored and
Korbinian Singhammer
committed
advisor: Redesign Advisor class
Let vulnerabilty providers, e.g., NexusIq inherit from the VulnerabilityProvider class. Move responsibility to create providers inside the Advisor class, which can then call the provider. This is a preparation to let the Advisor handle multiple providers. Signed-off-by: Korbinian Singhammer <[email protected]>
1 parent d5c4030 commit 14286dd

File tree

6 files changed

+43
-89
lines changed

6 files changed

+43
-89
lines changed

advisor/src/main/kotlin/Advisor.kt

+6-45
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,22 @@ import java.util.ServiceLoader
2626

2727
import kotlinx.coroutines.runBlocking
2828

29-
import org.ossreviewtoolkit.model.AdvisorDetails
3029
import org.ossreviewtoolkit.model.AdvisorRecord
31-
import org.ossreviewtoolkit.model.AdvisorResult
3230
import org.ossreviewtoolkit.model.AdvisorRun
33-
import org.ossreviewtoolkit.model.AdvisorSummary
3431
import org.ossreviewtoolkit.model.OrtResult
35-
import org.ossreviewtoolkit.model.Package
3632
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
37-
import org.ossreviewtoolkit.model.createAndLogIssue
3833
import org.ossreviewtoolkit.model.readValue
3934
import org.ossreviewtoolkit.utils.Environment
40-
import org.ossreviewtoolkit.utils.collectMessagesAsString
41-
import org.ossreviewtoolkit.utils.showStackTrace
4235

4336
/**
44-
* The class to retrieve security advisories.
37+
* The class to manage [VulnerabilityProvider]s that retrieve security advisories.
4538
*/
46-
abstract class Advisor(val advisorName: String, protected val config: AdvisorConfiguration) {
39+
class Advisor(private val providerFactory: VulnerabilityProviderFactory, private val config: AdvisorConfiguration) {
4740
companion object {
4841
private val LOADER = ServiceLoader.load(VulnerabilityProviderFactory::class.java)!!
4942

5043
/**
51-
* The list of all available advisors in the classpath.
44+
* The list of all available [VulnerabilityProvider]s in the classpath.
5245
*/
5346
val ALL by lazy { LOADER.iterator().asSequence().toList() }
5447
}
@@ -69,8 +62,10 @@ abstract class Advisor(val advisorName: String, protected val config: AdvisorCon
6962
"The provided ORT result file '${ortResultFile.canonicalPath}' does not contain an analyzer result."
7063
}
7164

65+
val provider = providerFactory.create(config)
66+
7267
val packages = ortResult.getPackages(skipExcluded).map { it.pkg }
73-
val results = runBlocking { retrievePackageVulnerabilities(packages) }
68+
val results = runBlocking { provider.retrievePackageVulnerabilities(packages) }
7469
.mapKeysTo(sortedMapOf()) { (pkg, _) -> pkg.id }
7570

7671
val advisorRecord = AdvisorRecord(results)
@@ -80,38 +75,4 @@ abstract class Advisor(val advisorName: String, protected val config: AdvisorCon
8075
val advisorRun = AdvisorRun(startTime, endTime, Environment(), config, advisorRecord)
8176
return ortResult.copy(advisor = advisorRun)
8277
}
83-
84-
protected abstract suspend fun retrievePackageVulnerabilities(
85-
packages: List<Package>
86-
): Map<Package, List<AdvisorResult>>
87-
88-
protected fun createFailedResults(
89-
startTime: Instant,
90-
packages: List<Package>,
91-
t: Throwable
92-
): Map<Package, List<AdvisorResult>> {
93-
val endTime = Instant.now()
94-
95-
t.showStackTrace()
96-
97-
val failedResults = listOf(
98-
AdvisorResult(
99-
vulnerabilities = emptyList(),
100-
advisor = AdvisorDetails(advisorName),
101-
summary = AdvisorSummary(
102-
startTime = startTime,
103-
endTime = endTime,
104-
issues = listOf(
105-
createAndLogIssue(
106-
source = advisorName,
107-
message = "Failed to retrieve security vulnerabilities from $advisorName: " +
108-
t.collectMessagesAsString()
109-
)
110-
)
111-
)
112-
)
113-
)
114-
115-
return packages.associateWith { failedResults }
116-
}
11778
}

advisor/src/main/kotlin/VulnerabilityProvider.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ abstract class VulnerabilityProvider(val providerName: String) {
4141
* that associates each package with a list of [AdvisorResult]s. Needs to be implemented
4242
* by child classes.
4343
*/
44-
protected abstract suspend fun retrievePackageVulnerabilities(
44+
abstract suspend fun retrievePackageVulnerabilities(
4545
packages: List<Package>
4646
): Map<Package, List<AdvisorResult>>
4747

advisor/src/main/kotlin/VulnerabilityProviderFactory.kt

+10-10
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,32 @@ import org.ossreviewtoolkit.utils.ortConfigDirectory
3131
*/
3232
interface VulnerabilityProviderFactory {
3333
/**
34-
* The name to use to refer to the advisor.
34+
* The name to use to refer to the provider.
3535
*/
36-
val advisorName: String
36+
val providerName: String
3737

3838
/**
39-
* Create an [Advisor] using the specified [config].
39+
* Create a [VulnerabilityProvider] using the specified [config].
4040
*/
41-
fun create(config: AdvisorConfiguration): Advisor
41+
fun create(config: AdvisorConfiguration): VulnerabilityProvider
4242
}
4343

4444
/**
45-
* A generic factory class for an [Advisor].
45+
* A generic factory class for an [VulnerabilityProvider].
4646
*/
47-
abstract class AbstractVulnerabilityProviderFactory<out T : Advisor>(
48-
override val advisorName: String
47+
abstract class AbstractVulnerabilityProviderFactory<out T : VulnerabilityProvider>(
48+
override val providerName: String
4949
) : VulnerabilityProviderFactory {
5050
abstract override fun create(config: AdvisorConfiguration): T
5151

5252
protected fun <T> getProviderConfiguration(config: AdvisorConfiguration, select: (AdvisorConfiguration) -> T?): T =
5353
select(config) ?: throw IllegalArgumentException(
54-
"No $advisorName advisor configuration found in ${ortConfigDirectory.resolve(ORT_CONFIG_FILENAME)}"
54+
"No configuration for $providerName found in ${ortConfigDirectory.resolve(ORT_CONFIG_FILENAME)}"
5555
)
5656

5757
/**
58-
* Return the advisor's name here to allow Clikt to display something meaningful when listing the scanners
58+
* Return the provider's name here to allow Clikt to display something meaningful when listing the scanners
5959
* which are enabled by default via their factories.
6060
*/
61-
override fun toString() = advisorName
61+
override fun toString() = providerName
6262
}

advisor/src/main/kotlin/advisors/NexusIq.kt

+6-14
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,20 @@ import java.net.URI
2424
import java.time.Instant
2525

2626
import org.ossreviewtoolkit.advisor.AbstractVulnerabilityProviderFactory
27-
import org.ossreviewtoolkit.advisor.Advisor
27+
import org.ossreviewtoolkit.advisor.VulnerabilityProvider
2828
import org.ossreviewtoolkit.clients.nexusiq.NexusIqService
2929
import org.ossreviewtoolkit.model.AdvisorDetails
3030
import org.ossreviewtoolkit.model.AdvisorResult
3131
import org.ossreviewtoolkit.model.AdvisorSummary
3232
import org.ossreviewtoolkit.model.Package
3333
import org.ossreviewtoolkit.model.Vulnerability
3434
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
35+
import org.ossreviewtoolkit.model.config.NexusIqConfiguration
3536
import org.ossreviewtoolkit.model.utils.PurlType
3637
import org.ossreviewtoolkit.model.utils.getPurlType
3738
import org.ossreviewtoolkit.model.utils.toPurl
38-
import org.ossreviewtoolkit.utils.ORT_CONFIG_FILENAME
3939
import org.ossreviewtoolkit.utils.OkHttpClientHelper
4040
import org.ossreviewtoolkit.utils.log
41-
import org.ossreviewtoolkit.utils.ortConfigDirectory
4241

4342
import retrofit2.HttpException
4443

@@ -50,19 +49,12 @@ private const val REQUEST_CHUNK_SIZE = 100
5049
/**
5150
* A wrapper for [Nexus IQ Server](https://help.sonatype.com/iqserver) security vulnerability data.
5251
*/
53-
class NexusIq(
54-
name: String,
55-
config: AdvisorConfiguration
56-
) : Advisor(name, config) {
52+
class NexusIq(name: String, private val nexusIqConfig: NexusIqConfiguration) : VulnerabilityProvider(name) {
5753
class Factory : AbstractVulnerabilityProviderFactory<NexusIq>("NexusIQ") {
58-
override fun create(config: AdvisorConfiguration) = NexusIq(advisorName, config)
54+
override fun create(config: AdvisorConfiguration) =
55+
NexusIq(providerName, getProviderConfiguration(config) { it.nexusIq })
5956
}
6057

61-
private val nexusIqConfig = config.nexusIq
62-
?: throw IllegalArgumentException(
63-
"No $advisorName advisor configuration found in ${ortConfigDirectory.resolve(ORT_CONFIG_FILENAME)}"
64-
)
65-
6658
private val service by lazy {
6759
NexusIqService.create(
6860
nexusIqConfig.serverUrl,
@@ -108,7 +100,7 @@ class NexusIq(
108100
pkg to listOf(
109101
AdvisorResult(
110102
details.securityData.securityIssues.map { it.toVulnerability() },
111-
AdvisorDetails(advisorName),
103+
AdvisorDetails(providerName),
112104
AdvisorSummary(startTime, endTime)
113105
)
114106
)

advisor/src/main/kotlin/advisors/VulnerableCode.kt

+10-11
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,29 @@ import kotlinx.coroutines.awaitAll
2828
import kotlinx.coroutines.coroutineScope
2929

3030
import org.ossreviewtoolkit.advisor.AbstractVulnerabilityProviderFactory
31-
import org.ossreviewtoolkit.advisor.Advisor
31+
import org.ossreviewtoolkit.advisor.VulnerabilityProvider
3232
import org.ossreviewtoolkit.clients.vulnerablecode.VulnerableCodeService
3333
import org.ossreviewtoolkit.model.AdvisorDetails
3434
import org.ossreviewtoolkit.model.AdvisorResult
3535
import org.ossreviewtoolkit.model.AdvisorSummary
3636
import org.ossreviewtoolkit.model.Package
3737
import org.ossreviewtoolkit.model.Vulnerability
3838
import org.ossreviewtoolkit.model.config.AdvisorConfiguration
39+
import org.ossreviewtoolkit.model.config.VulnerableCodeConfiguration
3940
import org.ossreviewtoolkit.model.utils.toPurl
40-
import org.ossreviewtoolkit.utils.ORT_CONFIG_FILENAME
4141
import org.ossreviewtoolkit.utils.OkHttpClientHelper
42-
import org.ossreviewtoolkit.utils.ortConfigDirectory
4342

4443
/**
45-
* An [Advisor] implementation that obtains security vulnerability information from a
44+
* An [VulnerabilityProvider] implementation that obtains security vulnerability information from a
4645
* [VulnerableCode][https://github.com/nexB/vulnerablecode] instance.
4746
*/
48-
class VulnerableCode(name: String, config: AdvisorConfiguration) : Advisor(name, config) {
47+
class VulnerableCode(
48+
name: String,
49+
private val vulnerableCodeConfiguration: VulnerableCodeConfiguration
50+
) : VulnerabilityProvider(name) {
4951
class Factory : AbstractVulnerabilityProviderFactory<VulnerableCode>("VulnerableCode") {
50-
override fun create(config: AdvisorConfiguration) = VulnerableCode(advisorName, config)
52+
override fun create(config: AdvisorConfiguration) =
53+
VulnerableCode(providerName, getProviderConfiguration(config) { it.vulnerableCode })
5154
}
5255

5356
companion object {
@@ -73,10 +76,6 @@ class VulnerableCode(name: String, config: AdvisorConfiguration) : Advisor(name,
7376
}
7477

7578
private val service by lazy {
76-
val vulnerableCodeConfiguration = config.vulnerableCode
77-
?: throw IllegalArgumentException(
78-
"No $advisorName advisor configuration found in ${ortConfigDirectory.resolve(ORT_CONFIG_FILENAME)}"
79-
)
8079
VulnerableCodeService.create(vulnerableCodeConfiguration.serverUrl, OkHttpClientHelper.buildClient())
8180
}
8281

@@ -103,7 +102,7 @@ class VulnerableCode(name: String, config: AdvisorConfiguration) : Advisor(name,
103102
pkg to listOf(
104103
AdvisorResult(
105104
vulnerabilities,
106-
AdvisorDetails(advisorName),
105+
AdvisorDetails(providerName),
107106
AdvisorSummary(startTime, endTime)
108107
)
109108
)

cli/src/main/kotlin/commands/AdvisorCommand.kt

+10-8
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import org.ossreviewtoolkit.utils.expandTilde
4343
import org.ossreviewtoolkit.utils.safeMkdirs
4444

4545
class AdvisorCommand : CliktCommand(name = "advise", help = "Check dependencies for security vulnerabilities.") {
46-
private val allAdvisorsByName = Advisor.ALL.associateBy { it.advisorName.toUpperCase() }
46+
private val allVulnerabilityProvidersByName = Advisor.ALL.associateBy { it.providerName.toUpperCase() }
4747

4848
private val input by option(
4949
"--ort-file", "-i",
@@ -76,12 +76,14 @@ class AdvisorCommand : CliktCommand(name = "advise", help = "Check dependencies
7676

7777
private val globalOptionsForSubcommands by requireObject<GlobalOptions>()
7878

79-
private val advisorFactory by option(
79+
private val providerFactory by option(
8080
"--advisor", "-a",
81-
help = "The advisor to use, one of ${allAdvisorsByName.keys}"
82-
).convert { advisorName ->
83-
allAdvisorsByName[advisorName.toUpperCase()]
84-
?: throw BadParameterValue("Advisor '$advisorName' is not one of ${allAdvisorsByName.keys}")
81+
help = "The advisor to use, one of ${allVulnerabilityProvidersByName.keys}"
82+
).convert { providerName ->
83+
allVulnerabilityProvidersByName[providerName.toUpperCase()]
84+
?: throw BadParameterValue(
85+
"Advisor '$providerName' is not one of ${allVulnerabilityProvidersByName.keys}"
86+
)
8587
}.required()
8688

8789
private val skipExcluded by option(
@@ -101,9 +103,9 @@ class AdvisorCommand : CliktCommand(name = "advise", help = "Check dependencies
101103
}
102104
}
103105

104-
val advisor = advisorFactory.create(globalOptionsForSubcommands.config.advisor)
106+
val advisor = Advisor(providerFactory, globalOptionsForSubcommands.config.advisor)
105107

106-
println("Using advisor '${advisor.advisorName}'.")
108+
println("Using advisor '${providerFactory.providerName}'.")
107109

108110
val ortResult = advisor.retrieveVulnerabilityInformation(input, skipExcluded).mergeLabels(labels)
109111

0 commit comments

Comments
 (0)