From 62885308353f567dad5398220c6a22964902da58 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 14 Feb 2024 08:18:16 -0500 Subject: [PATCH] fix: add BOMRef to CycloneDX OS Component (#2634) --- .../cyclonedxhelpers/to_format_model.go | 18 +++- .../cyclonedxhelpers/to_format_model_test.go | 87 +++++++++++++++++++ .../TestCycloneDxDirectoryEncoder.golden | 1 + .../snapshot/TestCycloneDxImageEncoder.golden | 1 + .../TestCycloneDxDirectoryEncoder.golden | 2 +- .../snapshot/TestCycloneDxImageEncoder.golden | 2 +- 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/syft/format/common/cyclonedxhelpers/to_format_model.go b/syft/format/common/cyclonedxhelpers/to_format_model.go index 15f34a71d..6ad7e8102 100644 --- a/syft/format/common/cyclonedxhelpers/to_format_model.go +++ b/syft/format/common/cyclonedxhelpers/to_format_model.go @@ -1,6 +1,7 @@ package cyclonedxhelpers import ( + "fmt" "slices" "strings" "time" @@ -84,8 +85,9 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component { } return []cyclonedx.Component{ { - Type: cyclonedx.ComponentTypeOS, - // FIXME is it idiomatic to be using SWID here for specific name and version information? + BOMRef: toOSBomRef(distro.ID, distro.VersionID), + Type: cyclonedx.ComponentTypeOS, + // is it idiomatic to be using SWID here for specific name and version information? SWID: &cyclonedx.SWID{ TagID: distro.ID, Name: distro.ID, @@ -94,7 +96,7 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component { Description: distro.PrettyName, Name: distro.ID, Version: distro.VersionID, - // TODO should we add a PURL? + // should we add a PURL? CPE: formatCPE(distro.CPEName), ExternalReferences: eRefs, Properties: properties, @@ -102,6 +104,16 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component { } } +func toOSBomRef(name string, version string) string { + if name == "" { + return "os:unknown" + } + if version == "" { + return fmt.Sprintf("os:%s", name) + } + return fmt.Sprintf("os:%s@%s", name, version) +} + func formatCPE(cpeString string) string { c, err := cpe.NewAttributes(cpeString) if err != nil { diff --git a/syft/format/common/cyclonedxhelpers/to_format_model_test.go b/syft/format/common/cyclonedxhelpers/to_format_model_test.go index 432643804..8ac465b90 100644 --- a/syft/format/common/cyclonedxhelpers/to_format_model_test.go +++ b/syft/format/common/cyclonedxhelpers/to_format_model_test.go @@ -11,6 +11,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers" + "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" @@ -234,3 +235,89 @@ func Test_toBomDescriptor(t *testing.T) { }) } } + +func Test_toOsComponent(t *testing.T) { + tests := []struct { + name string + release linux.Release + expected cyclonedx.Component + }{ + { + name: "basic os component", + release: linux.Release{ + ID: "myLinux", + VersionID: "myVersion", + }, + expected: cyclonedx.Component{ + BOMRef: "os:myLinux@myVersion", + Type: cyclonedx.ComponentTypeOS, + Name: "myLinux", + Version: "myVersion", + SWID: &cyclonedx.SWID{ + TagID: "myLinux", + Name: "myLinux", + Version: "myVersion", + }, + Properties: &[]cyclonedx.Property{ + { + Name: "syft:distro:id", + Value: "myLinux", + }, + { + Name: "syft:distro:versionID", + Value: "myVersion", + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + gotSlice := toOSComponent(&test.release) + require.Len(t, gotSlice, 1) + got := gotSlice[0] + require.Equal(t, test.expected, got) + }) + } +} + +func Test_toOSBomRef(t *testing.T) { + tests := []struct { + name string + osName string + osVersion string + expected string + }{ + { + name: "no name or version specified", + osName: "", + osVersion: "", + expected: "os:unknown", + }, + { + name: "no version specified", + osName: "my-name", + osVersion: "", + expected: "os:my-name", + }, + { + name: "no name specified", + osName: "", + osVersion: "my-version", + expected: "os:unknown", + }, + { + name: "both name and version specified", + osName: "my-name", + osVersion: "my-version", + expected: "os:my-name@my-version", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := toOSBomRef(test.osName, test.osVersion) + require.Equal(t, test.expected, got) + }) + } +} diff --git a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 4f1190ece..a39921ac8 100644 --- a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -91,6 +91,7 @@ ] }, { + "bom-ref":"redacted", "type": "operating-system", "name": "debian", "version": "1.2.3", diff --git a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 3b4844b02..5bbd1a51b 100644 --- a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -100,6 +100,7 @@ ] }, { + "bom-ref":"redacted", "type": "operating-system", "name": "debian", "version": "1.2.3", diff --git a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index c56f6724b..9c52a354a 100644 --- a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -47,7 +47,7 @@ 0 - + debian 1.2.3 debian diff --git a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 8e0c8800f..c130be60d 100644 --- a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -50,7 +50,7 @@ 0 - + debian 1.2.3 debian