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

feat(sbom): Add unmarshal for spdx #2868

Merged
merged 13 commits into from Sep 15, 2022
3 changes: 1 addition & 2 deletions integration/client_server_test.go
Expand Up @@ -12,10 +12,9 @@ import (
"testing"
"time"

"github.com/samber/lo"

cdx "github.com/CycloneDX/cyclonedx-go"
"github.com/docker/go-connections/nat"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
testcontainers "github.com/testcontainers/testcontainers-go"
Expand Down
8 changes: 4 additions & 4 deletions integration/docker_engine_test.go
Expand Up @@ -11,7 +11,7 @@ import (
"strings"
"testing"

"github.com/docker/docker/api/types"
api "github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -213,7 +213,7 @@ func TestDockerEngine(t *testing.T) {
require.NoError(t, err, tt.name)

// ensure image doesnt already exists
_, _ = cli.ImageRemove(ctx, tt.input, types.ImageRemoveOptions{
_, _ = cli.ImageRemove(ctx, tt.input, api.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
Expand Down Expand Up @@ -264,11 +264,11 @@ func TestDockerEngine(t *testing.T) {
compareReports(t, tt.golden, output)

// cleanup
_, err = cli.ImageRemove(ctx, tt.input, types.ImageRemoveOptions{
_, err = cli.ImageRemove(ctx, tt.input, api.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
_, err = cli.ImageRemove(ctx, tt.imageTag, types.ImageRemoveOptions{
_, err = cli.ImageRemove(ctx, tt.imageTag, api.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
Expand Down
106 changes: 95 additions & 11 deletions integration/sbom_test.go
Expand Up @@ -8,23 +8,28 @@ import (
"testing"

cdx "github.com/CycloneDX/cyclonedx-go"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)

func TestCycloneDX(t *testing.T) {
func TestSBOM(t *testing.T) {
type args struct {
input string
format string
artifactType string
}
tests := []struct {
name string
args args
golden string
name string
args args
golden string
override types.Report
}{
{
name: "centos7-bom by trivy",
name: "centos7 cyclonedx",
args: args{
input: "testdata/fixtures/sbom/centos-7-cyclonedx.json",
format: "cyclonedx",
Expand All @@ -33,7 +38,7 @@ func TestCycloneDX(t *testing.T) {
golden: "testdata/centos-7-cyclonedx.json.golden",
},
{
name: "fluentd-multiple-lockfiles-bom by trivy",
name: "fluentd-multiple-lockfiles cyclonedx",
args: args{
input: "testdata/fixtures/sbom/fluentd-multiple-lockfiles-cyclonedx.json",
format: "cyclonedx",
Expand All @@ -42,14 +47,60 @@ func TestCycloneDX(t *testing.T) {
golden: "testdata/fluentd-multiple-lockfiles-cyclonedx.json.golden",
},
{
name: "centos7-bom in in-toto attestation",
name: "centos7 in in-toto attestation",
args: args{
input: "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl",
format: "cyclonedx",
artifactType: "cyclonedx",
},
golden: "testdata/centos-7-cyclonedx.json.golden",
},
{
name: "centos7 spdx tag-value",
args: args{
input: "testdata/fixtures/sbom/centos-7-spdx.txt",
format: "json",
artifactType: "spdx",
},
golden: "testdata/centos-7.json.golden",
override: types.Report{
ArtifactName: "testdata/fixtures/sbom/centos-7-spdx.txt",
ArtifactType: ftypes.ArtifactType("spdx"),
Results: types.Results{
{
Target: "testdata/fixtures/sbom/centos-7-spdx.txt (centos 7.6.1810)",
Vulnerabilities: []types.DetectedVulnerability{
{Ref: "pkg:rpm/centos/bash@4.2.46-31.el7?arch=x86_64&distro=centos-7.6.1810"},
{Ref: "pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810"},
{Ref: "pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810"},
},
},
},
},
},
{
name: "centos7 spdx json",
args: args{
input: "testdata/fixtures/sbom/centos-7-spdx.json",
format: "json",
artifactType: "spdx",
},
golden: "testdata/centos-7.json.golden",
override: types.Report{
ArtifactName: "testdata/fixtures/sbom/centos-7-spdx.json",
ArtifactType: ftypes.ArtifactType("spdx"),
Results: types.Results{
{
Target: "testdata/fixtures/sbom/centos-7-spdx.json (centos 7.6.1810)",
Vulnerabilities: []types.DetectedVulnerability{
{Ref: "pkg:rpm/centos/bash@4.2.46-31.el7?arch=x86_64&distro=centos-7.6.1810"},
{Ref: "pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810"},
{Ref: "pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810"},
},
},
},
},
},
}

// Set up testing DB
Expand All @@ -61,7 +112,7 @@ func TestCycloneDX(t *testing.T) {
"--cache-dir", cacheDir, "sbom", "-q", "--skip-db-update", "--format", tt.args.format,
}

// Setup the output file
// Set up the output file
outputFile := filepath.Join(t.TempDir(), "output.json")
if *update {
outputFile = tt.golden
Expand All @@ -75,13 +126,46 @@ func TestCycloneDX(t *testing.T) {
assert.NoError(t, err)

// Compare want and got
want := decodeCycloneDX(t, tt.golden)
got := decodeCycloneDX(t, outputFile)
assert.Equal(t, want, got)
switch tt.args.format {
case "cyclonedx":
want := decodeCycloneDX(t, tt.golden)
got := decodeCycloneDX(t, outputFile)
assert.Equal(t, want, got)
case "json":
compareSBOMReports(t, tt.golden, outputFile, tt.override)
default:
require.Fail(t, "invalid format", "format: %s", tt.args.format)
}
})
}
}

// TODO(teppei): merge into compareReports
func compareSBOMReports(t *testing.T, wantFile, gotFile string, overrideWant types.Report) {
want := readReport(t, wantFile)

want.ArtifactName = overrideWant.ArtifactName
want.ArtifactType = overrideWant.ArtifactType
want.Metadata.ImageID = ""
want.Metadata.ImageConfig = v1.ConfigFile{}
want.Metadata.DiffIDs = nil
for i, result := range want.Results {
for j := range result.Vulnerabilities {
want.Results[i].Vulnerabilities[j].Layer.DiffID = ""
}
}

for i, result := range overrideWant.Results {
want.Results[i].Target = result.Target
for j, vuln := range result.Vulnerabilities {
want.Results[i].Vulnerabilities[j].Ref = vuln.Ref
}
}

got := readReport(t, gotFile)
assert.Equal(t, want, got)
}

func decodeCycloneDX(t *testing.T, filePath string) *cdx.BOM {
f, err := os.Open(filePath)
require.NoError(t, err)
Expand Down
94 changes: 94 additions & 0 deletions integration/testdata/fixtures/sbom/centos-7-spdx.json
@@ -0,0 +1,94 @@
{
"SPDXID": "SPDXRef-DOCUMENT",
"creationInfo": {
"created": "2022-09-13T13:27:55.874784Z",
"creators": [
"Tool: trivy",
"Organization: aquasecurity"
]
},
"dataLicense": "CC0-1.0",
"documentNamespace": "http://aquasecurity.github.io/trivy/container_image/integration/testdata/fixtures/images/centos-7.tar.gz-2906855d-5098-4a22-9a72-4f7099ea3d66",
"name": "integration/testdata/fixtures/images/centos-7.tar.gz",
"packages": [
{
"SPDXID": "SPDXRef-ContainerImage-dd5cad897c6263",
"attributionTexts": [
"SchemaVersion: 2",
"ImageID: sha256:f1cb7c7d58b73eac859c395882eec49d50651244e342cd6c68a5c7809785f427",
"DiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a"
],
"filesAnalyzed": false,
"name": "integration/testdata/fixtures/images/centos-7.tar.gz"
},
{
"SPDXID": "SPDXRef-OperatingSystem-2e91c856c499a371",
"filesAnalyzed": false,
"name": "centos",
"versionInfo": "7.6.1810"
},
{
"SPDXID": "SPDXRef-Package-5a18334f22149877",
"attributionTexts": [
"LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b",
"LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a"
],
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:rpm/centos/bash@4.2.46-31.el7?arch=x86_64\u0026distro=centos-7.6.1810",
"referenceType": "purl"
}
],
"filesAnalyzed": false,
"licenseConcluded": "GPLv3+",
"licenseDeclared": "GPLv3+",
"name": "bash",
"sourceInfo": "built package from: bash 4.2.46-31.el7",
"versionInfo": "4.2.46"
},
{
"SPDXID": "SPDXRef-Package-e16b1cbaa5186199",
"attributionTexts": [
"LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b",
"LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a"
],
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64\u0026distro=centos-7.6.1810",
"referenceType": "purl"
}
],
"filesAnalyzed": false,
"licenseConcluded": "OpenSSL",
"licenseDeclared": "OpenSSL",
"name": "openssl-libs",
"sourceInfo": "built package from: openssl-libs 1:1.0.2k-16.el7",
"versionInfo": "1.0.2k"
}
],
"relationships": [
{
"relatedSpdxElement": "SPDXRef-ContainerImage-dd5cad897c6263",
"relationshipType": "DESCRIBE",
"spdxElementId": "SPDXRef-DOCUMENT"
},
{
"relatedSpdxElement": "SPDXRef-OperatingSystem-2e91c856c499a371",
"relationshipType": "CONTAINS",
"spdxElementId": "SPDXRef-ContainerImage-dd5cad897c6263"
},
{
"relatedSpdxElement": "SPDXRef-Package-5a18334f22149877",
"relationshipType": "CONTAINS",
"spdxElementId": "SPDXRef-OperatingSystem-2e91c856c499a371"
},
{
"relatedSpdxElement": "SPDXRef-Package-e16b1cbaa5186199",
"relationshipType": "CONTAINS",
"spdxElementId": "SPDXRef-OperatingSystem-2e91c856c499a371"
}
],
"spdxVersion": "SPDX-2.2"
}
57 changes: 57 additions & 0 deletions integration/testdata/fixtures/sbom/centos-7-spdx.txt
@@ -0,0 +1,57 @@
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: integration/testdata/fixtures/images/centos-7.tar.gz
DocumentNamespace: http://aquasecurity.github.io/trivy/container_image/integration/testdata/fixtures/images/centos-7.tar.gz-6a2c050f-bc12-46dc-b2df-1f4e3e0b5e1d
Creator: Organization: aquasecurity
Creator: Tool: trivy
Created: 2022-09-13T13:24:58.796907Z

##### Package: integration/testdata/fixtures/images/centos-7.tar.gz

PackageName: integration/testdata/fixtures/images/centos-7.tar.gz
SPDXID: SPDXRef-ContainerImage-dd5cad897c6263
FilesAnalyzed: false
PackageAttributionText: SchemaVersion: 2
PackageAttributionText: ImageID: sha256:f1cb7c7d58b73eac859c395882eec49d50651244e342cd6c68a5c7809785f427
PackageAttributionText: DiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a

##### Package: centos

PackageName: centos
SPDXID: SPDXRef-OperatingSystem-2e91c856c499a371
PackageVersion: 7.6.1810
FilesAnalyzed: false

##### Package: bash

PackageName: bash
SPDXID: SPDXRef-Package-5a18334f22149877
PackageVersion: 4.2.46
FilesAnalyzed: false
PackageSourceInfo: built package from: bash 4.2.46-31.el7
PackageLicenseConcluded: GPLv3+
PackageLicenseDeclared: GPLv3+
ExternalRef: PACKAGE-MANAGER purl pkg:rpm/centos/bash@4.2.46-31.el7?arch=x86_64&distro=centos-7.6.1810
PackageAttributionText: LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b
PackageAttributionText: LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a

##### Package: openssl-libs

PackageName: openssl-libs
SPDXID: SPDXRef-Package-e16b1cbaa5186199
PackageVersion: 1.0.2k
FilesAnalyzed: false
PackageSourceInfo: built package from: openssl-libs 1:1.0.2k-16.el7
PackageLicenseConcluded: OpenSSL
PackageLicenseDeclared: OpenSSL
ExternalRef: PACKAGE-MANAGER purl pkg:rpm/centos/openssl-libs@1:1.0.2k-16.el7?arch=x86_64&distro=centos-7.6.1810
PackageAttributionText: LayerDigest: sha256:ac9208207adaac3a48e54a4dc6b49c69e78c3072d2b3add7efdabf814db2133b
PackageAttributionText: LayerDiffID: sha256:89169d87dbe2b72ba42bfbb3579c957322baca28e03a1e558076542a1c1b2b4a

##### Relationships

Relationship: SPDXRef-DOCUMENT DESCRIBE SPDXRef-ContainerImage-dd5cad897c6263
Relationship: SPDXRef-ContainerImage-dd5cad897c6263 CONTAINS SPDXRef-OperatingSystem-2e91c856c499a371
Relationship: SPDXRef-OperatingSystem-2e91c856c499a371 DEPENDS_ON SPDXRef-Package-5a18334f22149877
Relationship: SPDXRef-OperatingSystem-2e91c856c499a371 DEPENDS_ON SPDXRef-Package-e16b1cbaa5186199
11 changes: 11 additions & 0 deletions pkg/fanal/artifact/sbom/sbom.go
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/sbom"
"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
"github.com/aquasecurity/trivy/pkg/sbom/spdx"
)

type Artifact struct {
Expand Down Expand Up @@ -83,6 +84,9 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
switch format {
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON:
artifactType = types.ArtifactCycloneDX
case sbom.FormatSPDXTV, sbom.FormatSPDXJSON:
artifactType = types.ArtifactSPDX

}

return types.ArtifactReference{
Expand Down Expand Up @@ -117,6 +121,13 @@ func (a Artifact) Decode(f io.Reader, format sbom.Format) (sbom.SBOM, error) {
},
}
decoder = json.NewDecoder(f)
case sbom.FormatSPDXJSON:
v = &spdx.SPDX{SBOM: &bom}
decoder = json.NewDecoder(f)
case sbom.FormatSPDXTV:
v = &spdx.SPDX{SBOM: &bom}
decoder = spdx.NewTVDecoder(f)

default:
return sbom.SBOM{}, xerrors.Errorf("%s scanning is not yet supported", format)

Expand Down