Port to new syft source API (#1376)

* port to new syft source API

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

* fix linting

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 2023-07-06 09:01:49 -04:00 committed by GitHub
parent 7545e8858d
commit 64e9c9c0d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 318 additions and 233 deletions

4
go.mod
View file

@ -53,7 +53,7 @@ require (
require (
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963
github.com/anchore/syft v0.84.1
github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
github.com/mitchellh/mapstructure v1.5.0
)
@ -170,7 +170,7 @@ require (
go.uber.org/goleak v1.2.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect

8
go.sum
View file

@ -243,8 +243,8 @@ github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963 h1:vrf2PYH77vqVJo
github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963/go.mod h1:AVRyXOUP0hTz9Cb8OlD1XnwA8t4lBPfTuwPHmEUuiLc=
github.com/anchore/stereoscope v0.0.0-20230627195312-cd49355d934e h1:zhk3ZLtomMJ750nNCE+c24PonMzoO/SeL/4uTr1L9kM=
github.com/anchore/stereoscope v0.0.0-20230627195312-cd49355d934e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
github.com/anchore/syft v0.84.1 h1:O6V1gCSHTVbyfQq6M1qB86ui64qobZRC3h7lvKpVNWw=
github.com/anchore/syft v0.84.1/go.mod h1:dozEWcwhRawdB3ArPM2BGfZWLslZ+bDNwW+wWUwKySY=
github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7 h1:E8pdc689HTwXaHLRcmMTGi6TBukDa6oD8dQ0bJTSUm0=
github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7/go.mod h1:4ruIUJNJY2IsuUPrvUdYu8kG4ScFjGoiy/PPmgBEuTw=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@ -942,8 +942,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

View file

@ -227,7 +227,7 @@ func Test_NewDistroFromRelease_Coverage(t *testing.T) {
for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) {
s, err := source.NewFromDirectory(test.fixture)
s, err := source.NewFromDirectory(source.DirectoryConfig{Path: test.fixture})
require.NoError(t, err)
resolver, err := s.FileResolver(source.SquashedScope)

View file

@ -9,8 +9,8 @@ import (
grypeDb "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/file"
syftPkg "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
var (
@ -28,7 +28,7 @@ var (
Name: "dive",
Version: "0.5.2",
Type: "deb",
Locations: source.NewLocationSet(source.NewLocation("/path/that/has/dive")),
Locations: file.NewLocationSet(file.NewLocation("/path/that/has/dive")),
},
},
{
@ -45,7 +45,7 @@ var (
Version: "100.0.50",
Language: syftPkg.Ruby,
Type: syftPkg.GemPkg,
Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/reach",
Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/reach",
"/virtual/path/that/has/reach")),
},
},
@ -63,7 +63,7 @@ var (
Version: "100.0.51",
Language: syftPkg.Ruby,
Type: syftPkg.GemPkg,
Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/beach",
Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/beach",
"/virtual/path/that/has/beach")),
},
},
@ -81,7 +81,7 @@ var (
Version: "100.0.52",
Language: syftPkg.Ruby,
Type: syftPkg.GemPkg,
Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/speach",
Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/speach",
"/virtual/path/that/has/speach")),
},
},
@ -337,9 +337,9 @@ var (
ID: pkg.ID(uuid.NewString()),
Name: "a-pkg",
Version: "1.0",
Locations: source.NewLocationSet(
source.NewLocation("/some/path"),
source.NewVirtualLocation("/some/path", "/some/virtual/path"),
Locations: file.NewLocationSet(
file.NewLocation("/some/path"),
file.NewVirtualLocation("/some/path", "/some/virtual/path"),
),
Type: "rpm",
},

View file

@ -6,6 +6,6 @@ import (
)
type Context struct {
Source *source.Metadata
Source *source.Description
Distro *linux.Release
}

View file

@ -14,7 +14,6 @@ import (
"github.com/anchore/syft/syft/file"
syftFile "github.com/anchore/syft/syft/file"
syftPkg "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func TestNew(t *testing.T) {
@ -550,8 +549,8 @@ func TestFromCollection_DoesNotPanic(t *testing.T) {
examplePackage := syftPkg.Package{
Name: "test",
Version: "1.2.3",
Locations: source.NewLocationSet(
source.NewLocation("/test-path"),
Locations: file.NewLocationSet(
file.NewLocation("/test-path"),
),
Type: syftPkg.NpmPkg,
}

View file

@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/anchore/syft/syft/source"
)
func TestProviderLocationExcludes(t *testing.T) {
@ -158,10 +158,10 @@ func Test_filterPackageExclusions(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
var packages []Package
for _, pkg := range test.locations {
locations := source.NewLocationSet()
locations := file.NewLocationSet()
for _, l := range pkg {
locations.Add(
source.NewVirtualLocation(l, l),
file.NewVirtualLocation(l, l),
)
}
packages = append(packages, Package{Locations: locations})
@ -221,7 +221,7 @@ func Test_matchesLocation(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
matches, err := locationMatches(source.NewVirtualLocation(test.realPath, test.virtualPath), test.match)
matches, err := locationMatches(file.NewVirtualLocation(test.realPath, test.virtualPath), test.match)
assert.NoError(t, err)
assert.Equal(t, test.expected, matches)
})

View file

@ -1,26 +1,19 @@
package pkg
import (
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) {
if config.CatalogingOptions.Search.Scope == "" {
return nil, Context{}, nil, errDoesNotProvide
}
sourceInput, err := source.ParseInputWithName(userInput, config.Platform, config.Name, config.DefaultImagePullSource)
src, err := getSource(userInput, config)
if err != nil {
return nil, Context{}, nil, err
}
src, cleanup, err := source.New(*sourceInput, config.RegistryOptions, config.Exclusions)
if err != nil {
return nil, Context{}, nil, err
}
defer cleanup()
defer src.Close()
catalog, relationships, theDistro, err := syft.CatalogPackages(src, config.CatalogingOptions)
if err != nil {
@ -29,14 +22,16 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context,
catalog = removePackagesByOverlap(catalog, relationships)
srcDescription := src.Describe()
packages := FromCollection(catalog, config.SynthesisConfig)
context := Context{
Source: &src.Metadata,
Source: &srcDescription,
Distro: theDistro,
}
sbom := &sbom.SBOM{
Source: src.Metadata,
Source: srcDescription,
Relationships: relationships,
Artifacts: sbom.Artifacts{
Packages: catalog,
@ -45,3 +40,35 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context,
return packages, context, sbom, nil
}
func getSource(userInput string, config ProviderConfig) (source.Source, error) {
if config.CatalogingOptions.Search.Scope == "" {
return nil, errDoesNotProvide
}
detection, err := source.Detect(userInput, source.DetectConfig{
DefaultImageSource: config.DefaultImagePullSource,
})
if err != nil {
return nil, err
}
var platform *image.Platform
if config.Platform != "" {
platform, err = image.NewPlatform(config.Platform)
if err != nil {
return nil, err
}
}
return detection.NewSource(source.DetectionSourceConfig{
Alias: source.Alias{
Name: config.Name,
},
RegistryOptions: config.RegistryOptions,
Platform: platform,
Exclude: source.ExcludeConfig{
Paths: config.Exclusions,
},
})
}

View file

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/source"
)
@ -26,8 +27,8 @@ func TestParseSyftJSON(t *testing.T) {
{
Name: "alpine-baselayout",
Version: "3.2.0-r6",
Locations: source.NewLocationSet(
source.NewLocationFromCoordinates(source.Coordinates{
Locations: file.NewLocationSet(
file.NewLocationFromCoordinates(file.Coordinates{
RealPath: "/lib/apk/db/installed",
FileSystemID: "sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759",
}),
@ -50,8 +51,8 @@ func TestParseSyftJSON(t *testing.T) {
{
Name: "fake",
Version: "1.2.0",
Locations: source.NewLocationSet(
source.NewLocationFromCoordinates(source.Coordinates{
Locations: file.NewLocationSet(
file.NewLocationFromCoordinates(file.Coordinates{
RealPath: "/lib/apk/db/installed",
FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c",
}),
@ -76,8 +77,8 @@ func TestParseSyftJSON(t *testing.T) {
{
Name: "gmp",
Version: "6.2.0-r0",
Locations: source.NewLocationSet(
source.NewLocationFromCoordinates(source.Coordinates{
Locations: file.NewLocationSet(
file.NewLocationFromCoordinates(file.Coordinates{
RealPath: "/lib/apk/db/installed",
FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c",
}),
@ -101,11 +102,10 @@ func TestParseSyftJSON(t *testing.T) {
},
},
Context: Context{
Source: &source.Metadata{
Scheme: source.ImageScheme,
ImageMetadata: source.ImageMetadata{
Source: &source.Description{
Metadata: source.StereoscopeImageSourceMetadata{
UserInput: "alpine:fake",
Layers: []source.LayerMetadata{
Layers: []source.StereoscopeLayerMetadata{
{
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Digest: "sha256:50644c29ef5a27c9a40c393a73ece2479de78325cae7d762ef3cdc19bf42dd0a",
@ -120,7 +120,6 @@ func TestParseSyftJSON(t *testing.T) {
"alpine:fake",
},
},
Path: "",
},
Distro: &linux.Release{
Name: "alpine",
@ -138,8 +137,12 @@ func TestParseSyftJSON(t *testing.T) {
t.Fatalf("unable to parse: %+v", err)
}
context.Source.ImageMetadata.RawConfig = nil
context.Source.ImageMetadata.RawManifest = nil
if m, ok := context.Source.Metadata.(source.StereoscopeImageSourceMetadata); ok {
m.RawConfig = nil
m.RawManifest = nil
context.Source.Metadata = m
}
for _, d := range deep.Equal(test.Packages, pkgs) {
if strings.Contains(d, ".ID: ") {
@ -179,8 +182,8 @@ var springImageTestCase = struct {
{
Name: "charsets",
Version: "",
Locations: source.NewLocationSet(
source.NewLocationFromCoordinates(source.Coordinates{
Locations: file.NewLocationSet(
file.NewLocationFromCoordinates(file.Coordinates{
RealPath: "/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar",
FileSystemID: "sha256:a1a6ceadb701ab4e6c93b243dc2a0daedc8cee23a24203845ecccd5784cd1393",
}),
@ -199,8 +202,8 @@ var springImageTestCase = struct {
{
Name: "tomcat-embed-el",
Version: "9.0.27",
Locations: source.NewLocationSet(
source.NewLocationFromCoordinates(source.Coordinates{
Locations: file.NewLocationSet(
file.NewLocationFromCoordinates(file.Coordinates{
RealPath: "/app/libs/tomcat-embed-el-9.0.27.jar",
FileSystemID: "sha256:89504f083d3f15322f97ae240df44650203f24427860db1b3d32e66dd05940e4",
}),
@ -218,11 +221,10 @@ var springImageTestCase = struct {
},
},
Context: Context{
Source: &source.Metadata{
Scheme: source.ImageScheme,
ImageMetadata: source.ImageMetadata{
Source: &source.Description{
Metadata: source.StereoscopeImageSourceMetadata{
UserInput: "springio/gs-spring-boot-docker:latest",
Layers: []source.LayerMetadata{
Layers: []source.StereoscopeLayerMetadata{
{
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Digest: "sha256:42a3027eaac150d2b8f516100921f4bd83b3dbc20bfe64124f686c072b49c602",
@ -238,7 +240,6 @@ var springImageTestCase = struct {
},
RepoDigests: []string{"springio/gs-spring-boot-docker@sha256:39c2ffc784f5f34862e22c1f2ccdbcb62430736114c13f60111eabdb79decb08"},
},
Path: "",
},
Distro: &linux.Release{
Name: "debian",

View file

@ -20,7 +20,7 @@ import (
type Presenter struct {
results match.Matches
packages []pkg.Package
srcMetadata *source.Metadata
src *source.Description
metadataProvider vulnerability.MetadataProvider
format cyclonedx.BOMFileFormat
sbom *sbom.SBOM
@ -32,7 +32,7 @@ func NewJSONPresenter(pb models.PresenterConfig) *Presenter {
results: pb.Matches,
packages: pb.Packages,
metadataProvider: pb.MetadataProvider,
srcMetadata: pb.Context.Source,
src: pb.Context.Source,
sbom: pb.SBOM,
format: cyclonedx.BOMFileFormatJSON,
}
@ -44,7 +44,7 @@ func NewXMLPresenter(pb models.PresenterConfig) *Presenter {
results: pb.Matches,
packages: pb.Packages,
metadataProvider: pb.MetadataProvider,
srcMetadata: pb.Context.Source,
src: pb.Context.Source,
sbom: pb.SBOM,
format: cyclonedx.BOMFileFormatXML,
}

View file

@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/go-testutils"
"github.com/anchore/grype/grype/presenter/internal"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/syft/syft/source"
)
var update = flag.Bool("update", false, "update the *.golden files for cyclonedx presenters")
@ -17,8 +17,8 @@ var update = flag.Bool("update", false, "update the *.golden files for cyclonedx
func TestCycloneDxPresenterImage(t *testing.T) {
var buffer bytes.Buffer
matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme)
sbom := models.SBOMFromPackages(t, packages)
matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource)
sbom := internal.SBOMFromPackages(t, packages)
pb := models.PresenterConfig{
Matches: matches,
Packages: packages,
@ -42,16 +42,16 @@ func TestCycloneDxPresenterImage(t *testing.T) {
var expected = testutils.GetGoldenFileContents(t)
// remove dynamic values, which are tested independently
actual = models.Redact(actual)
expected = models.Redact(expected)
actual = internal.Redact(actual)
expected = internal.Redact(expected)
require.JSONEq(t, string(expected), string(actual))
}
func TestCycloneDxPresenterDir(t *testing.T) {
var buffer bytes.Buffer
matches, packages, ctx, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme)
sbom := models.SBOMFromPackages(t, packages)
matches, packages, ctx, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource)
sbom := internal.SBOMFromPackages(t, packages)
pb := models.PresenterConfig{
Matches: matches,
Packages: packages,
@ -76,8 +76,8 @@ func TestCycloneDxPresenterDir(t *testing.T) {
var expected = testutils.GetGoldenFileContents(t)
// remove dynamic values, which are tested independently
actual = models.Redact(actual)
expected = models.Redact(expected)
actual = internal.Redact(actual)
expected = internal.Redact(expected)
require.JSONEq(t, string(expected), string(actual))
}

View file

@ -1,4 +1,4 @@
package models
package internal
import (
"regexp"
@ -9,6 +9,7 @@ import (
grypeDb "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/syft/artifact"
@ -20,17 +21,25 @@ import (
syftSource "github.com/anchore/syft/syft/source"
)
func GenerateAnalysis(t *testing.T, scheme syftSource.Scheme) (match.Matches, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) {
const (
DirectorySource SyftSource = "directory"
ImageSource SyftSource = "image"
FileSource SyftSource = "file"
)
type SyftSource string
func GenerateAnalysis(t *testing.T, scheme SyftSource) (match.Matches, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) {
t.Helper()
packages := generatePackages(t)
matches := generateMatches(t, packages[0], packages[1])
context := generateContext(t, scheme)
return matches, packages, context, NewMetadataMock(), nil, nil
return matches, packages, context, models.NewMetadataMock(), nil, nil
}
func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme syftSource.Scheme) (match.Matches, []match.IgnoredMatch, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) {
func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme SyftSource) (match.Matches, []match.IgnoredMatch, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) {
t.Helper()
packages := generatePackages(t)
@ -38,7 +47,7 @@ func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme syftSource.Scheme)
ignoredMatches := generateIgnoredMatches(t, packages[1])
context := generateContext(t, scheme)
return matches, ignoredMatches, packages, context, NewMetadataMock(), nil, nil
return matches, ignoredMatches, packages, context, models.NewMetadataMock(), nil, nil
}
func SBOMFromPackages(t *testing.T, packages []pkg.Package) *sbom.SBOM {
@ -260,59 +269,84 @@ func generatePackages(t *testing.T) []pkg.Package {
return updatedPkgs
}
func generateContext(t *testing.T, scheme syftSource.Scheme) pkg.Context {
var src syftSource.Source
img := image.Image{
Metadata: image.Metadata{
ID: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f",
ManifestDigest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5",
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Size: 65,
},
Layers: []*image.Layer{
{
Metadata: image.LayerMetadata{
Digest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 22,
},
},
{
Metadata: image.LayerMetadata{
Digest: "sha256:a05cd9ebf88af96450f1e25367281ab232ac0645f314124fe01af759b93f3006",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 16,
},
},
{
Metadata: image.LayerMetadata{
Digest: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 27,
},
},
},
}
//nolint:funlen
func generateContext(t *testing.T, scheme SyftSource) pkg.Context {
var (
src syftSource.Source
desc syftSource.Description
)
switch scheme {
case syftSource.ImageScheme:
case FileSource:
var err error
src, err = syftSource.NewFromImage(&img, "user-input")
src, err = syftSource.NewFromFile(syftSource.FileConfig{
Path: "user-input",
})
if err != nil {
t.Fatalf("failed to generate mock file source from mock image: %+v", err)
}
desc = src.Describe()
case ImageSource:
img := image.Image{
Metadata: image.Metadata{
ID: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f",
ManifestDigest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5",
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Size: 65,
},
Layers: []*image.Layer{
{
Metadata: image.LayerMetadata{
Digest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 22,
},
},
{
Metadata: image.LayerMetadata{
Digest: "sha256:a05cd9ebf88af96450f1e25367281ab232ac0645f314124fe01af759b93f3006",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 16,
},
},
{
Metadata: image.LayerMetadata{
Digest: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f",
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Size: 27,
},
},
},
}
var err error
src, err = syftSource.NewFromStereoscopeImageObject(&img, "user-input", nil)
if err != nil {
t.Fatalf("failed to generate mock image source from mock image: %+v", err)
}
case syftSource.DirectoryScheme:
desc = src.Describe()
case DirectorySource:
// note: the dir must exist for the source to be created
d := t.TempDir()
var err error
src, err = syftSource.NewFromDirectory("/some/path")
src, err = syftSource.NewFromDirectory(syftSource.DirectoryConfig{
Path: d,
})
if err != nil {
t.Fatalf("failed to generate mock directory source from mock dir: %+v", err)
}
desc = src.Describe()
if m, ok := desc.Metadata.(syftSource.DirectorySourceMetadata); ok {
m.Path = "/some/path"
desc.Metadata = m
}
default:
t.Fatalf("unknown scheme: %s", scheme)
}
return pkg.Context{
Source: &src.Metadata,
Source: &desc,
Distro: &linux.Release{
Name: "centos",
IDLike: []string{

View file

@ -11,6 +11,7 @@ import (
"github.com/anchore/go-testutils"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/internal"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/source"
@ -21,7 +22,7 @@ var timestampRegexp = regexp.MustCompile(`"timestamp":\s*"[^"]+"`)
func TestJsonImgsPresenter(t *testing.T) {
var buffer bytes.Buffer
matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme)
matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource)
pb := models.PresenterConfig{
Matches: matches,
@ -54,7 +55,7 @@ func TestJsonImgsPresenter(t *testing.T) {
func TestJsonDirsPresenter(t *testing.T) {
var buffer bytes.Buffer
matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme)
matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource)
pb := models.PresenterConfig{
Matches: matches,
@ -91,7 +92,7 @@ func TestEmptyJsonPresenter(t *testing.T) {
matches := match.NewMatches()
ctx := pkg.Context{
Source: &source.Metadata{},
Source: &source.Description{},
Distro: &linux.Release{
ID: "centos",
IDLike: []string{"rhel"},

View file

@ -72,9 +72,8 @@ func TestPackagesAreSorted(t *testing.T) {
packages := []pkg.Package{pkg1, pkg2}
ctx := pkg.Context{
Source: &syftSource.Metadata{
Scheme: syftSource.DirectoryScheme,
ImageMetadata: syftSource.ImageMetadata{},
Source: &syftSource.Description{
Metadata: syftSource.DirectorySourceMetadata{},
},
Distro: &linux.Release{
ID: "centos",

View file

@ -12,39 +12,38 @@ type source struct {
}
// newSource creates a new source object to be represented into JSON.
func newSource(src syftSource.Metadata) (source, error) {
switch src.Scheme {
case syftSource.ImageScheme:
metadata := src.ImageMetadata
func newSource(src syftSource.Description) (source, error) {
switch m := src.Metadata.(type) {
case syftSource.StereoscopeImageSourceMetadata:
// ensure that empty collections are not shown as null
if metadata.RepoDigests == nil {
metadata.RepoDigests = []string{}
if m.RepoDigests == nil {
m.RepoDigests = []string{}
}
if metadata.Tags == nil {
metadata.Tags = []string{}
if m.Tags == nil {
m.Tags = []string{}
}
return source{
Type: "image",
Target: metadata,
Target: m,
}, nil
case syftSource.DirectoryScheme:
case syftSource.DirectorySourceMetadata:
return source{
Type: "directory",
Target: src.Path,
Target: m.Path,
}, nil
case syftSource.FileScheme:
case syftSource.FileSourceMetadata:
return source{
Type: "file",
Target: src.Path,
Target: m.Path,
}, nil
case "":
case nil:
// we may be showing results from a input source that does not support source information
return source{
Type: "unknown",
Target: "unknown",
}, nil
default:
return source{}, fmt.Errorf("unsupported source: %q", src.Scheme)
return source{}, fmt.Errorf("unsupported source: %T", src.Metadata)
}
}

View file

@ -12,14 +12,13 @@ import (
func TestNewSource(t *testing.T) {
testCases := []struct {
name string
metadata syftSource.Metadata
metadata syftSource.Description
expected source
}{
{
name: "image",
metadata: syftSource.Metadata{
Scheme: syftSource.ImageScheme,
ImageMetadata: syftSource.ImageMetadata{
metadata: syftSource.Description{
Metadata: syftSource.StereoscopeImageSourceMetadata{
UserInput: "abc",
ID: "def",
ManifestDigest: "abcdef",
@ -28,7 +27,7 @@ func TestNewSource(t *testing.T) {
},
expected: source{
Type: "image",
Target: syftSource.ImageMetadata{
Target: syftSource.StereoscopeImageSourceMetadata{
UserInput: "abc",
ID: "def",
ManifestDigest: "abcdef",
@ -40,9 +39,10 @@ func TestNewSource(t *testing.T) {
},
{
name: "directory",
metadata: syftSource.Metadata{
Scheme: syftSource.DirectoryScheme,
Path: "/foo/bar",
metadata: syftSource.Description{
Metadata: syftSource.DirectorySourceMetadata{
Path: "/foo/bar",
},
},
expected: source{
Type: "directory",
@ -51,9 +51,10 @@ func TestNewSource(t *testing.T) {
},
{
name: "file",
metadata: syftSource.Metadata{
Scheme: syftSource.FileScheme,
Path: "/foo/bar/test.zip",
metadata: syftSource.Description{
Metadata: syftSource.FileSourceMetadata{
Path: "/foo/bar/test.zip",
},
},
expected: source{
Type: "file",
@ -62,18 +63,12 @@ func TestNewSource(t *testing.T) {
},
}
var testedSchemes []syftSource.Scheme
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
actual, err := newSource(testCase.metadata)
require.NoError(t, err)
assert.Equal(t, testCase.expected, actual)
testedSchemes = append(testedSchemes, testCase.metadata.Scheme)
})
}
// Ensure we have test coverage for all possible syftSource.Scheme values.
assert.ElementsMatchf(t, syftSource.AllSchemes, testedSchemes, "not all scheme values are being tested")
}

View file

@ -22,7 +22,7 @@ import (
type Presenter struct {
results match.Matches
packages []pkg.Package
srcMetadata *source.Metadata
src *source.Description
metadataProvider vulnerability.MetadataProvider
}
@ -32,7 +32,7 @@ func NewPresenter(pb models.PresenterConfig) *Presenter {
results: pb.Matches,
packages: pb.Packages,
metadataProvider: pb.MetadataProvider,
srcMetadata: pb.Context.Source,
src: pb.Context.Source,
}
}
@ -163,10 +163,19 @@ func (pres *Presenter) packagePath(p pkg.Package) string {
// inputPath returns a friendlier relative path or absolute path depending on the input, not prefixed by . or ./
func (pres *Presenter) inputPath() string {
if pres.srcMetadata == nil {
if pres.src == nil {
return ""
}
inputPath := strings.TrimPrefix(pres.srcMetadata.Path, "./")
var inputPath string
switch m := pres.src.Metadata.(type) {
case source.FileSourceMetadata:
inputPath = m.Path
case source.DirectorySourceMetadata:
inputPath = m.Path
default:
return ""
}
inputPath = strings.TrimPrefix(inputPath, "./")
if inputPath == "." {
return ""
}
@ -182,13 +191,17 @@ func (pres *Presenter) locationPath(l file.Location) string {
in := pres.inputPath()
path = strings.TrimPrefix(path, "./")
// trimmed off any ./ and accounted for dir:. for both path and input path
if pres.srcMetadata != nil && pres.srcMetadata.Scheme == source.DirectoryScheme {
if filepath.IsAbs(path) || in == "" {
return path
if pres.src != nil {
_, ok := pres.src.Metadata.(source.DirectorySourceMetadata)
if ok {
if filepath.IsAbs(path) || in == "" {
return path
}
// return a path relative to the cwd, if it's not absolute
return fmt.Sprintf("%s/%s", in, path)
}
// return a path relative to the cwd, if it's not absolute
return fmt.Sprintf("%s/%s", in, path)
}
return path
}
@ -198,9 +211,9 @@ func (pres *Presenter) locations(m match.Match) []*sarif.Location {
var logicalLocations []*sarif.LogicalLocation
switch pres.srcMetadata.Scheme {
case source.ImageScheme:
img := pres.srcMetadata.ImageMetadata.UserInput
switch metadata := pres.src.Metadata.(type) {
case source.StereoscopeImageSourceMetadata:
img := metadata.UserInput
locations := m.Package.Locations.ToSlice()
for _, l := range locations {
trimmedPath := strings.TrimPrefix(pres.locationPath(l), "/")
@ -215,15 +228,15 @@ func (pres *Presenter) locations(m match.Match) []*sarif.Location {
// TODO we could add configuration to specify the prefix, a user might want to specify an image name and architecture
// in the case of multiple vuln scans, for example
physicalLocation = fmt.Sprintf("image/%s", physicalLocation)
case source.FileScheme:
case source.FileSourceMetadata:
locations := m.Package.Locations.ToSlice()
for _, l := range locations {
logicalLocations = append(logicalLocations, &sarif.LogicalLocation{
FullyQualifiedName: sp(fmt.Sprintf("%s:/%s", pres.srcMetadata.Path, pres.locationPath(l))),
FullyQualifiedName: sp(fmt.Sprintf("%s:/%s", metadata.Path, pres.locationPath(l))),
Name: sp(l.RealPath),
})
}
case source.DirectoryScheme:
case source.DirectorySourceMetadata:
// DirectoryScheme is already handled, with input prepended if needed
}
@ -399,7 +412,7 @@ func (pres *Presenter) resultMessage(m match.Match) sarif.Message {
path := pres.packagePath(m.Package)
message := fmt.Sprintf("The path %s reports %s at version %s ", path, m.Package.Name, m.Package.Version)
if pres.srcMetadata.Scheme == source.DirectoryScheme {
if _, ok := pres.src.Metadata.(source.DirectorySourceMetadata); ok {
message = fmt.Sprintf("%s which would result in a vulnerable (%s) package installed", message, m.Package.Type)
} else {
message = fmt.Sprintf("%s which is a vulnerable (%s) package installed in the container", message, m.Package.Type)

View file

@ -10,8 +10,10 @@ import (
"github.com/anchore/go-testutils"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/internal"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/source"
)
@ -20,15 +22,15 @@ var update = flag.Bool("update", false, "update .golden files for sarif presente
func TestSarifPresenter(t *testing.T) {
tests := []struct {
name string
scheme source.Scheme
scheme internal.SyftSource
}{
{
name: "directory",
scheme: source.DirectoryScheme,
scheme: internal.DirectorySource,
},
{
name: "image",
scheme: source.ImageScheme,
scheme: internal.ImageSource,
},
}
@ -36,7 +38,7 @@ func TestSarifPresenter(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var buffer bytes.Buffer
matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, tc.scheme)
matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, tc.scheme)
pb := models.PresenterConfig{
Matches: matches,
@ -57,8 +59,8 @@ func TestSarifPresenter(t *testing.T) {
}
var expected = testutils.GetGoldenFileContents(t)
actual = models.Redact(actual)
expected = models.Redact(expected)
actual = internal.Redact(actual)
expected = internal.Redact(expected)
if !bytes.Equal(expected, actual) {
assert.JSONEq(t, string(expected), string(actual))
@ -70,83 +72,92 @@ func TestSarifPresenter(t *testing.T) {
func Test_locationPath(t *testing.T) {
tests := []struct {
name string
path string
scheme source.Scheme
metadata any
real string
virtual string
expected string
}{
{
name: "dir:.",
scheme: source.DirectoryScheme,
path: ".",
name: "dir:.",
metadata: source.DirectorySourceMetadata{
Path: ".",
},
real: "/home/usr/file",
virtual: "file",
expected: "file",
},
{
name: "dir:./",
scheme: source.DirectoryScheme,
path: "./",
name: "dir:./",
metadata: source.DirectorySourceMetadata{
Path: "./",
},
real: "/home/usr/file",
virtual: "file",
expected: "file",
},
{
name: "dir:./someplace",
scheme: source.DirectoryScheme,
path: "./someplace",
name: "dir:./someplace",
metadata: source.DirectorySourceMetadata{
Path: "./someplace",
},
real: "/home/usr/file",
virtual: "file",
expected: "someplace/file",
},
{
name: "dir:/someplace",
scheme: source.DirectoryScheme,
path: "/someplace",
name: "dir:/someplace",
metadata: source.DirectorySourceMetadata{
Path: "/someplace",
},
real: "file",
expected: "/someplace/file",
},
{
name: "dir:/someplace symlink",
scheme: source.DirectoryScheme,
path: "/someplace",
name: "dir:/someplace symlink",
metadata: source.DirectorySourceMetadata{
Path: "/someplace",
},
real: "/someplace/usr/file",
virtual: "file",
expected: "/someplace/file",
},
{
name: "dir:/someplace absolute",
scheme: source.DirectoryScheme,
path: "/someplace",
name: "dir:/someplace absolute",
metadata: source.DirectorySourceMetadata{
Path: "/someplace",
},
real: "/usr/file",
expected: "/usr/file",
},
{
name: "file:/someplace/file",
scheme: source.FileScheme,
path: "/someplace/file",
name: "file:/someplace/file",
metadata: source.FileSourceMetadata{
Path: "/someplace/file",
},
real: "/usr/file",
expected: "/usr/file",
},
{
name: "file:/someplace/file relative",
scheme: source.FileScheme,
path: "/someplace/file",
name: "file:/someplace/file relative",
metadata: source.FileSourceMetadata{
Path: "/someplace/file",
},
real: "file",
expected: "file",
},
{
name: "image",
scheme: source.ImageScheme,
path: "alpine:latest",
name: "image",
metadata: source.StereoscopeImageSourceMetadata{
UserInput: "alpine:latest",
},
real: "/etc/file",
expected: "/etc/file",
},
{
name: "image symlink",
scheme: source.ImageScheme,
path: "alpine:latest",
name: "image symlink",
metadata: source.StereoscopeImageSourceMetadata{
UserInput: "alpine:latest",
},
real: "/etc/elsewhere/file",
virtual: "/etc/file",
expected: "/etc/file",
@ -155,15 +166,14 @@ func Test_locationPath(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pres := createDirPresenter(t, test.path)
pres.srcMetadata = &source.Metadata{
Scheme: test.scheme,
Path: test.path,
pres := createDirPresenter(t)
pres.src = &source.Description{
Metadata: test.metadata,
}
path := pres.packagePath(pkg.Package{
Locations: source.NewLocationSet(
source.NewVirtualLocation(test.real, test.virtual),
Locations: file.NewLocationSet(
file.NewVirtualLocation(test.real, test.virtual),
),
})
@ -172,19 +182,21 @@ func Test_locationPath(t *testing.T) {
}
}
func createDirPresenter(t *testing.T, path string) *Presenter {
matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme)
s, err := source.NewFromDirectory(path)
func createDirPresenter(t *testing.T) *Presenter {
matches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource)
d := t.TempDir()
s, err := source.NewFromDirectory(source.DirectoryConfig{Path: d})
if err != nil {
t.Fatal(err)
}
desc := s.Describe()
pb := models.PresenterConfig{
Matches: matches,
Packages: packages,
MetadataProvider: metadataProvider,
Context: pkg.Context{
Source: &s.Metadata,
Source: &desc,
},
}
@ -196,12 +208,12 @@ func createDirPresenter(t *testing.T, path string) *Presenter {
func TestToSarifReport(t *testing.T) {
tt := []struct {
name string
scheme source.Scheme
scheme internal.SyftSource
locations map[string]string
}{
{
name: "directory",
scheme: source.DirectoryScheme,
scheme: internal.DirectorySource,
locations: map[string]string{
"CVE-1999-0001-package-1": "/some/path/somefile-1.txt",
"CVE-1999-0002-package-2": "/some/path/somefile-2.txt",
@ -209,7 +221,7 @@ func TestToSarifReport(t *testing.T) {
},
{
name: "image",
scheme: source.ImageScheme,
scheme: internal.ImageSource,
locations: map[string]string{
"CVE-1999-0001-package-1": "image/somefile-1.txt",
"CVE-1999-0002-package-2": "image/somefile-2.txt",
@ -222,7 +234,7 @@ func TestToSarifReport(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, tc.scheme)
matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, tc.scheme)
pb := models.PresenterConfig{
Matches: matches,

View file

@ -12,10 +12,10 @@ import (
"github.com/anchore/go-testutils"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/presenter/internal"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/grype/grype/vulnerability"
syftPkg "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
var update = flag.Bool("update", false, "update the *.golden files for table presenters")
@ -76,7 +76,7 @@ func TestCreateRow(t *testing.T) {
func TestTablePresenter(t *testing.T) {
var buffer bytes.Buffer
matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme)
matches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource)
pb := models.PresenterConfig{
Matches: matches,
@ -174,7 +174,7 @@ func TestRemoveDuplicateRows(t *testing.T) {
func TestHidesIgnoredMatches(t *testing.T) {
var buffer bytes.Buffer
matches, ignoredMatches, packages, _, metadataProvider, _, _ := models.GenerateAnalysisWithIgnoredMatches(t, source.ImageScheme)
matches, ignoredMatches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource)
pb := models.PresenterConfig{
Matches: matches,
@ -205,7 +205,7 @@ func TestHidesIgnoredMatches(t *testing.T) {
func TestDisplaysIgnoredMatches(t *testing.T) {
var buffer bytes.Buffer
matches, ignoredMatches, packages, _, metadataProvider, _, _ := models.GenerateAnalysisWithIgnoredMatches(t, source.ImageScheme)
matches, ignoredMatches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource)
pb := models.PresenterConfig{
Matches: matches,

View file

@ -11,14 +11,14 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/go-testutils"
"github.com/anchore/grype/grype/presenter/internal"
"github.com/anchore/grype/grype/presenter/models"
"github.com/anchore/syft/syft/source"
)
var update = flag.Bool("update", false, "update the *.golden files for template presenters")
func TestPresenter_Present(t *testing.T) {
matches, packages, context, metadataProvider, appConfig, dbStatus := models.GenerateAnalysis(t, source.ImageScheme)
matches, packages, context, metadataProvider, appConfig, dbStatus := internal.GenerateAnalysis(t, internal.ImageSource)
workingDirectory, err := os.Getwd()
if err != nil {
@ -53,7 +53,7 @@ func TestPresenter_Present(t *testing.T) {
}
func TestPresenter_SprigDate_Fails(t *testing.T) {
matches, packages, context, metadataProvider, appConfig, dbStatus := models.GenerateAnalysis(t, source.ImageScheme)
matches, packages, context, metadataProvider, appConfig, dbStatus := internal.GenerateAnalysis(t, internal.ImageSource)
workingDirectory, err := os.Getwd()
require.NoError(t, err)

View file

@ -606,13 +606,15 @@ func TestMatchByImage(t *testing.T) {
userImage := "docker-archive:" + tarPath
sourceInput, err := source.ParseInput(userImage, "")
detection, err := source.Detect(userImage, source.DetectConfig{})
require.NoError(t, err)
// this is purely done to help setup mocks
theSource, cleanup, err := source.New(*sourceInput, nil, nil)
theSource, err := detection.NewSource(source.DetectionSourceConfig{})
require.NoError(t, err)
defer cleanup()
t.Cleanup(func() {
require.NoError(t, theSource.Close())
})
// TODO: relationships are not verified at this time
config := cataloger.DefaultConfig()
@ -645,7 +647,7 @@ func TestMatchByImage(t *testing.T) {
}
// build expected matches from what's discovered from the catalog
expectedMatches := test.expectedFn(*theSource, collection, theStore)
expectedMatches := test.expectedFn(theSource, collection, theStore)
assertMatches(t, expectedMatches.Sorted(), actualResults.Sorted())
})

View file

@ -11,6 +11,7 @@ import (
"testing"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/require"
"github.com/anchore/grype/grype/match"
"github.com/anchore/syft/syft"
@ -70,16 +71,18 @@ func saveImage(t testing.TB, imageName string, destPath string) {
}
func getSyftSBOM(t testing.TB, image string, format sbom.Format) string {
sourceInput, err := source.ParseInput(image, "")
detection, err := source.Detect(image, source.DetectConfig{})
if err != nil {
t.Fatalf("could not generate source input for packages command: %+v", err)
}
src, cleanup, err := source.New(*sourceInput, nil, nil)
src, err := detection.NewSource(source.DetectionSourceConfig{})
if err != nil {
t.Fatalf("can't get the source: %+v", err)
}
t.Cleanup(cleanup)
t.Cleanup(func() {
require.NoError(t, src.Close())
})
config := cataloger.DefaultConfig()
config.Search.Scope = source.SquashedScope
@ -91,7 +94,7 @@ func getSyftSBOM(t testing.TB, image string, format sbom.Format) string {
Packages: collection,
LinuxDistribution: distro,
},
Source: src.Metadata,
Source: src.Describe(),
}
bytes, err := syft.Encode(s, format)