mirror of
https://github.com/anchore/grype
synced 2024-11-10 14:44:12 +00:00
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:
parent
7545e8858d
commit
64e9c9c0d3
22 changed files with 318 additions and 233 deletions
4
go.mod
4
go.mod
|
@ -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
8
go.sum
|
@ -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=
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
|
|
|
@ -6,6 +6,6 @@ import (
|
|||
)
|
||||
|
||||
type Context struct {
|
||||
Source *source.Metadata
|
||||
Source *source.Description
|
||||
Distro *linux.Release
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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{
|
|
@ -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"},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue