mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Support Windows Directory Resolver Add function that converts windows to posix functionality Add function that converts posix to windows Add build tags to remove windows developer environment errors redact carriage return specific windows issues Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
271 lines
7.2 KiB
Go
271 lines
7.2 KiB
Go
package testutils
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/anchore/go-presenter"
|
|
"github.com/anchore/go-testutils"
|
|
"github.com/anchore/stereoscope/pkg/filetree"
|
|
"github.com/anchore/stereoscope/pkg/image"
|
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
|
"github.com/anchore/syft/syft/distro"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
"github.com/anchore/syft/syft/sbom"
|
|
"github.com/anchore/syft/syft/source"
|
|
"github.com/sergi/go-diff/diffmatchpatch"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type redactor func(s []byte) []byte
|
|
|
|
type imageCfg struct {
|
|
fromSnapshot bool
|
|
}
|
|
|
|
type ImageOption func(cfg *imageCfg)
|
|
|
|
func FromSnapshot() ImageOption {
|
|
return func(cfg *imageCfg) {
|
|
cfg.fromSnapshot = true
|
|
}
|
|
}
|
|
|
|
func AssertPresenterAgainstGoldenImageSnapshot(t *testing.T, pres presenter.Presenter, testImage string, updateSnapshot bool, redactors ...redactor) {
|
|
var buffer bytes.Buffer
|
|
|
|
// grab the latest image contents and persist
|
|
if updateSnapshot {
|
|
imagetest.UpdateGoldenFixtureImage(t, testImage)
|
|
}
|
|
|
|
err := pres.Present(&buffer)
|
|
assert.NoError(t, err)
|
|
actual := buffer.Bytes()
|
|
|
|
// replace the expected snapshot contents with the current presenter contents
|
|
if updateSnapshot {
|
|
testutils.UpdateGoldenFileContents(t, actual)
|
|
}
|
|
|
|
var expected = testutils.GetGoldenFileContents(t)
|
|
|
|
// remove dynamic values, which should be tested independently
|
|
redactors = append(redactors, carriageRedactor)
|
|
for _, r := range redactors {
|
|
actual = r(actual)
|
|
expected = r(expected)
|
|
}
|
|
|
|
// assert that the golden file snapshot matches the actual contents
|
|
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 AssertPresenterAgainstGoldenSnapshot(t *testing.T, pres presenter.Presenter, updateSnapshot bool, redactors ...redactor) {
|
|
var buffer bytes.Buffer
|
|
|
|
err := pres.Present(&buffer)
|
|
assert.NoError(t, err)
|
|
actual := buffer.Bytes()
|
|
|
|
// replace the expected snapshot contents with the current presenter contents
|
|
if updateSnapshot {
|
|
testutils.UpdateGoldenFileContents(t, actual)
|
|
}
|
|
|
|
var expected = testutils.GetGoldenFileContents(t)
|
|
|
|
// remove dynamic values, which should be tested independently
|
|
redactors = append(redactors, carriageRedactor)
|
|
for _, r := range redactors {
|
|
actual = r(actual)
|
|
expected = r(expected)
|
|
}
|
|
|
|
if !bytes.Equal(expected, actual) {
|
|
dmp := diffmatchpatch.New()
|
|
diffs := dmp.DiffMain(string(expected), string(actual), true)
|
|
t.Logf("len: %d\nexpected: %s", len(expected), expected)
|
|
t.Logf("len: %d\nactual: %s", len(actual), actual)
|
|
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
|
|
}
|
|
}
|
|
|
|
func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBOM {
|
|
t.Helper()
|
|
catalog := pkg.NewCatalog()
|
|
var cfg imageCfg
|
|
var img *image.Image
|
|
for _, opt := range options {
|
|
opt(&cfg)
|
|
}
|
|
|
|
switch cfg.fromSnapshot {
|
|
case true:
|
|
img = imagetest.GetGoldenFixtureImage(t, testImage)
|
|
default:
|
|
img = imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
|
}
|
|
|
|
populateImageCatalog(catalog, img)
|
|
|
|
// this is a hard coded value that is not given by the fixture helper and must be provided manually
|
|
img.Metadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
|
|
|
|
src, err := source.NewFromImage(img, "user-image-input")
|
|
assert.NoError(t, err)
|
|
|
|
dist, err := distro.NewDistro(distro.Debian, "1.2.3", "like!")
|
|
assert.NoError(t, err)
|
|
|
|
return sbom.SBOM{
|
|
Artifacts: sbom.Artifacts{
|
|
PackageCatalog: catalog,
|
|
Distro: &dist,
|
|
},
|
|
Source: src.Metadata,
|
|
Descriptor: sbom.Descriptor{
|
|
Name: "syft",
|
|
Version: "v0.42.0-bogus",
|
|
// the application configuration should be persisted here, however, we do not want to import
|
|
// the application configuration in this package (it's reserved only for ingestion by the cmd package)
|
|
Configuration: map[string]string{
|
|
"config-key": "config-value",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func carriageRedactor(s []byte) []byte {
|
|
msg := strings.ReplaceAll(string(s), "\r\n", "\n")
|
|
return []byte(msg)
|
|
}
|
|
|
|
func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
|
_, 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: "package-1",
|
|
Version: "1.0.1",
|
|
Locations: []source.Location{
|
|
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
|
|
},
|
|
Type: pkg.PythonPkg,
|
|
FoundBy: "the-cataloger-1",
|
|
Language: pkg.Python,
|
|
MetadataType: pkg.PythonPackageMetadataType,
|
|
Licenses: []string{"MIT"},
|
|
Metadata: pkg.PythonPackageMetadata{
|
|
Name: "package-1",
|
|
Version: "1.0.1",
|
|
},
|
|
PURL: "a-purl-1",
|
|
CPEs: []pkg.CPE{
|
|
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
|
},
|
|
})
|
|
catalog.Add(pkg.Package{
|
|
Name: "package-2",
|
|
Version: "2.0.1",
|
|
Locations: []source.Location{
|
|
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
|
|
},
|
|
Type: pkg.DebPkg,
|
|
FoundBy: "the-cataloger-2",
|
|
MetadataType: pkg.DpkgMetadataType,
|
|
Metadata: pkg.DpkgMetadata{
|
|
Package: "package-2",
|
|
Version: "2.0.1",
|
|
},
|
|
PURL: "a-purl-2",
|
|
CPEs: []pkg.CPE{
|
|
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func DirectoryInput(t testing.TB) sbom.SBOM {
|
|
catalog := newDirectoryCatalog()
|
|
|
|
dist, err := distro.NewDistro(distro.Debian, "1.2.3", "like!")
|
|
assert.NoError(t, err)
|
|
|
|
src, err := source.NewFromDirectory("/some/path")
|
|
assert.NoError(t, err)
|
|
|
|
return sbom.SBOM{
|
|
Artifacts: sbom.Artifacts{
|
|
PackageCatalog: catalog,
|
|
Distro: &dist,
|
|
},
|
|
Source: src.Metadata,
|
|
Descriptor: sbom.Descriptor{
|
|
Name: "syft",
|
|
Version: "v0.42.0-bogus",
|
|
// the application configuration should be persisted here, however, we do not want to import
|
|
// the application configuration in this package (it's reserved only for ingestion by the cmd package)
|
|
Configuration: map[string]string{
|
|
"config-key": "config-value",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func newDirectoryCatalog() *pkg.Catalog {
|
|
catalog := pkg.NewCatalog()
|
|
|
|
// populate catalog with test data
|
|
catalog.Add(pkg.Package{
|
|
Name: "package-1",
|
|
Version: "1.0.1",
|
|
Type: pkg.PythonPkg,
|
|
FoundBy: "the-cataloger-1",
|
|
Locations: []source.Location{
|
|
source.NewLocation("/some/path/pkg1"),
|
|
},
|
|
Language: pkg.Python,
|
|
MetadataType: pkg.PythonPackageMetadataType,
|
|
Licenses: []string{"MIT"},
|
|
Metadata: pkg.PythonPackageMetadata{
|
|
Name: "package-1",
|
|
Version: "1.0.1",
|
|
Files: []pkg.PythonFileRecord{
|
|
{
|
|
Path: "/some/path/pkg1/dependencies/foo",
|
|
},
|
|
},
|
|
},
|
|
PURL: "a-purl-2",
|
|
CPEs: []pkg.CPE{
|
|
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
|
},
|
|
})
|
|
catalog.Add(pkg.Package{
|
|
Name: "package-2",
|
|
Version: "2.0.1",
|
|
Type: pkg.DebPkg,
|
|
FoundBy: "the-cataloger-2",
|
|
Locations: []source.Location{
|
|
source.NewLocation("/some/path/pkg1"),
|
|
},
|
|
MetadataType: pkg.DpkgMetadataType,
|
|
Metadata: pkg.DpkgMetadata{
|
|
Package: "package-2",
|
|
Version: "2.0.1",
|
|
},
|
|
PURL: "a-purl-2",
|
|
CPEs: []pkg.CPE{
|
|
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
|
},
|
|
})
|
|
|
|
return catalog
|
|
}
|