Skip to content
Permalink
Browse files

CycloneDxReporter: Add project information as external references

Signed-off-by: Sebastian Schuberth <sebastian.schuberth@bosch-si.com>
  • Loading branch information...
sschuberth committed Sep 5, 2019
1 parent 89552bf commit 511132e3c63acef17a09f4fe66a93fba96861d62
@@ -33,6 +33,7 @@ import java.io.ByteArrayOutputStream
import java.io.File

import org.cyclonedx.BomParser
import org.cyclonedx.model.ExternalReference

class CycloneDxReporterTest : WordSpec({
"A generated BOM" should {
@@ -52,19 +53,18 @@ class CycloneDxReporterTest : WordSpec({

"match the result from the official Gradle plugin" {
val ortResultFile = File("src/funTest/assets/gradle-all-dependencies-result.yml")
val ortResult = yamlMapper.readValue(
patchExpectedResult(
ortResultFile,
url = "https://github.com/heremaps/oss-review-toolkit.git",
urlProcessed = "https://github.com/heremaps/oss-review-toolkit.git",
revision = "9fded2ad79d07ab5cda44f2549301669ea10442a"
),
OrtResult::class.java
)

val bomBytesFromReporter = ByteArrayOutputStream().also { outputStream ->
CycloneDxReporter().generateReport(
outputStream,
yamlMapper.readValue(
patchExpectedResult(
ortResultFile,
url = "https://github.com/heremaps/oss-review-toolkit.git",
urlProcessed = "https://github.com/heremaps/oss-review-toolkit.git",
revision = "9fded2ad79d07ab5cda44f2549301669ea10442a"
),
OrtResult::class.java
)
)
CycloneDxReporter().generateReport(outputStream, ortResult)
}.toByteArray()
val bomFromReporter = BomParser().parse(bomBytesFromReporter).apply { components.sortBy { it.name } }

@@ -92,6 +92,13 @@ class CycloneDxReporterTest : WordSpec({
}
}

// TODO: Remove this once the official Gradle plugin adds project information as external references.
bomFromPlugin.addExternalReference(ExternalReference().apply {
type = ExternalReference.Type.VCS
url = ortResult.repository.vcsProcessed.url
comment = "URL to the Git repository of the projects"
})

// Clear out the unique serial numbers for comparison.
bomFromReporter.serialNumber = null
bomFromPlugin.serialNumber = null
@@ -33,6 +33,7 @@ import org.cyclonedx.BomGeneratorFactory
import org.cyclonedx.CycloneDxSchema
import org.cyclonedx.model.Bom
import org.cyclonedx.model.Component
import org.cyclonedx.model.ExternalReference
import org.cyclonedx.model.Hash
import org.cyclonedx.model.License
import org.cyclonedx.model.LicenseChoice
@@ -42,6 +43,16 @@ class CycloneDxReporter : Reporter() {
override val reporterName = "CycloneDx"
override val defaultFilename = "bom.xml"

private fun Bom.addExternalReference(type: ExternalReference.Type, url: String, comment: String? = null) {
if (url.isBlank()) return

addExternalReference(ExternalReference().also { ref ->
ref.type = type
ref.url = url
if (!comment.isNullOrBlank()) ref.comment = comment
})
}

private fun mapHash(hash: com.here.ort.model.Hash): Hash? =
enumValues<Hash.Algorithm>().find { it.spec == hash.algorithm.toString() }?.let { Hash(it, hash.value) }

@@ -55,6 +66,58 @@ class CycloneDxReporter : Reporter() {
) {
val bom = Bom().apply { serialNumber = "urn:uuid:${UUID.randomUUID()}" }

// Add information about projects as external references at the BOM level.
val rootProject = ortResult.getProjects().singleOrNull()
if (rootProject != null) {
// If there is only one project, it is clear that a single BOM should be created for that single project.
bom.addExternalReference(
ExternalReference.Type.VCS,
rootProject.vcsProcessed.url,
"URL to the project's ${rootProject.vcsProcessed.type} repository"
)

bom.addExternalReference(
ExternalReference.Type.WEBSITE,
rootProject.homepageUrl
)

val licenseNames = ortResult.getDetectedLicensesForId(rootProject.id) +
rootProject.declaredLicensesProcessed.allLicenses
bom.addExternalReference(
ExternalReference.Type.LICENSE,
licenseNames.joinToString(", ")
)

bom.addExternalReference(
ExternalReference.Type.BUILD_SYSTEM,
rootProject.id.type
)

bom.addExternalReference(
ExternalReference.Type.OTHER,
rootProject.id.toPurl(),
"Package-URL of the project"
)
} else {
// In case of multiple projects it is not always clear how many BOMs to create, and for which project(s):
//
// - If a multi-module project only produces a single application that gets distributed, then usually only a
// single BOM for that application is generated.
// - If a multi-module project produces multiple applications (e.g. if there is one module per independent
// micro-service), then usually for each project a BOM is generated as there are multiple things being
// distributed.
//
// As this distinction is hard to make programmatically (without additional information about the
// distributable), just create a single BOM for all projects in that case for now. As there also is no
// single correct project to pick for adding external references in that case, simply only use the global
// repository VCS information here.
bom.addExternalReference(
ExternalReference.Type.VCS,
ortResult.repository.vcsProcessed.url,
"URL to the ${ortResult.repository.vcsProcessed.type} repository of the projects"
)
}

ortResult.getPackages().forEach { (pkg, _) ->
// TODO: We should actually use the concluded license expression here, but we first need a workflow to
// ensure it is being set.

0 comments on commit 511132e

Please sign in to comment.
You can’t perform that action at this time.