Ensure that all cyclonedx components have bom-refs (#914)

Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Sambhav Kothari 2022-04-01 17:19:30 +01:00 committed by GitHub
parent 68b7ad9770
commit 8bc5d84481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 111 additions and 40 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/CycloneDX/cyclonedx-go" "github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/formats/common" "github.com/anchore/syft/internal/formats/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
@ -39,9 +40,23 @@ func encodeComponent(p pkg.Package) cyclonedx.Component {
Description: encodeDescription(p), Description: encodeDescription(p),
ExternalReferences: encodeExternalReferences(p), ExternalReferences: encodeExternalReferences(p),
Properties: properties, Properties: properties,
BOMRef: deriveBomRef(p),
} }
} }
func deriveBomRef(p pkg.Package) string {
// try and parse the PURL if possible and append syft id to it, to make
// the purl unique in the BOM.
// TODO: In the future we may want to dedupe by PURL and combine components with
// the same PURL while preserving their unique metadata.
if parsedPURL, err := packageurl.FromString(p.PURL); err == nil {
parsedPURL.Qualifiers = append(parsedPURL.Qualifiers, packageurl.Qualifier{Key: "syft-id", Value: string(p.ID())})
return parsedPURL.ToString()
}
// fallback is to use strictly the ID if there is no valid pURL
return string(p.ID())
}
func hasMetadata(p pkg.Package) bool { func hasMetadata(p pkg.Package) bool {
return p.Metadata != nil return p.Metadata != nil
} }

View File

@ -1,6 +1,7 @@
package cyclonedxhelpers package cyclonedxhelpers
import ( import (
"fmt"
"testing" "testing"
"github.com/CycloneDX/cyclonedx-go" "github.com/CycloneDX/cyclonedx-go"
@ -139,3 +140,54 @@ func Test_encodeComponentProperties(t *testing.T) {
}) })
} }
} }
func Test_deriveBomRef(t *testing.T) {
pkgWithPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "pkg:pypi/django@1.11.1",
}
pkgWithPurl.SetID()
pkgWithOutPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "",
}
pkgWithOutPurl.SetID()
pkgWithBadPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "pkg:pyjango@1.11.1",
}
pkgWithBadPurl.SetID()
tests := []struct {
name string
pkg pkg.Package
want string
}{
{
name: "use pURL-id hybrid",
pkg: pkgWithPurl,
want: fmt.Sprintf("pkg:pypi/django@1.11.1?syft-id=%s", pkgWithPurl.ID()),
},
{
name: "fallback to ID when pURL is invalid",
pkg: pkgWithBadPurl,
want: string(pkgWithBadPurl.ID()),
},
{
name: "fallback to ID when pURL is missing",
pkg: pkgWithOutPurl,
want: string(pkgWithOutPurl.ID()),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.pkg.ID()
assert.Equal(t, tt.want, deriveBomRef(tt.pkg))
})
}
}

View File

@ -169,7 +169,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
Name: "package-1", Name: "package-1",
Version: "1.0.1", Version: "1.0.1",
}, },
PURL: "a-purl-1", PURL: "a-purl-1", // intentionally a bad pURL for test fixtures
CPEs: []pkg.CPE{ CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"), pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
}, },
@ -187,7 +187,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
Package: "package-2", Package: "package-2",
Version: "2.0.1", Version: "2.0.1",
}, },
PURL: "a-purl-2", PURL: "pkg:deb/debian/package-2@2.0.1",
CPEs: []pkg.CPE{ CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"), pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
}, },
@ -249,7 +249,7 @@ func newDirectoryCatalog() *pkg.Catalog {
}, },
}, },
}, },
PURL: "a-purl-2", PURL: "a-purl-2", // intentionally a bad pURL for test fixtures
CPEs: []pkg.CPE{ CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"), pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
}, },
@ -267,7 +267,7 @@ func newDirectoryCatalog() *pkg.Catalog {
Package: "package-2", Package: "package-2",
Version: "2.0.1", Version: "2.0.1",
}, },
PURL: "a-purl-2", PURL: "pkg:deb/debian/package-2@2.0.1",
CPEs: []pkg.CPE{ CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"), pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
}, },

View File

