From 8847fddaa85b7d14bb2579d17618a299f40521b7 Mon Sep 17 00:00:00 2001 From: Shane Alvarez Date: Sun, 14 May 2023 18:34:35 -0600 Subject: [PATCH] fix: cyclonedx depends-on relationship inverted When reading CycloneDX boms, the relationships defined in the "dependencies" sections are translated into "artifact.Relationship" objects. Previously, relationships were inverted by this process. That is, a CyloneDx dependency "a depends on b" was being read as "a dependency of b". This caused downstream processing to write out the dependency relationship incorrectly. This commit fixes the issue by inverting the relationship during decoding and adding a regression test. See anchore/syft#1815 Signed-off-by: Shane Alvarez --- .../common/cyclonedxhelpers/decoder.go | 4 +- .../common/cyclonedxhelpers/decoder_test.go | 58 ++++++++++++++++--- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/decoder.go b/syft/formats/common/cyclonedxhelpers/decoder.go index 727c668a403..ef81bf9997e 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder.go +++ b/syft/formats/common/cyclonedxhelpers/decoder.go @@ -206,7 +206,7 @@ func collectRelationships(bom *cyclonedx.BOM, s *sbom.SBOM, idMap map[string]int return } for _, d := range *bom.Dependencies { - from, fromExists := idMap[d.Ref].(artifact.Identifiable) + to, fromExists := idMap[d.Ref].(artifact.Identifiable) if !fromExists { continue } @@ -216,7 +216,7 @@ func collectRelationships(bom *cyclonedx.BOM, s *sbom.SBOM, idMap map[string]int } for _, t := range *d.Dependencies { - to, toExists := idMap[t].(artifact.Identifiable) + from, toExists := idMap[t].(artifact.Identifiable) if !toExists { continue } diff --git a/syft/formats/common/cyclonedxhelpers/decoder_test.go b/syft/formats/common/cyclonedxhelpers/decoder_test.go index 4daa4f8c8b8..751574d0e0c 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder_test.go +++ b/syft/formats/common/cyclonedxhelpers/decoder_test.go @@ -7,6 +7,8 @@ import ( "testing" "github.com/CycloneDX/cyclonedx-go" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/sbom" "github.com/stretchr/testify/assert" ) @@ -184,16 +186,16 @@ func Test_decode(t *testing.T) { ver: "1.2.3", }, { - pkg: "package-1", - ver: "1.0.1", - cpe: "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", - purl: "pkg:some/package-1@1.0.1?arch=arm64&upstream=upstream1&distro=alpine-1", - relation: "package-2", + pkg: "package-1", + ver: "1.0.1", + cpe: "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", + purl: "pkg:some/package-1@1.0.1?arch=arm64&upstream=upstream1&distro=alpine-1", }, { - pkg: "package-2", - ver: "2.0.2", - purl: "pkg:apk/alpine/alpine-baselayout@3.2.0-r16?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.14.2", + pkg: "package-2", + ver: "2.0.2", + purl: "pkg:apk/alpine/alpine-baselayout@3.2.0-r16?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.14.2", + relation: "package-1", }, }, }, @@ -257,6 +259,46 @@ func Test_decode(t *testing.T) { } } +func Test_relationshipDirection(t *testing.T) { + cyclonedx_bom := cyclonedx.BOM{Metadata: nil, + Components: &[]cyclonedx.Component{ + { + BOMRef: "p1", + Type: cyclonedx.ComponentTypeLibrary, + Name: "package-1", + Version: "1.0.1", + PackageURL: "pkg:some/package-1@1.0.1?arch=arm64&upstream=upstream1&distro=alpine-1", + }, + { + BOMRef: "p2", + Type: cyclonedx.ComponentTypeLibrary, + Name: "package-2", + Version: "2.0.2", + PackageURL: "pkg:some/package-2@2.0.2?arch=arm64&upstream=upstream1&distro=alpine-1", + }, + }, + Dependencies: &[]cyclonedx.Dependency{ + { + Ref: "p1", + Dependencies: &[]string{"p2"}, + }, + }} + sbom, err := ToSyftModel(&cyclonedx_bom) + assert.Nil(t, err) + assert.Len(t, sbom.Relationships, 1) + relationship := sbom.Relationships[0] + + // check that p2 -- dependency of --> p1 + // same as p1 -- depends on --> p2 + assert.Equal(t, artifact.DependencyOfRelationship, relationship.Type) + assert.Equal(t, "package-2", packageNameFromIdentifier(sbom, relationship.From)) + assert.Equal(t, "package-1", packageNameFromIdentifier(sbom, relationship.To)) +} + +func packageNameFromIdentifier(model *sbom.SBOM, identifier artifact.Identifiable) string { + return model.Artifacts.Packages.Package(identifier.ID()).Name +} + func Test_missingDataDecode(t *testing.T) { bom := &cyclonedx.BOM{ Metadata: nil,