Skip to content

Commit

Permalink
Add SPDX support (anchore#445)
Browse files Browse the repository at this point in the history
* add initial spdx support

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* expose FileOwner and use in SPDX presenter

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add initial json support for SPDX

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add remaining package fields

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add spdx license list generation + tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* keep fileOwner unexported from pkg

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* restore cli test util

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add external refs to spdx tag-value format

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add golang support to CPE generation

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* use tag-value format as default "spdx" format flavor

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add tests around spdx presenters + refactor presenter tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add bouncer exception for spdx tools-golang repo

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* remove spdx model questions

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
  • Loading branch information
wagoodman authored Jun 25, 2021
1 parent 4924c8f commit b733a53
Show file tree
Hide file tree
Showing 60 changed files with 4,290 additions and 581 deletions.
10 changes: 9 additions & 1 deletion .bouncer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@ permit:
- ISC
ignore-packages:
# packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE
- github.com/package-url/packageurl-go
- github.com/package-url/packageurl-go

# from: https://github.com/spdx/tools-golang/blob/main/LICENSE.code
# The tools-golang source code is provided and may be used, at your option,
# under either:
# * Apache License, version 2.0 (Apache-2.0), OR
# * GNU General Public License, version 2.0 or later (GPL-2.0-or-later).
# (we choose Apache-2.0)
- github.com/spdx/tools-golang
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/scylladb/go-set v1.0.2
github.com/sergi/go-diff v1.1.0
github.com/sirupsen/logrus v1.6.0
github.com/spdx/tools-golang v0.1.0
github.com/spf13/afero v1.2.2
github.com/spf13/cobra v1.0.1-0.20200909172742-8a63648dd905
github.com/spf13/pflag v1.0.5
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,9 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.1.0 h1:iDMNEPqQk6CdiDj6eWDIDw85j0wQ3IR3pH9p0X05TSQ=
github.com/spdx/tools-golang v0.1.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
Expand Down
171 changes: 18 additions & 153 deletions internal/presenter/packages/cyclonedx_presenter_test.go
Original file line number Diff line number Diff line change
@@ -1,169 +1,34 @@
package packages

import (
"bytes"
"flag"
"regexp"
"testing"

"github.com/anchore/stereoscope/pkg/filetree"

"github.com/anchore/stereoscope/pkg/imagetest"

"github.com/anchore/go-testutils"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
"github.com/sergi/go-diff/diffmatchpatch"
)

var update = flag.Bool("update", false, "update the *.golden files for json presenters")

func TestCycloneDxDirsPresenter(t *testing.T) {
var buffer bytes.Buffer

catalog := pkg.NewCatalog()

// populate catalog with test data
catalog.Add(pkg.Package{
Name: "package1",
Version: "1.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-1",
Locations: []source.Location{
{RealPath: "/some/path/pkg1"},
},
Metadata: pkg.DpkgMetadata{
Package: "package1",
Version: "1.0.1",
Architecture: "amd64",
},
})
catalog.Add(pkg.Package{
Name: "package2",
Version: "2.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
Locations: []source.Location{
{RealPath: "/some/path/pkg1"},
},
Licenses: []string{
"MIT",
"Apache-v2",
},
Metadata: pkg.DpkgMetadata{
Package: "package2",
Version: "1.0.2",
Architecture: "amd64",
},
})

s, err := source.NewFromDirectory("/some/path")
if err != nil {
t.Fatal(err)
}

pres := NewCycloneDxPresenter(catalog, s.Metadata)

// run presenter
err = pres.Present(&buffer)
if err != nil {
t.Fatal(err)
}
actual := buffer.Bytes()

if *update {
testutils.UpdateGoldenFileContents(t, actual)
}

var expected = testutils.GetGoldenFileContents(t)

// remove dynamic values, which are tested independently
actual = redact(actual)
expected = redact(expected)

if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(actual), string(expected), true)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}
var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden files for cyclone-dx presenters")

func TestCycloneDxDirectoryPresenter(t *testing.T) {
catalog, metadata, _ := presenterDirectoryInput(t)
assertPresenterAgainstGoldenSnapshot(t,
NewCycloneDxPresenter(catalog, metadata),
*updateCycloneDx,
cycloneDxRedactor,
)
}

func TestCycloneDxImgsPresenter(t *testing.T) {
var buffer bytes.Buffer

catalog := pkg.NewCatalog()
img := imagetest.GetFixtureImage(t, "docker-archive", "image-simple")

_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
_, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks)

// populate catalog with test data
catalog.Add(pkg.Package{
Name: "package1",
Version: "1.0.1",
Locations: []source.Location{
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
},
Type: pkg.RpmPkg,
FoundBy: "the-cataloger-1",
PURL: "the-purl-1",
})
catalog.Add(pkg.Package{
Name: "package2",
Version: "2.0.1",
Locations: []source.Location{
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
},
Type: pkg.RpmPkg,
FoundBy: "the-cataloger-2",
Licenses: []string{
"MIT",
"Apache-v2",
},
PURL: "the-purl-2",
})

s, err := source.NewFromImage(img, "user-image-input")
if err != nil {
t.Fatal(err)
}

// This accounts for the non-deterministic digest value that we end up with when
// we build a container image dynamically during testing. Ultimately, we should
// use a golden image as a test fixture in place of building this image during
// testing. At that time, this line will no longer be necessary.
//
// This value is sourced from the "version" node in "./test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden"
s.Metadata.ImageMetadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"

pres := NewCycloneDxPresenter(catalog, s.Metadata)

// run presenter
err = pres.Present(&buffer)
if err != nil {
t.Fatal(err)
}
actual := buffer.Bytes()

if *update {
testutils.UpdateGoldenFileContents(t, actual)
}

var expected = testutils.GetGoldenFileContents(t)

// remove dynamic values, which are tested independently
actual = redact(actual)
expected = redact(expected)

if !bytes.Equal(expected, actual) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(expected), string(actual), true)
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
}
func TestCycloneDxImagePresenter(t *testing.T) {
testImage := "image-simple"
catalog, metadata, _ := presenterImageInput(t, testImage)
assertPresenterAgainstGoldenImageSnapshot(t,
NewCycloneDxPresenter(catalog, metadata),
testImage,
*updateCycloneDx,
cycloneDxRedactor,
)
}

func redact(s []byte) []byte {
func cycloneDxRedactor(s []byte) []byte {
serialPattern := regexp.MustCompile(`serialNumber="[a-zA-Z0-9\-:]+"`)
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)

Expand Down
Loading

0 comments on commit b733a53

Please sign in to comment.