@ -1,10 +1,10 @@
{ {
"bomFormat": "CycloneDX", "bomFormat": "CycloneDX",
"specVersion": "1.4", "specVersion": "1.4",
"serialNumber": "urn:uuid:498e659b-0758-4a7f-816e-91bee18df634", "serialNumber": "urn:uuid:dec3f6b4-8458-48bb-b60d-dfd312f6ec4e",
"version": 1, "version": 1,
"metadata": { "metadata": {
"timestamp": "2022-03-08T12:30:39Z", "timestamp": "2022-04-01T11:48:04-04:00",
"tools": [ "tools": [
{ {
"vendor": "anchore", "vendor": "anchore",
@ -20,6 +20,7 @@
}, },
"components": [ "components": [
{ {
"bom-ref": "b85dbb4e6ece5082",
"type": "library", "type": "library",
"name": "package-1", "name": "package-1",
"version": "1.0.1", "version": "1.0.1",
@ -56,11 +57,12 @@
] ]
}, },
{ {
"bom-ref": "pkg:deb/debian/package-2@2.0.1?syft-id=ceda99598967ae8d",
"type": "library", "type": "library",
"name": "package-2", "name": "package-2",
"version": "2.0.1", "version": "2.0.1",
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", "cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"purl": "a-purl-2", "purl": "pkg:deb/debian/package-2@2.0.1",
"properties": [ "properties": [
{ {
"name": "syft:package:foundBy", "name": "syft:package:foundBy",

View File

@ -1,10 +1,10 @@
{ {
"bomFormat": "CycloneDX", "bomFormat": "CycloneDX",
"specVersion": "1.4", "specVersion": "1.4",
"serialNumber": "urn:uuid:342c3d2c-d26e-47b6-94d6-92fbf41da945", "serialNumber": "urn:uuid:054d973e-fe99-4762-92e4-eaf01997ae41",
"version": 1, "version": 1,
"metadata": { "metadata": {
"timestamp": "2022-03-08T12:30:39Z", "timestamp": "2022-04-01T11:48:04-04:00",
"tools": [ "tools": [
{ {
"vendor": "anchore", "vendor": "anchore",
@ -13,7 +13,7 @@
} }
], ],
"component": { "component": {
"bom-ref": "711095b1cdf90cce", "bom-ref": "e777314b02b362e4",
"type": "container", "type": "container",
"name": "user-image-input", "name": "user-image-input",
"version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" "version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
@ -21,6 +21,7 @@
}, },
"components": [ "components": [
{ {
"bom-ref": "2a46171f91c8d4bc",
"type": "library", "type": "library",
"name": "package-1", "name": "package-1",
"version": "1.0.1", "version": "1.0.1",
@ -52,7 +53,7 @@
}, },
{ {
"name": "syft:location:0:layerID", "name": "syft:location:0:layerID",
"value": "sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab" "value": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59"
}, },
{ {
"name": "syft:location:0:path", "name": "syft:location:0:path",
@ -61,11 +62,12 @@
] ]
}, },
{ {
"bom-ref": "pkg:deb/debian/package-2@2.0.1?syft-id=ae77680e9b1d087e",
"type": "library", "type": "library",
"name": "package-2", "name": "package-2",
"version": "2.0.1", "version": "2.0.1",
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", "cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"purl": "a-purl-2", "purl": "pkg:deb/debian/package-2@2.0.1",
"properties": [ "properties": [
{ {
"name": "syft:package:foundBy", "name": "syft:package:foundBy",
@ -81,7 +83,7 @@
}, },
{ {
"name": "syft:location:0:layerID", "name": "syft:location:0:layerID",
"value": "sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67" "value": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec"
}, },
{ {
"name": "syft:location:0:path", "name": "syft:location:0:path",

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:892f8304-0142-45b1-b411-cade3c53057f" version="1"> <bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:554fd820-210b-40c8-8c0b-75690274e21c" version="1">
<metadata> <metadata>
<timestamp>2022-03-08T12:30:33Z</timestamp> <timestamp>2022-04-01T11:57:46-04:00</timestamp>
<tools> <tools>
<tool> <tool>
<vendor>anchore</vendor> <vendor>anchore</vendor>
@ -14,7 +14,7 @@
</component> </component>
</metadata> </metadata>
<components> <components>
<component type="library"> <component bom-ref="b85dbb4e6ece5082" type="library">
<name>package-1</name> <name>package-1</name>
<version>1.0.1</version> <version>1.0.1</version>
<licenses> <licenses>
@ -32,11 +32,11 @@
<property name="syft:location:0:path">/some/path/pkg1</property> <property name="syft:location:0:path">/some/path/pkg1</property>
</properties> </properties>
</component> </component>
<component type="library"> <component bom-ref="pkg:deb/debian/package-2@2.0.1?syft-id=ceda99598967ae8d" type="library">
<name>package-2</name> <name>package-2</name>
<version>2.0.1</version> <version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe> <cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
<purl>a-purl-2</purl> <purl>pkg:deb/debian/package-2@2.0.1</purl>
<properties> <properties>
<property name="syft:package:foundBy">the-cataloger-2</property> <property name="syft:package:foundBy">the-cataloger-2</property>
<property name="syft:package:metadataType">DpkgMetadata</property> <property name="syft:package:metadataType">DpkgMetadata</property>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:5fa94827-eb85-4f32-a62d-76fb6e89a2dd" version="1"> <bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1535f940-172f-4d97-8280-d5a5764d1557" version="1">
<metadata> <metadata>
<timestamp>2022-03-08T12:30:33Z</timestamp> <timestamp>2022-04-01T11:57:46-04:00</timestamp>
<tools> <tools>
<tool> <tool>
<vendor>anchore</vendor> <vendor>anchore</vendor>
@ -9,13 +9,13 @@
<version>[not provided]</version> <version>[not provided]</version>
</tool> </tool>
</tools> </tools>
<component bom-ref="711095b1cdf90cce" type="container"> <component bom-ref="e777314b02b362e4" type="container">
<name>user-image-input</name> <name>user-image-input</name>
<version>sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368</version> <version>sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368</version>
</component> </component>
</metadata> </metadata>
<components> <components>
<component type="library"> <component bom-ref="2a46171f91c8d4bc" type="library">
<name>package-1</name> <name>package-1</name>
<version>1.0.1</version> <version>1.0.1</version>
<licenses> <licenses>
@ -30,20 +30,20 @@
<property name="syft:package:language">python</property> <property name="syft:package:language">python</property>
<property name="syft:package:metadataType">PythonPackageMetadata</property> <property name="syft:package:metadataType">PythonPackageMetadata</property>
<property name="syft:package:type">python</property> <property name="syft:package:type">python</property>
<property name="syft:location:0:layerID">sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab</property> <property name="syft:location:0:layerID">sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59</property>
<property name="syft:location:0:path">/somefile-1.txt</property> <property name="syft:location:0:path">/somefile-1.txt</property>
</properties> </properties>
</component> </component>
<component type="library"> <component bom-ref="pkg:deb/debian/package-2@2.0.1?syft-id=ae77680e9b1d087e" type="library">
<name>package-2</name> <name>package-2</name>
<version>2.0.1</version> <version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe> <cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
<purl>a-purl-2</purl> <purl>pkg:deb/debian/package-2@2.0.1</purl>
<properties> <properties>
<property name="syft:package:foundBy">the-cataloger-2</property> <property name="syft:package:foundBy">the-cataloger-2</property>
<property name="syft:package:metadataType">DpkgMetadata</property> <property name="syft:package:metadataType">DpkgMetadata</property>
<property name="syft:package:type">deb</property> <property name="syft:package:type">deb</property>
<property name="syft:location:0:layerID">sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67</property> <property name="syft:location:0:layerID">sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec</property>
<property name="syft:location:0:path">/somefile-2.txt</property> <property name="syft:location:0:path">/somefile-2.txt</property>
<property name="syft:metadata:installedSize">0</property> <property name="syft:metadata:installedSize">0</property>
</properties> </properties>

View File

@ -3,7 +3,7 @@
"name": "/some/path", "name": "/some/path",
"spdxVersion": "SPDX-2.2", "spdxVersion": "SPDX-2.2",
"creationInfo": { "creationInfo": {
"created": "2022-03-30T21:48:28.297464Z", "created": "2022-04-01T15:48:39.459232Z",
"creators": [ "creators": [
"Organization: Anchore, Inc", "Organization: Anchore, Inc",
"Tool: syft-[not provided]" "Tool: syft-[not provided]"
@ -11,7 +11,7 @@
"licenseListVersion": "3.16" "licenseListVersion": "3.16"
}, },
"dataLicense": "CC0-1.0", "dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/dir/some/path-e188d59b-76f6-4c7f-a9f2-1ae7d0577781", "documentNamespace": "https://anchore.com/syft/dir/some/path-8d335d81-29c9-4236-84f1-2292ea92aaf5",
"packages": [ "packages": [
{ {
"SPDXID": "SPDXRef-b85dbb4e6ece5082", "SPDXID": "SPDXRef-b85dbb4e6ece5082",
@ -48,7 +48,7 @@
}, },
{ {
"referenceCategory": "PACKAGE_MANAGER", "referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "a-purl-2", "referenceLocator": "pkg:deb/debian/package-2@2.0.1",
"referenceType": "purl" "referenceType": "purl"
} }
], ],

View File

@ -3,7 +3,7 @@
"name": "user-image-input", "name": "user-image-input",
"spdxVersion": "SPDX-2.2", "spdxVersion": "SPDX-2.2",
"creationInfo": { "creationInfo": {
"created": "2022-03-30T21:48:28.303986Z", "created": "2022-04-01T15:48:39.465643Z",
"creators": [ "creators": [
"Organization: Anchore, Inc", "Organization: Anchore, Inc",
"Tool: syft-[not provided]" "Tool: syft-[not provided]"
@ -11,7 +11,7 @@
"licenseListVersion": "3.16" "licenseListVersion": "3.16"
}, },
"dataLicense": "CC0-1.0", "dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/image/user-image-input-9e4f4190-c5ae-4e31-a852-d1ab71357516", "documentNamespace": "https://anchore.com/syft/image/user-image-input-e64e0be8-5031-4eec-842d-e59fb6deb518",
"packages": [ "packages": [
{ {
"SPDXID": "SPDXRef-2a46171f91c8d4bc", "SPDXID": "SPDXRef-2a46171f91c8d4bc",
@ -48,7 +48,7 @@
}, },
{ {
"referenceCategory": "PACKAGE_MANAGER", "referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "a-purl-2", "referenceLocator": "pkg:deb/debian/package-2@2.0.1",
"referenceType": "purl" "referenceType": "purl"
} }
], ],

View File

@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0 DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT SPDXID: SPDXRef-DOCUMENT
DocumentName: /some/path DocumentName: /some/path
DocumentNamespace: https://anchore.com/syft/dir/some/path-71aa3553-1a73-405f-9f1f-6347d6d4593b DocumentNamespace: https://anchore.com/syft/dir/some/path-d227b0f2-4ee8-4e10-ac43-019db86d16ff
LicenseListVersion: 3.16 LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided] Creator: Tool: syft-[not provided]
Created: 2022-03-30T21:48:22Z Created: 2022-04-01T15:48:44Z
##### Package: package-2 ##### Package: package-2
@ -19,7 +19,7 @@ PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
ExternalRef: PACKAGE_MANAGER purl a-purl-2 ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1 ##### Package: package-1

View File

@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0 DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT SPDXID: SPDXRef-DOCUMENT
DocumentName: user-image-input DocumentName: user-image-input
DocumentNamespace: https://anchore.com/syft/image/user-image-input-e46e20f4-43a4-40e7-9f82-fd55b8a89e5f DocumentNamespace: https://anchore.com/syft/image/user-image-input-49f98c61-3418-4427-9e00-8b1c735e9799
LicenseListVersion: 3.16 LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided] Creator: Tool: syft-[not provided]
Created: 2022-03-30T21:48:22Z Created: 2022-04-01T15:48:44Z
##### Package: package-2 ##### Package: package-2
@ -19,7 +19,7 @@ PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
ExternalRef: PACKAGE_MANAGER purl a-purl-2 ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1 ##### Package: package-1

View File

@ -51,7 +51,7 @@
"cpes": [ "cpes": [
"cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
], ],
"purl": "a-purl-2", "purl": "pkg:deb/debian/package-2@2.0.1",
"metadataType": "DpkgMetadata", "metadataType": "DpkgMetadata",
"metadata": { "metadata": {
"package": "package-2", "package": "package-2",

View File

@ -48,7 +48,7 @@
"cpes": [ "cpes": [
"cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
], ],
"purl": "a-purl-2", "purl": "pkg:deb/debian/package-2@2.0.1",
"metadataType": "DpkgMetadata", "metadataType": "DpkgMetadata",
"metadata": { "metadata": {
"package": "package-2", "package": "package-2",