Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include the OS Information available in the SBOM model in the SPDX reports #3462

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 64 additions & 18 deletions syft/format/common/spdxhelpers/to_format_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ const (
spdxPrimaryPurposeContainer = "CONTAINER"
spdxPrimaryPurposeFile = "FILE"
spdxPrimaryPurposeOther = "OTHER"
spdxPrimaryPurposeOS = "OPERATING-SYSTEM"

prefixImage = "Image"
prefixDirectory = "Directory"
prefixFile = "File"
prefixUnknown = "Unknown"
prefixOS = "OperatingSystem"
)

// ToFormatModel creates and populates a new SPDX document struct that follows the SPDX 2.3
Expand All @@ -52,33 +54,57 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
allRelationships := toRelationships(rels.All())

// for valid SPDX we need a document describes relationship
describesID := spdx.ElementID("DOCUMENT")

rootPackage := toRootPackage(s.Source)
if rootPackage != nil {
describesID = rootPackage.PackageSPDXIdentifier
osPackage := toOSPackage(s)

if rootPackage != nil && osPackage != nil {
// When both exist, rootPackage describes an OS

// add all relationships from the os root to all other packages
allRelationships = append(allRelationships, toRootRelationships(osPackage, packages)...)

packages = append(packages, rootPackage)
packages = append(packages, osPackage)

allRelationships = append(allRelationships, &spdx.Relationship{
RefA: spdx.DocElementID{
ElementRefID: rootPackage.PackageSPDXIdentifier,
},
Relationship: string(helpers.ContainsRelationship),
RefB: spdx.DocElementID{
ElementRefID: osPackage.PackageSPDXIdentifier,
},
})

allRelationships = append(allRelationships, &spdx.Relationship{
RefA: spdx.DocElementID{
ElementRefID: "DOCUMENT",
},
Relationship: string(helpers.DescribesRelationship),
RefB: spdx.DocElementID{
ElementRefID: rootPackage.PackageSPDXIdentifier,
},
})
} else if rootPackage != nil {
// When root exists, then document describes a rootfs

// add all relationships from the document root to all other packages
allRelationships = append(allRelationships, toRootRelationships(rootPackage, packages)...)

// append the root package
packages = append(packages, rootPackage)
// add a relationship for the package the document describes
allRelationships = append(allRelationships, &spdx.Relationship{
RefA: spdx.DocElementID{
ElementRefID: "DOCUMENT",
},
Relationship: string(helpers.DescribesRelationship),
RefB: spdx.DocElementID{
ElementRefID: rootPackage.PackageSPDXIdentifier,
},
})
}

// add a relationship for the package the document describes
documentDescribesRelationship := &spdx.Relationship{
RefA: spdx.DocElementID{
ElementRefID: "DOCUMENT",
},
Relationship: string(helpers.DescribesRelationship),
RefB: spdx.DocElementID{
ElementRefID: describesID,
},
}

// add the root document relationship
allRelationships = append(allRelationships, documentDescribesRelationship)

return &spdx.Document{
// 6.1: SPDX Version; should be in the format "SPDX-x.x"
// Cardinality: mandatory, one
Expand Down Expand Up @@ -157,6 +183,26 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
}
}

func toOSPackage(s sbom.SBOM) *spdx.Package {
distro := s.Artifacts.LinuxDistribution
if distro == nil {
return nil
}
return &spdx.Package{
PackageName: distro.ID,
PackageDescription: distro.String(),
PackageSPDXIdentifier: spdx.ElementID(helpers.SanitizeElementID(fmt.Sprintf("%s-%s", prefixOS, strings.ToLower(distro.ID)))),
PackageVersion: distro.VersionID,
PrimaryPackagePurpose: spdxPrimaryPurposeOS,
PackageSupplier: &spdx.Supplier{
Supplier: helpers.NOASSERTION,
},
PackageDownloadLocation: helpers.NOASSERTION,
PackageLicenseConcluded: helpers.NOASSERTION,
PackageLicenseDeclared: helpers.NOASSERTION,
}
}

