Replace core SBOM-creation API with builder pattern (#1383)

* remove existing cataloging API

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add file cataloging config

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add package cataloging config

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add configs for cross-cutting concerns

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename CLI option configs to not require import aliases later

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update all nested structs for the Catalog struct

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update Catalog cli options

- add new cataloger selection options (selection and default)
- remove the excludeBinaryOverlapByOwnership
- deprecate "catalogers" flag
- add new javascript configuration

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* migrate relationship capabilities to separate internal package

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* refactor golang cataloger to use configuration options when creating packages

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* create internal object to facilitate reading from and writing to an SBOM

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* create a command-like object (task) to facilitate partial SBOM creation

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add cataloger selection capability

- be able to parse string expressions into a set of resolved actions against sets
- be able to use expressions to select/add/remove tasks to/from the final set of tasks to run

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add package, file, and environment related tasks

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update existing file catalogers to use nested UI elements

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add CreateSBOMConfig that drives the SBOM creation process

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* capture SBOM creation info as a struct

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add CreateSBOM() function

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update docs with SBOM selection help + breaking changes

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix multiple override default inputs

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix deprecation flag printing to stdout

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* refactor cataloger selection description to separate object

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* address review comments

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* keep expression errors and show specific suggestions only

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* address additional review feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* address more review comments

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* addressed additional PR review feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix file selection references

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* remove guess language data generation option

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add tests for coordinatesForSelection

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename relationship attributes

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add descriptions to relationships config fields

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* improve documentation around configuration options

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add explicit errors around legacy config entries

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2024-01-12 17:39:13 -05:00
committed by GitHub
parent 1bffc6ef0c
commit b0ab75fd89
126 changed files with 5740 additions and 1841 deletions
+42 -8
View File
@@ -162,7 +162,7 @@ func TestPackagesCmdFlags(t *testing.T) {
name: "all-layers-scope-flag-by-env",
args: []string{"scan", "-o", "json", hiddenPackagesImage},
env: map[string]string{
"SYFT_PACKAGE_CATALOGER_SCOPE": "all-layers",
"SYFT_SCOPE": "all-layers",
},
assertions: []traitAssertion{
assertPackageCount(163), // packages are now deduplicated for this case
@@ -234,14 +234,50 @@ func TestPackagesCmdFlags(t *testing.T) {
},
},
{
name: "catalogers-option",
// This will detect enable python-package-cataloger, python-installed-package-cataloger and ruby-gemspec cataloger
args: []string{"scan", "-o", "json", "--catalogers", "python,ruby-gemspec", coverageImage},
name: "legacy-catalogers-option",
// This will detect enable:
// - python-installed-package-cataloger
// - python-package-cataloger
// - ruby-gemspec-cataloger
// - ruby-installed-gemspec-cataloger
args: []string{"packages", "-o", "json", "--catalogers", "python,gemspec", coverageImage},
assertions: []traitAssertion{
assertInOutput("Flag --catalogers has been deprecated, use: override-default-catalogers and select-catalogers"),
assertPackageCount(13),
assertSuccessfulReturnCode,
},
},
{
name: "select-catalogers-option",
// This will detect enable:
// - python-installed-package-cataloger
// - ruby-installed-gemspec-cataloger
args: []string{"scan", "-o", "json", "--select-catalogers", "python,gemspec", coverageImage},
assertions: []traitAssertion{
assertPackageCount(6),
assertSuccessfulReturnCode,
},
},
{
name: "override-default-catalogers-option",
// This will detect enable:
// - python-installed-package-cataloger
// - python-package-cataloger
// - ruby-gemspec-cataloger
// - ruby-installed-gemspec-cataloger
args: []string{"packages", "-o", "json", "--override-default-catalogers", "python,gemspec", coverageImage},
assertions: []traitAssertion{
assertPackageCount(13),
assertSuccessfulReturnCode,
},
},
{
name: "new and old cataloger options are mutually exclusive",
args: []string{"packages", "-o", "json", "--override-default-catalogers", "python", "--catalogers", "gemspec", coverageImage},
assertions: []traitAssertion{
assertFailingReturnCode,
},
},
{
name: "override-default-parallelism",
args: []string{"scan", "-vvv", "-o", "json", coverageImage},
@@ -250,8 +286,7 @@ func TestPackagesCmdFlags(t *testing.T) {
},
assertions: []traitAssertion{
// the application config in the log matches that of what we expect to have been configured.
assertInOutput("parallelism: 2"),
assertInOutput("parallelism=2"),
assertInOutput(`parallelism: 2`),
assertPackageCount(coverageImageSquashedPackageCount),
assertSuccessfulReturnCode,
},
@@ -261,8 +296,7 @@ func TestPackagesCmdFlags(t *testing.T) {
args: []string{"scan", "-vvv", "-o", "json", coverageImage},
assertions: []traitAssertion{
// the application config in the log matches that of what we expect to have been configured.
assertInOutput("parallelism: 1"),
assertInOutput("parallelism=1"),
assertInOutput(`parallelism: 1`),
assertPackageCount(coverageImageSquashedPackageCount),
assertSuccessfulReturnCode,
},
@@ -8,8 +8,8 @@ import (
func Test_AllLayersIncludesSquashed(t *testing.T) {
// This is a verification test for issue #894 (https://github.com/anchore/syft/issues/894)
allLayers, _ := catalogFixtureImage(t, "image-suse-all-layers", source.AllLayersScope, nil)
squashed, _ := catalogFixtureImage(t, "image-suse-all-layers", source.SquashedScope, nil)
allLayers, _ := catalogFixtureImage(t, "image-suse-all-layers", source.AllLayersScope)
squashed, _ := catalogFixtureImage(t, "image-suse-all-layers", source.SquashedScope)
lenAllLayers := len(allLayers.Artifacts.Packages.Sorted())
lenSquashed := len(squashed.Artifacts.Packages.Sorted())
+19 -53
View File
@@ -1,6 +1,7 @@
package integration
import (
"context"
"strings"
"testing"
@@ -9,53 +10,39 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/anchore/syft/syft/source"
)
func BenchmarkImagePackageCatalogers(b *testing.B) {
// get the fixture image tar file
fixtureImageName := "image-pkg-coverage"
imagetest.GetFixtureImage(b, "docker-archive", fixtureImageName)
tarPath := imagetest.GetFixtureImageTarPath(b, fixtureImageName)
var pc *pkg.Collection
for _, c := range cataloger.ImageCatalogers(cataloger.DefaultConfig()) {
// in case of future alteration where state is persisted, assume no dependency is safe to reuse
userInput := "docker-archive:" + tarPath
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
require.NoError(b, err)
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
if err != nil {
b.Fatalf("unable to get source: %+v", err)
}
b.Cleanup(func() {
theSource.Close()
})
// get the source object for the image
userInput := "docker-archive:" + tarPath
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
require.NoError(b, err)
resolver, err := theSource.FileResolver(source.SquashedScope)
if err != nil {
b.Fatalf("unable to get resolver: %+v", err)
}
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
require.NoError(b, err)
theDistro := linux.IdentifyRelease(resolver)
b.Cleanup(func() {
theSource.Close()
})
b.Run(c.Name(), func(b *testing.B) {
for i := 0; i < b.N; i++ {
pc, _, err = cataloger.Catalog(resolver, theDistro, 1, c)
if err != nil {
b.Fatalf("failure during benchmark: %+v", err)
}
}
})
// build the SBOM
s, err := syft.CreateSBOM(context.Background(), theSource, syft.DefaultCreateSBOMConfig())
b.Logf("catalog for %q number of packages: %d", c.Name(), pc.PackageCount())
}
// did it work?
require.NoError(b, err)
require.NotNil(b, s)
}
func TestPkgCoverageImage(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
observedLanguages := strset.New()
definedLanguages := strset.New()
@@ -250,29 +237,8 @@ func TestPkgCoverageDirectory(t *testing.T) {
}
}
func TestPkgCoverageCatalogerConfiguration(t *testing.T) {
// Check that cataloger configuration can be used to run a cataloger on a source
// for which that cataloger isn't enabled by defauly
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, []string{"rust"})
observedLanguages := strset.New()
definedLanguages := strset.New()
definedLanguages.Add("rust")
for actualPkg := range sbom.Artifacts.Packages.Enumerate() {
observedLanguages.Add(actualPkg.Language.String())
}
assert.Equal(t, definedLanguages, observedLanguages)
// Verify that rust isn't actually an image cataloger
c := cataloger.DefaultConfig()
c.Catalogers = []string{"rust"}
assert.Len(t, cataloger.ImageCatalogers(c), 0)
}
func TestPkgCoverageImage_HasEvidence(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
var cases []testCase
cases = append(cases, commonTestCases...)
+1 -1
View File
@@ -59,7 +59,7 @@ func TestConvertCmd(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
syftSbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil)
syftSbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
syftFormat := syftjson.NewFormatEncoder()
syftFile, err := os.CreateTemp("", "test-convert-sbom-")
+1 -1
View File
@@ -10,7 +10,7 @@ import (
)
func TestDistroImage(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-distro-id", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-distro-id", source.SquashedScope)
expected := &linux.Release{
PrettyName: "BusyBox v1.31.1",
+9 -10
View File
@@ -15,7 +15,6 @@ import (
"github.com/anchore/syft/syft/format/cyclonedxjson"
"github.com/anchore/syft/syft/format/cyclonedxxml"
"github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
@@ -32,12 +31,12 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
"image-owning-package",
}
tests := []struct {
formatOption sbom.FormatID
redactor func(in []byte) []byte
json bool
name string
redactor func(in []byte) []byte
json bool
}{
{
formatOption: syftjson.ID,
name: syftjson.ID.String(),
redactor: func(in []byte) []byte {
// no redactions necessary
return in
@@ -45,7 +44,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
json: true,
},
{
formatOption: cyclonedxjson.ID,
name: cyclonedxjson.ID.String(),
redactor: func(in []byte) []byte {
// unstable values
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref|ref)":\s*"(\n|[^"])+"`).ReplaceAll(in, []byte(`"$1": "redacted"`))
@@ -55,7 +54,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
json: true,
},
{
formatOption: cyclonedxxml.ID,
name: cyclonedxxml.ID.String(),
redactor: func(in []byte) []byte {
// unstable values
in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{})
@@ -75,11 +74,11 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
decoders := format.NewDecoderCollection(format.Decoders()...)
for _, test := range tests {
t.Run(string(test.formatOption), func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
for _, image := range images {
originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope, nil)
originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope)
f := encoders.GetByString(string(test.formatOption))
f := encoders.GetByString(test.name)
require.NotNil(t, f)
var buff1 bytes.Buffer
+154
View File
@@ -0,0 +1,154 @@
package integration
import (
"crypto"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/clio"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/syft/cataloging/filecataloging"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/file/cataloger/filecontent"
"github.com/anchore/syft/syft/sbom"
)
func TestFileCataloging_Default(t *testing.T) {
cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{})
cfg = cfg.WithFilesConfig(filecataloging.DefaultConfig())
sbom, _ := catalogDirectoryWithConfig(t, "test-fixtures/files", cfg)
var metadata map[file.Coordinates]file.Metadata
var digests map[file.Coordinates][]file.Digest
var contents map[file.Coordinates]string
assertFileData(t, metadata, digests, contents, sbom)
}
func TestFileCataloging_AllFiles(t *testing.T) {
cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{})
cfg = cfg.WithFilesConfig(filecataloging.Config{
Selection: file.AllFilesSelection,
Hashers: []crypto.Hash{
crypto.SHA256,
},
Content: filecontent.Config{
// this is enough to potentially capture a/file, a-small-file, a-symlink-to-a-small-file, and a-symlink-to-file
// but the size of a/file will cause it to be filtered, and the symlinks will not be included since
// they are not regular files
Globs: []string{"**/*file"},
SkipFilesAboveSize: 30,
},
})
sbom, _ := catalogDirectoryWithConfig(t, "test-fixtures/files", cfg)
pwd, err := os.Getwd()
require.NoError(t, err)
testPath := func(path string) string {
return filepath.Join(pwd, "test-fixtures/files", path)
}
metadata := map[file.Coordinates]file.Metadata{
{RealPath: ""}: {
Path: testPath(""),
Type: stereoscopeFile.TypeDirectory,
},
{RealPath: "/somewhere"}: {
Path: testPath("/somewhere"),
Type: stereoscopeFile.TypeDirectory,
},
{RealPath: "/somewhere/there"}: {
Path: testPath("/somewhere/there"),
Type: stereoscopeFile.TypeDirectory,
},
{RealPath: "/somewhere/there/is"}: {
Path: testPath("/somewhere/there/is"),
Type: stereoscopeFile.TypeDirectory,
},
{RealPath: "/somewhere/there/is/a"}: {
Path: testPath("/somewhere/there/is/a"),
Type: stereoscopeFile.TypeDirectory,
},
{RealPath: "/somewhere/there/is/a-small-file"}: {
Path: testPath("/somewhere/there/is/a-small-file"),
Type: stereoscopeFile.TypeRegular,
MIMEType: "text/plain",
},
{RealPath: "/somewhere/there/is/a-symlink-to-a-small-file"}: {
Path: testPath("/somewhere/there/is/a-symlink-to-a-small-file"),
LinkDestination: testPath("/somewhere/there/is/a-small-file"),
Type: stereoscopeFile.TypeSymLink,
},
{RealPath: "/somewhere/there/is/a-symlink-to-file"}: {
Path: testPath("/somewhere/there/is/a-symlink-to-file"),
LinkDestination: testPath("/somewhere/there/is/a/file"),
Type: stereoscopeFile.TypeSymLink,
},
{RealPath: "/somewhere/there/is/a/file"}: {
Path: testPath("/somewhere/there/is/a/file"),
Type: stereoscopeFile.TypeRegular,
MIMEType: "text/plain",
},
}
digests := map[file.Coordinates][]file.Digest{
{RealPath: "/somewhere/there/is/a-small-file"}: {
file.Digest{Algorithm: "sha256", Value: "672c23470e4ce99cf270bb63ae66ad2b8a80aa19090c40e59fbb1229a4ab661a"},
},
{RealPath: "/somewhere/there/is/a/file"}: {
file.Digest{Algorithm: "sha256", Value: "00dac26d6d94353ac0d92bb9640cba76f82f5ca8707bb845ecdc574bd002348e"},
},
}
contents := map[file.Coordinates]string{
{RealPath: "/somewhere/there/is/a-small-file"}: "c29tZSBjb250ZW50cyE=",
}
assertFileData(t, metadata, digests, contents, sbom)
}
func assertFileData(t testing.TB, metadata map[file.Coordinates]file.Metadata, digests map[file.Coordinates][]file.Digest, contents map[file.Coordinates]string, sbom sbom.SBOM) {
metadataCompareOpts := cmp.Options{
cmp.Comparer(func(x, y file.Metadata) bool {
if x.Path != y.Path {
t.Logf("path mismatch: %s != %s", x.Path, y.Path)
return false
}
if x.Type != y.Type {
t.Logf("type mismatch: %s != %s", x.Type, y.Type)
return false
}
if x.LinkDestination != y.LinkDestination {
t.Logf("link destination mismatch: %s != %s", x.LinkDestination, y.LinkDestination)
return false
}
if x.MIMEType != y.MIMEType {
t.Logf("mime type mismatch: %s != %s", x.MIMEType, y.MIMEType)
return false
}
return true
}),
}
if d := cmp.Diff(metadata, sbom.Artifacts.FileMetadata, metadataCompareOpts...); d != "" {
t.Errorf("unexpected metadata (-want +got):\n%s", d)
}
assert.Equal(t, digests, sbom.Artifacts.FileDigests, "different digests detected")
assert.Equal(t, contents, sbom.Artifacts.FileContents, "different contents detected")
}
@@ -25,7 +25,7 @@ func TestGolangCompilerDetection(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, tt.image, source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, tt.image, source.SquashedScope)
packages := sbom.Artifacts.Packages.PackagesByName("stdlib")
foundCompilerVersions := make(map[string]struct{})
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
func TestJavaPURLs(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-test-java-purls", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-test-java-purls", source.SquashedScope)
found := make(map[string]string)
for _, p := range sbom.Artifacts.Packages.Sorted() {
if p.Type != pkg.JavaPkg && p.Type != pkg.JenkinsPluginPkg {
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func TestMarinerDistroless(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-mariner-distroless", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-mariner-distroless", source.SquashedScope)
expectedPkgs := 12
actualPkgs := 0
@@ -63,7 +63,7 @@ func TestPackageDeduplication(t *testing.T) {
for _, tt := range tests {
t.Run(string(tt.scope), func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope, nil)
sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope)
for _, p := range sbom.Artifacts.Packages.Sorted() {
if p.Type == pkg.BinaryPkg {
assert.NotEmpty(t, p.Name)
@@ -25,7 +25,7 @@ func TestPackageOwnershipRelationships(t *testing.T) {
for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope)
output := bytes.NewBufferString("")
err := syftjson.NewFormatEncoder().Encode(output, sbom)
@@ -60,7 +60,7 @@ func TestPackageOwnershipExclusions(t *testing.T) {
for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope)
binaryPackages := make([]pkg.Package, 0)
apkPackages := make([]pkg.Package, 0)
for p := range sbom.Artifacts.Packages.Enumerate() {
@@ -10,7 +10,7 @@ import (
func TestRegression212ApkBufferSize(t *testing.T) {
// This is a regression test for issue #212 (https://github.com/anchore/syft/issues/212) in which the apk db could
// not be processed due to a scanner buffer that was too small
sbom, _ := catalogFixtureImage(t, "image-large-apk-data", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-large-apk-data", source.SquashedScope)
expectedPkgs := 58
actualPkgs := 0
@@ -16,7 +16,7 @@ func TestRegressionGoArchDiscovery(t *testing.T) {
)
// This is a regression test to make sure the way we detect go binary packages
// stays consistent and reproducible as the tool chain evolves
sbom, _ := catalogFixtureImage(t, "image-go-bin-arch-coverage", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-go-bin-arch-coverage", source.SquashedScope)
var actualELF, actualWIN, actualMACOS int
@@ -7,5 +7,5 @@ import (
)
func TestRegressionJavaNoMainPackage(t *testing.T) { // Regression: https://github.com/anchore/syft/issues/252
catalogFixtureImage(t, "image-java-no-main-package", source.SquashedScope, nil)
catalogFixtureImage(t, "image-java-no-main-package", source.SquashedScope)
}
@@ -12,7 +12,7 @@ import (
func TestWarCatalogedCorrectlyIfRenamed(t *testing.T) {
// install hudson-war@2.2.1 and renames the file to `/hudson.war`
sbom, _ := catalogFixtureImage(t, "image-java-virtualpath-regression", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-java-virtualpath-regression", source.SquashedScope)
badPURL := "pkg:maven/hudson/hudson@2.2.1"
goodPURL := "pkg:maven/org.jvnet.hudson.main/hudson-war@2.2.1"
@@ -8,7 +8,7 @@ import (
)
func TestPhotonPackageRegression(t *testing.T) { // Regression: https://github.com/anchore/syft/pull/1997
sbom, _ := catalogFixtureImage(t, "image-photon-all-layers", source.AllLayersScope, nil)
sbom, _ := catalogFixtureImage(t, "image-photon-all-layers", source.AllLayersScope)
var packages []pkg.Package
for p := range sbom.Artifacts.Packages.Enumerate() {
packages = append(packages, p)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
func TestRustAudit(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-rust-auditable", source.SquashedScope, []string{"all"})
sbom, _ := catalogFixtureImage(t, "image-rust-auditable", source.SquashedScope)
expectedPkgs := 2
actualPkgs := 0
+4 -4
View File
@@ -11,16 +11,16 @@ func TestSbomCataloger(t *testing.T) {
// The image contains a go.mod file with 2 dependencies and an spdx json sbom.
// The go.mod file contains 2 dependencies, and the sbom includes a go dependency
// that overlaps with the go.mod
sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, []string{"all"})
sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, "+go-module-file-cataloger")
expectedSbomCatalogerPkgs := 1
expectedGoModCatalogerPkgs := 2
actualSbomPkgs := 0
actualGoModPkgs := 0
for pkg := range sbom.Artifacts.Packages.Enumerate(pkg.GoModulePkg) {
if pkg.FoundBy == "go-module-file-cataloger" {
for p := range sbom.Artifacts.Packages.Enumerate(pkg.GoModulePkg) {
if p.FoundBy == "go-module-file-cataloger" {
actualGoModPkgs += 1
} else if pkg.FoundBy == "sbom-cataloger" {
} else if p.FoundBy == "sbom-cataloger" {
actualSbomPkgs += 1
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func TestSqliteRpm(t *testing.T) {
// This is a regression test for issue #469 (https://github.com/anchore/syft/issues/469). Recent RPM
// based distribution store package data in an sqlite database
sbom, _ := catalogFixtureImage(t, "image-sqlite-rpmdb", source.SquashedScope, nil)
sbom, _ := catalogFixtureImage(t, "image-sqlite-rpmdb", source.SquashedScope)
expectedPkgs := 139
actualPkgs := 0
@@ -0,0 +1 @@
some contents!
@@ -0,0 +1 @@
./a-small-file
@@ -0,0 +1 @@
./a/file
@@ -0,0 +1,10 @@
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
with a lot of contents!
+52 -42
View File
@@ -1,81 +1,91 @@
package integration
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/clio"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Scope, catalogerCfg []string) (sbom.SBOM, source.Source) {
func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Scope, catalogerSelection ...string) (sbom.SBOM, source.Source) {
cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{
Name: "syft-tester",
Version: "v0.99.0",
}).WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
WithExpression(catalogerSelection...),
)
cfg.Search.Scope = scope
return catalogFixtureImageWithConfig(t, fixtureImageName, cfg)
}
func catalogFixtureImageWithConfig(t *testing.T, fixtureImageName string, cfg *syft.CreateSBOMConfig) (sbom.SBOM, source.Source) {
cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.ImageTag)
// get the fixture image tar file
imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName)
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
userInput := "docker-archive:" + tarPath
// get the source to build an SBOM against
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
require.NoError(t, err)
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
require.NoError(t, err)
t.Cleanup(func() {
theSource.Close()
})
c := cataloger.DefaultConfig()
c.Catalogers = catalogerCfg
s, err := syft.CreateSBOM(context.Background(), theSource, cfg)
c.Search.Scope = scope
pkgCatalog, relationships, actualDistro, err := syft.CatalogPackages(theSource, c)
if err != nil {
t.Fatalf("failed to catalog image: %+v", err)
}
require.NoError(t, err)
require.NotNil(t, s)
return sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkgCatalog,
LinuxDistribution: actualDistro,
},
Relationships: relationships,
Source: theSource.Describe(),
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",
},
},
}, theSource
return *s, theSource
}
func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, source.Source) {
func catalogDirectory(t *testing.T, dir string, catalogerSelection ...string) (sbom.SBOM, source.Source) {
cfg := options.DefaultCatalog().ToSBOMConfig(clio.Identification{
Name: "syft-tester",
Version: "v0.99.0",
}).WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
WithExpression(catalogerSelection...),
)
return catalogDirectoryWithConfig(t, dir, cfg)
}
func catalogDirectoryWithConfig(t *testing.T, dir string, cfg *syft.CreateSBOMConfig) (sbom.SBOM, source.Source) {
cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.DirectoryTag)
// get the source to build an sbom against
userInput := "dir:" + dir
detection, err := source.Detect(userInput, source.DefaultDetectConfig())
require.NoError(t, err)
theSource, err := detection.NewSource(source.DefaultDetectionSourceConfig())
require.NoError(t, err)
t.Cleanup(func() {
theSource.Close()
})
// TODO: this would be better with functional options (after/during API refactor)
c := cataloger.DefaultConfig()
c.Search.Scope = source.AllLayersScope
pkgCatalog, relationships, actualDistro, err := syft.CatalogPackages(theSource, c)
if err != nil {
t.Fatalf("failed to catalog image: %+v", err)
}
// build the SBOM
s, err := syft.CreateSBOM(context.Background(), theSource, cfg)
return sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkgCatalog,
LinuxDistribution: actualDistro,
},
Relationships: relationships,
Source: theSource.Describe(),
}, theSource
require.NoError(t, err)
require.NotNil(t, s)
return *s, theSource
}