From 101f5e4187bbbb74226743587b8cc0a3b28f8cbf Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Sat, 7 Dec 2024 16:58:30 +0100 Subject: [PATCH] feat(cyclonedx): Add the dependency graph While CycloneDX's `Dependency` data structure would allow for a nested graph approach, [1] says that "Graphs with one node of depth are recommended", so each `Dependency` entry only lists its own respective direct dependencies. Resolves #3906. [1]: https://cyclonedx.org/use-cases/#dependency-graph Signed-off-by: Sebastian Schuberth --- ...reporter-expected-result-with-findings.json | 12 ++++++++++++ .../cyclonedx-reporter-expected-result.json | 12 ++++++++++++ .../cyclonedx-reporter-expected-result.xml | 9 +++++++++ .../cyclonedx/src/main/kotlin/BomExtensions.kt | 18 ++++++++++++++++++ .../src/main/kotlin/CycloneDxReporter.kt | 4 ++++ 5 files changed, 55 insertions(+) diff --git a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result-with-findings.json b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result-with-findings.json index 2ccf3fc8ed438..7ee74c03ecdce 100644 --- a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result-with-findings.json +++ b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result-with-findings.json @@ -263,5 +263,17 @@ "url": "pkg:npm/%40ort/project-with-findings@1.0", "comment": "Package-URL of the project" } + ], + "dependencies": [ + { + "ref": "NPM:@ort:project-with-findings:1.0", + "dependsOn": [ + "NPM:@ort:no-license-file:1.0", + "NPM:@ort:license-file:1.0", + "NPM:@ort:license-file-and-additional-licenses:1.0", + "NPM:@ort:concluded-license:1.0", + "NPM:@ort:declared-license:1.0" + ] + } ] } diff --git a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.json b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.json index 1b30378bbe1f1..d2f1e1fa8eecf 100644 --- a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.json +++ b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.json @@ -245,6 +245,18 @@ "comment": "URL to the Git repository of the projects" } ], + "dependencies": [ + { + "ref": "https://github.com/oss-review-toolkit/ort.git@main", + "dependsOn": [ + "NPM:@ort:no-license-file:1.0", + "NPM:@ort:license-file:1.0", + "NPM:@ort:license-file-and-additional-licenses:1.0", + "NPM:@ort:concluded-license:1.0", + "NPM:@ort:declared-license:1.0" + ] + } + ], "vulnerabilities": [ { "id": "CVE-2021-1234", diff --git a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.xml b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.xml index 704893273cc69..e01eca519c17d 100644 --- a/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.xml +++ b/plugins/reporters/cyclonedx/src/funTest/assets/cyclonedx-reporter-expected-result.xml @@ -361,6 +361,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. URL to the Git repository of the projects + + + + + + + + + CVE-2021-1234 diff --git a/plugins/reporters/cyclonedx/src/main/kotlin/BomExtensions.kt b/plugins/reporters/cyclonedx/src/main/kotlin/BomExtensions.kt index 86cf4bb7d24e2..5078accce74ca 100644 --- a/plugins/reporters/cyclonedx/src/main/kotlin/BomExtensions.kt +++ b/plugins/reporters/cyclonedx/src/main/kotlin/BomExtensions.kt @@ -33,6 +33,7 @@ import org.cyclonedx.Version import org.cyclonedx.generators.BomGeneratorFactory import org.cyclonedx.model.Bom import org.cyclonedx.model.Component +import org.cyclonedx.model.Dependency import org.cyclonedx.model.ExtensibleType import org.cyclonedx.model.ExternalReference import org.cyclonedx.model.LicenseChoice @@ -48,6 +49,23 @@ import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.ort.ORT_NAME +/** + * Enrich this [Bom] with information about the hierarchy of dependencies, starting with the [parentRef] and its direct + * dependencies given as ORT [ids]. + */ +internal fun Bom.addDependencies(input: ReporterInput, parentRef: String, ids: Set) { + val dependency = Dependency(parentRef).apply { + dependencies = ids.map { id -> Dependency(id.toCoordinates()) } + } + + if (dependency.dependencies.isNotEmpty()) addDependency(dependency) + + ids.forEach { id -> + val directDependencies = input.ortResult.getDependencies(id, maxLevel = 1, omitExcluded = true) + addDependencies(input, id.toCoordinates(), directDependencies) + } +} + /** * Add a [ExternalReference] of the given [type] to this [Bom] which points to [url] and has an optional [comment]. */ diff --git a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt index 2eb307a1d63d1..e79849c401573 100644 --- a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt +++ b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt @@ -193,6 +193,8 @@ class CycloneDxReporter( bom.addComponent(input, pkg, dependencyType) } + bom.addDependencies(input, bom.metadata.component.bomRef, allDirectDependencies) + bom.addVulnerabilities(input.ortResult.getVulnerabilities()) reportFileResults += bom.writeFormats(schemaVersion, outputDir, REPORT_BASE_FILENAME, outputFileExtensions) @@ -253,6 +255,8 @@ class CycloneDxReporter( bom.addComponent(input, pkg, dependencyType) } + bom.addDependencies(input, bom.metadata.component.bomRef, directDependencies) + bom.addVulnerabilities(input.ortResult.getVulnerabilities()) val reportName = "$REPORT_BASE_FILENAME-${project.id.toPath("-")}"