func toRootRelationships(rootPackage *spdx.Package, packages []*spdx.Package) (out []*spdx.Relationship) {
for _, p := range packages {
out = append(out, &spdx.Relationship{
Expand Down
17 changes: 10 additions & 7 deletions syft/format/spdxjson/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,19 @@ func TestSupportedVersions(t *testing.T) {
relationshipOffsetPerVersion := map[string]int{
// the package representing the source gets a relationship from the source package to all other packages found
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
"2.1": 2,
"2.2": 2,
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
"2.3": 0,
"2.1": 3,
"2.2": 3,
// the os is saved now as a package with primaryPackagePurpose = OPERATING-SYSTEM
// but honestly... don't understand what's happening here....
"2.3": 2,
}

pkgCountOffsetPerVersion := map[string]int{
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
"2.1": 2, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.2": 2, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
// the os is saved now as a package with primaryPackagePurpose = OPERATING-SYSTEM
// but honestly... don't understand what's happening here....
"2.3": 1,
}

for _, enc := range encs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,36 @@
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "FILE"
},
{
"name": "debian",
"description": "debian",
"versionInfo": "1.2.3",
"SPDXID": "SPDXRef-OperatingSystem-debian",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"supplier": "NOASSERTION",
"primaryPackagePurpose": "OPERATING-SYSTEM"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-39392bb5e270f669",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"relatedSpdxElement": "SPDXRef-OperatingSystem-debian",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Directory-some-path",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,36 @@
}
],
"primaryPackagePurpose": "CONTAINER"
},
{
"name": "debian",
"SPDXID": "SPDXRef-OperatingSystem-debian",
"description": "debian",
"versionInfo": "1.2.3",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "OPERATING-SYSTEM"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-OperatingSystem-debian",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Image-user-image-input",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@
}
],
"primaryPackagePurpose": "CONTAINER"
},
{
"name": "debian",
"SPDXID": "SPDXRef-OperatingSystem-debian",
"description": "debian",
"versionInfo": "1.2.3",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "OPERATING-SYSTEM"
}
],
"files": [
Expand Down Expand Up @@ -228,15 +240,20 @@
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"spdxElementId": "SPDXRef-OperatingSystem-debian",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-OperatingSystem-debian",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Image-user-image-input",
Expand Down
4 changes: 2 additions & 2 deletions syft/format/spdxtagvalue/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ func TestDecoder_Decode(t *testing.T) {
name: "dir-scan",
file: "snapshot/TestSPDXTagValueDirectoryEncoder.golden",
distro: "debian:1.2.3",
packages: []string{"package-1:1.0.1", "package-2:2.0.1"},
packages: []string{"package-1:1.0.1", "package-2:2.0.1", "debian:1.2.3"},
},
{
name: "image-scan",
file: "snapshot/TestSPDXTagValueImageEncoder.golden",
distro: "debian:1.2.3",
packages: []string{"package-1:1.0.1", "package-2:2.0.1"},
packages: []string{"package-1:1.0.1", "package-2:2.0.1", "debian:1.2.3"},
},
{
name: "not-an-sbom",
Expand Down
17 changes: 10 additions & 7 deletions syft/format/spdxtagvalue/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,19 @@ func TestSupportedVersions(t *testing.T) {
relationshipOffsetPerVersion := map[string]int{
// the package representing the source gets a relationship from the source package to all other packages found
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
"2.1": 2,
"2.2": 2,
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
"2.3": 0,
"2.1": 3,
"2.2": 3,
// the os is saved now as a package with primaryPackagePurpose = OPERATING-SYSTEM
// but honestly... don't understand what's happening here....
"2.3": 2,
}

pkgCountOffsetPerVersion := map[string]int{
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
"2.1": 2, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.2": 2, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
// the os is saved now as a package with primaryPackagePurpose = OPERATING-SYSTEM
// but honestly... don't understand what's happening here....
"2.3": 1,
}

for _, enc := range encs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=

##### Package: debian

PackageName: debian
SPDXID: SPDXRef-OperatingSystem-debian
PackageVersion: 1.2.3
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: OPERATING-SYSTEM
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageDescription: debian

##### Package: package-2

PackageName: package-2
Expand Down Expand Up @@ -104,7 +117,8 @@ Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-OperatingSystem-debian
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION

##### Package: debian

PackageName: debian
SPDXID: SPDXRef-OperatingSystem-debian
PackageVersion: 1.2.3
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: OPERATING-SYSTEM
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageDescription: debian

##### Package: package-2

PackageName: package-2
Expand Down Expand Up @@ -51,7 +64,8 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2

##### Relationships

Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-OperatingSystem-debian
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=

##### Package: debian

PackageName: debian
SPDXID: SPDXRef-OperatingSystem-debian
PackageVersion: 1.2.3
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: OPERATING-SYSTEM
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageDescription: debian

##### Package: package-2

PackageName: package-2
Expand Down Expand Up @@ -54,7 +67,8 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1

##### Relationships

Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-OperatingSystem-debian CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-OperatingSystem-debian
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

Loading