From 25f1a9c2afbc080b208fa5ff2487b460c08c3b58 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 08:46:33 -0500 Subject: [PATCH 01/54] replace raw globs with index equivelent operations Signed-off-by: Alex Goodman --- go.mod | 3 +- go.sum | 8 +- syft/pkg/cataloger/alpm/cataloger.go | 4 +- syft/pkg/cataloger/apkdb/cataloger.go | 4 +- syft/pkg/cataloger/binary/cataloger.go | 3 +- syft/pkg/cataloger/binary/cataloger_test.go | 42 +++-- syft/pkg/cataloger/binary/classifier.go | 5 +- syft/pkg/cataloger/binary/classifier_test.go | 7 +- .../cataloger/binary/default_classifiers.go | 65 +++---- syft/pkg/cataloger/cpp/cataloger.go | 4 +- syft/pkg/cataloger/dart/cataloger.go | 2 +- syft/pkg/cataloger/deb/cataloger.go | 2 + syft/pkg/cataloger/dotnet/cataloger.go | 2 +- syft/pkg/cataloger/elixir/cataloger.go | 2 +- syft/pkg/cataloger/erlang/cataloger.go | 2 +- syft/pkg/cataloger/generic/cataloger.go | 61 +++++++ syft/pkg/cataloger/generic/search.go | 169 ++++++++++++++++++ syft/pkg/cataloger/golang/cataloger.go | 2 +- syft/pkg/cataloger/haskell/cataloger.go | 6 +- syft/pkg/cataloger/java/archive_parser.go | 27 ++- syft/pkg/cataloger/java/cataloger.go | 8 +- syft/pkg/cataloger/java/parse_pom_xml.go | 1 - .../java/tar_wrapped_archive_parser.go | 36 ++-- .../java/zip_wrapped_archive_parser.go | 4 +- syft/pkg/cataloger/javascript/cataloger.go | 8 +- syft/pkg/cataloger/php/cataloger.go | 4 +- syft/pkg/cataloger/portage/cataloger.go | 2 +- syft/pkg/cataloger/python/cataloger.go | 22 +-- .../python/parse_wheel_egg_metadata.go | 2 +- syft/pkg/cataloger/rpm/cataloger.go | 12 +- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 21 ++- syft/pkg/cataloger/ruby/catalogers.go | 6 +- syft/pkg/cataloger/rust/cataloger.go | 2 +- syft/pkg/cataloger/sbom/cataloger.go | 29 +-- syft/pkg/cataloger/swift/cataloger.go | 2 +- syft/source/directory_resolver.go | 61 +++++++ syft/source/excluding_file_resolver.go | 15 ++ syft/source/excluding_file_resolver_test.go | 38 ++-- syft/source/file_resolver.go | 8 +- ...solver.go => image_all_layers_resolver.go} | 82 +++++++-- ...t.go => image_all_layers_resolver_test.go} | 0 syft/source/image_squash_resolver.go | 42 +++++ syft/source/mock_resolver.go | 36 +++- 43 files changed, 690 insertions(+), 171 deletions(-) create mode 100644 syft/pkg/cataloger/generic/search.go rename syft/source/{all_layers_resolver.go => image_all_layers_resolver.go} (73%) rename syft/source/{all_layers_resolver_test.go => image_all_layers_resolver_test.go} (100%) diff --git a/go.mod b/go.mod index fe161eb352f..bfdfc3407fb 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20221208011002-c5ff155d72f1 + github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb github.com/docker/docker v20.10.23+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 @@ -69,6 +69,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/containerd/containerd v1.6.12 // indirect github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 3fc7eb923a5..ae048f5523a 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,10 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20221208011002-c5ff155d72f1 h1:DXUAm/H9chRTEzMfkFyduBIcCiJyFXhCmv3zH3C0HGs= -github.com/anchore/stereoscope v0.0.0-20221208011002-c5ff155d72f1/go.mod h1:/zjVnu2Jdl7xQCUtASegzeEg+IHKrM7SyMqdao3e+Nc= +github.com/anchore/stereoscope v0.0.0-20230124175104-043b34b511a5 h1:64XL7Pnji0vdWlfUizwdUtagSpndpJD2g1yUb70stWU= +github.com/anchore/stereoscope v0.0.0-20230124175104-043b34b511a5/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= +github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb h1:fZhCHUYNl/+rf44PhwgeiLYlC2eL7YMj/Zy9s2tM/iA= +github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= 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= @@ -171,6 +173,8 @@ github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHD github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04/go.mod h1:Z+bXnIbhKJYSvxNwsNnwde7pDKxuqlEZCbUBoTwAqf0= +github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= +github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/syft/pkg/cataloger/alpm/cataloger.go b/syft/pkg/cataloger/alpm/cataloger.go index 39bc7d816c0..a82b6601300 100644 --- a/syft/pkg/cataloger/alpm/cataloger.go +++ b/syft/pkg/cataloger/alpm/cataloger.go @@ -9,5 +9,7 @@ const catalogerName = "alpmdb-cataloger" func NewAlpmdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob) + WithParser(parseAlpmDB, + generic.NewSearch().ByBasename("desc").MustMatchGlob(pkg.AlpmDBGlob), + ) } diff --git a/syft/pkg/cataloger/apkdb/cataloger.go b/syft/pkg/cataloger/apkdb/cataloger.go index d3cea71263c..91b2e8a400a 100644 --- a/syft/pkg/cataloger/apkdb/cataloger.go +++ b/syft/pkg/cataloger/apkdb/cataloger.go @@ -13,5 +13,7 @@ const catalogerName = "apkdb-cataloger" // NewApkdbCataloger returns a new Alpine DB cataloger object. func NewApkdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseApkDB, pkg.ApkDBGlob) + WithParser(parseApkDB, + generic.NewSearch().ByBasename("installed").MustMatchGlob(pkg.ApkDBGlob), + ) } diff --git a/syft/pkg/cataloger/binary/cataloger.go b/syft/pkg/cataloger/binary/cataloger.go index a3f8b19e696..bfbfa8bee95 100644 --- a/syft/pkg/cataloger/binary/cataloger.go +++ b/syft/pkg/cataloger/binary/cataloger.go @@ -32,6 +32,7 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif var relationships []artifact.Relationship for _, cls := range defaultClassifiers { + log.WithFields("classifier", cls.Class, "search", cls.SearchRequest.String()).Trace("cataloging binaries") pkgs, err := catalog(resolver, cls) if err != nil { log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err) @@ -45,7 +46,7 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error) { var pkgs []pkg.Package - locations, err := resolver.FilesByGlob(cls.FileGlob) + locations, err := cls.SearchRequest.Execute(resolver) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index c3af3bfcf67..8cbdded3e21 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -13,7 +13,7 @@ import ( "github.com/anchore/syft/syft/source" ) -func TestClassifierCataloger_DefaultClassifiers_PositiveCases(t *testing.T) { +func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { tests := []struct { name string fixtureDir string @@ -356,7 +356,7 @@ func TestClassifierCataloger_DefaultClassifiers_PositiveCases(t *testing.T) { } } -func TestClassifierCataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { +func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { tests := []struct { name string fixtureImage string @@ -437,39 +437,57 @@ func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) { } type panicyResolver struct { - globCalled bool + searchCalled bool } -func (p panicyResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { +func (p *panicyResolver) FilesByExtension(extension string) ([]source.Location, error) { + p.searchCalled = true return nil, errors.New("not implemented") } -func (p panicyResolver) HasPath(s string) bool { +func (p *panicyResolver) FilesByBasename(filename string) ([]source.Location, error) { + p.searchCalled = true + return nil, errors.New("not implemented") +} + +func (p *panicyResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { + p.searchCalled = true + return nil, errors.New("not implemented") +} + +func (p *panicyResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { + p.searchCalled = true + return nil, errors.New("not implemented") +} + +func (p *panicyResolver) HasPath(s string) bool { return true } -func (p panicyResolver) FilesByPath(paths ...string) ([]source.Location, error) { +func (p *panicyResolver) FilesByPath(paths ...string) ([]source.Location, error) { + p.searchCalled = true return nil, errors.New("not implemented") } func (p *panicyResolver) FilesByGlob(patterns ...string) ([]source.Location, error) { - p.globCalled = true + p.searchCalled = true return nil, errors.New("not implemented") } -func (p panicyResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { +func (p *panicyResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { + p.searchCalled = true return nil, errors.New("not implemented") } -func (p panicyResolver) RelativeFileByPath(_ source.Location, path string) *source.Location { +func (p *panicyResolver) RelativeFileByPath(_ source.Location, path string) *source.Location { return nil } -func (p panicyResolver) AllLocations() <-chan source.Location { +func (p *panicyResolver) AllLocations() <-chan source.Location { return nil } -func (p panicyResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { +func (p *panicyResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { return source.FileMetadata{}, errors.New("not implemented") } @@ -479,5 +497,5 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) { resolver := &panicyResolver{} _, _, err := c.Catalog(resolver) assert.NoError(t, err) - assert.True(t, resolver.globCalled) + assert.True(t, resolver.searchCalled) } diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 4e89483233d..94aa4d7a5da 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -12,6 +12,7 @@ import ( "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader" "github.com/anchore/syft/syft/source" ) @@ -23,8 +24,8 @@ var emptyPURL = packageurl.PackageURL{} type classifier struct { Class string - // FileGlob is a selector to narrow down file inspection using the **/glob* syntax - FileGlob string + // SearchRequest specifies how to search for a file (and what full path globs must match for the file to be considered) + generic.SearchRequest // EvidenceMatcher is what will be used to match against the file in the source // location. If the matcher returns a package, the file will be considered a candidate. diff --git a/syft/pkg/cataloger/binary/classifier_test.go b/syft/pkg/cataloger/binary/classifier_test.go index c0c9520af52..8062713224e 100644 --- a/syft/pkg/cataloger/binary/classifier_test.go +++ b/syft/pkg/cataloger/binary/classifier_test.go @@ -1,6 +1,7 @@ package binary import ( + "github.com/anchore/syft/syft/pkg/cataloger/generic" "testing" "github.com/stretchr/testify/require" @@ -21,7 +22,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - FileGlob: ".*/version.txt", + SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{}, }, @@ -32,7 +33,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - FileGlob: ".*/version.txt", + SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{ cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"), @@ -47,7 +48,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - FileGlob: ".*/version.txt", + SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{ cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"), diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index 114d9295b40..5c18d47c5d6 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -3,12 +3,13 @@ package binary import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" ) var defaultClassifiers = []classifier{ { - Class: "python-binary", - FileGlob: "**/python*", + Class: "python-binary", + SearchRequest: generic.NewSearch().ByBasenameGlob("python*").Request(), EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)python(?P[0-9]+\.[0-9]+)$`, `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), @@ -20,8 +21,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "python-binary-lib", - FileGlob: "**/libpython*.so*", + Class: "python-binary-lib", + SearchRequest: generic.NewSearch().ByBasenameGlob("libpython*.so*").Request(), EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)libpython(?P[0-9]+\.[0-9]+).so.*$`, `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), @@ -33,8 +34,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "cpython-source", - FileGlob: "**/patchlevel.h", + Class: "cpython-source", + SearchRequest: generic.NewSearch().ByBasename("patchlevel.h").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)#define\s+PY_VERSION\s+"?(?P[0-9\.\-_a-zA-Z]+)"?`), Package: "python", @@ -45,8 +46,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "go-binary", - FileGlob: "**/go", + Class: "go-binary", + SearchRequest: generic.NewSearch().ByBasename("go").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)go(?P[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)\x00`), Package: "go", @@ -54,8 +55,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"), }, { - Class: "redis-binary", - FileGlob: "**/redis-server", + Class: "redis-binary", + SearchRequest: generic.NewSearch().ByBasename("redis-server").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?s)payload %5.*(?P\d.\d\.\d\d*?)[a-z0-9]{12}-[0-9]{19}`), Package: "redis", @@ -63,8 +64,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-openjdk", - FileGlob: "**/java", + Class: "java-binary-openjdk", + SearchRequest: generic.NewSearch().ByBasename("java").Request(), EvidenceMatcher: fileContentsVersionMatcher( // [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL] // [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL] @@ -75,8 +76,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-ibm", - FileGlob: "**/java", + Class: "java-binary-ibm", + SearchRequest: generic.NewSearch().ByBasename("java").Request(), EvidenceMatcher: fileContentsVersionMatcher( // [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL] `(?m)\x00java\x00(?P[0-9]+[.0-9]+)\x00{4}(?P[0-9]+[-._a-zA-Z0-9]+)\x00`), @@ -85,8 +86,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-oracle", - FileGlob: "**/java", + Class: "java-binary-oracle", + SearchRequest: generic.NewSearch().ByBasename("java").Request(), EvidenceMatcher: fileContentsVersionMatcher( // [NUL]19.0.1+10-21[NUL] `(?m)\x00(?P[0-9]+[.0-9]+[+][-0-9]+)\x00`), @@ -95,8 +96,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"), }, { - Class: "nodejs-binary", - FileGlob: "**/node", + Class: "nodejs-binary", + SearchRequest: generic.NewSearch().ByBasename("node").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)node\.js\/v(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "node", @@ -105,24 +106,24 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*"), }, { - Class: "go-binary-hint", - FileGlob: "**/VERSION", + Class: "go-binary-hint", + SearchRequest: generic.NewSearch().ByBasename("VERSION").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)go(?P[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`), Package: "go", PURL: mustPURL("pkg:generic/go@version"), }, { - Class: "busybox-binary", - FileGlob: "**/busybox", + Class: "busybox-binary", + SearchRequest: generic.NewSearch().ByBasename("busybox").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)BusyBox\s+v(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "busybox", CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"), }, { - Class: "php-cli-binary", - FileGlob: "**/php*", + Class: "php-cli-binary", + SearchRequest: generic.NewSearch().ByBasenameGlob("php*").Request(), EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)php[0-9]*$`, `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), @@ -131,8 +132,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "php-fpm-binary", - FileGlob: "**/php-fpm*", + Class: "php-fpm-binary", + SearchRequest: generic.NewSearch().ByBasenameGlob("php-fpm*").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), Package: "php-fpm", @@ -140,8 +141,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "php-apache-binary", - FileGlob: "**/libphp*.so", + Class: "php-apache-binary", + SearchRequest: generic.NewSearch().ByBasenameGlob("libphp*.so").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), Package: "libphp", @@ -149,8 +150,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "httpd-binary", - FileGlob: "**/httpd", + Class: "httpd-binary", + SearchRequest: generic.NewSearch().ByBasename("httpd").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)Apache\/(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "httpd", @@ -158,8 +159,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*"), }, { - Class: "memcached-binary", - FileGlob: "**/memcached", + Class: "memcached-binary", + SearchRequest: generic.NewSearch().ByBasename("memcached").Request(), EvidenceMatcher: fileContentsVersionMatcher( `(?m)memcached\s(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "memcached", diff --git a/syft/pkg/cataloger/cpp/cataloger.go b/syft/pkg/cataloger/cpp/cataloger.go index 80c6b5b11b2..18d54d72fc3 100644 --- a/syft/pkg/cataloger/cpp/cataloger.go +++ b/syft/pkg/cataloger/cpp/cataloger.go @@ -9,6 +9,6 @@ const catalogerName = "conan-cataloger" // NewConanCataloger returns a new C++ conanfile.txt and conan.lock cataloger object. func NewConanCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseConanfile, "**/conanfile.txt"). - WithParserByGlobs(parseConanlock, "**/conan.lock") + WithParserByBasename(parseConanfile, "conanfile.txt"). + WithParserByBasename(parseConanlock, "conan.lock") } diff --git a/syft/pkg/cataloger/dart/cataloger.go b/syft/pkg/cataloger/dart/cataloger.go index 5fbff6f088a..b077880c815 100644 --- a/syft/pkg/cataloger/dart/cataloger.go +++ b/syft/pkg/cataloger/dart/cataloger.go @@ -9,5 +9,5 @@ const catalogerName = "dartlang-lock-cataloger" // NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files. func NewPubspecLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parsePubspecLock, "**/pubspec.lock") + WithParserByBasename(parsePubspecLock, "pubspec.lock") } diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index 4484757105d..d0011b9f0e0 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -13,5 +13,7 @@ const catalogerName = "dpkgdb-cataloger" // NewDpkgdbCataloger returns a new Deb package cataloger capable of parsing DPKG status DB files. func NewDpkgdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). + // TODO: split up and re-write the glob search patterns + // WithParser(parseDpkgDB, generic.NewSearch().ByBasename("status").MustMatchGlob(pkg.DpkgDBGlob)) WithParserByGlobs(parseDpkgDB, pkg.DpkgDBGlob) } diff --git a/syft/pkg/cataloger/dotnet/cataloger.go b/syft/pkg/cataloger/dotnet/cataloger.go index 159edcb2743..239bf6ca382 100644 --- a/syft/pkg/cataloger/dotnet/cataloger.go +++ b/syft/pkg/cataloger/dotnet/cataloger.go @@ -9,5 +9,5 @@ const catalogerName = "dotnet-deps-cataloger" // NewDotnetDepsCataloger returns a new Dotnet cataloger object base on deps json files. func NewDotnetDepsCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseDotnetDeps, "**/*.deps.json") + WithParserByExtensions(parseDotnetDeps, ".deps.json") } diff --git a/syft/pkg/cataloger/elixir/cataloger.go b/syft/pkg/cataloger/elixir/cataloger.go index f4e62effd59..4a9beb98b4a 100644 --- a/syft/pkg/cataloger/elixir/cataloger.go +++ b/syft/pkg/cataloger/elixir/cataloger.go @@ -12,5 +12,5 @@ const catalogerName = "elixir-mix-lock-cataloger" // NewMixLockCataloger returns parses mix.lock files and returns packages func NewMixLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseMixLock, "**/mix.lock") + WithParserByBasename(parseMixLock, "mix.lock") } diff --git a/syft/pkg/cataloger/erlang/cataloger.go b/syft/pkg/cataloger/erlang/cataloger.go index 3e3b54f88e7..740f4cce7f0 100644 --- a/syft/pkg/cataloger/erlang/cataloger.go +++ b/syft/pkg/cataloger/erlang/cataloger.go @@ -12,5 +12,5 @@ const catalogerName = "erlang-rebar-lock-cataloger" // NewRebarLockCataloger returns parses rebar.lock files and returns packages. func NewRebarLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseRebarLock, "**/rebar.lock") + WithParserByBasename(parseRebarLock, "rebar.lock") } diff --git a/syft/pkg/cataloger/generic/cataloger.go b/syft/pkg/cataloger/generic/cataloger.go index 73533288d09..4096363c6e6 100644 --- a/syft/pkg/cataloger/generic/cataloger.go +++ b/syft/pkg/cataloger/generic/cataloger.go @@ -23,6 +23,27 @@ type Cataloger struct { upstreamCataloger string } +func (c *Cataloger) WithParser(parser Parser, searchRequests ...SearchRequest) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, req := range searchRequests { + log.Tracef("searching for: %s", req.String()) + + matches, err := req.Execute(resolver) + if err != nil { + log.WithFields("error", err).Warnf("unable to process search request %q", req.String()) + continue + } + + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + func (c *Cataloger) WithParserByGlobs(parser Parser, globs ...string) *Cataloger { c.processor = append(c.processor, func(resolver source.FileResolver, env Environment) []request { @@ -83,6 +104,46 @@ func (c *Cataloger) WithParserByPath(parser Parser, paths ...string) *Cataloger return c } +func (c *Cataloger) WithParserByExtensions(parser Parser, extensions ...string) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, e := range extensions { + log.WithFields("extension", e).Trace("searching for paths with a matching file extension") + + matches, err := resolver.FilesByExtension(e) + if err != nil { + log.Warnf("unable to process file extension=%q: %+v", e, err) + continue + } + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + +func (c *Cataloger) WithParserByBasename(parser Parser, filenames ...string) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, e := range filenames { + log.WithFields("basename", e).Trace("searching for paths with a matching basename") + + matches, err := resolver.FilesByBasename(e) + if err != nil { + log.Warnf("unable to process file basename=%q: %+v", e, err) + continue + } + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + func makeRequests(parser Parser, locations []source.Location) []request { var requests []request for _, l := range locations { diff --git a/syft/pkg/cataloger/generic/search.go b/syft/pkg/cataloger/generic/search.go new file mode 100644 index 00000000000..0eaffbeb690 --- /dev/null +++ b/syft/pkg/cataloger/generic/search.go @@ -0,0 +1,169 @@ +package generic + +import ( + "fmt" + + "github.com/bmatcuk/doublestar/v4" + + "github.com/anchore/syft/syft/source" +) + +type Search interface { + ByGlob(string) SearchRequest + ByPath(string) SearchRequest + ByBasename(string) SearchRequirement + ByBasenameGlob(string) SearchRequirement + ByExtension(string) SearchRequirement + ByMimeType(...string) SearchRequirement +} + +type SearchRequirement interface { + MustMatchGlob(string) SearchRequest + Request() SearchRequest +} + +type search struct { + SearchRequest +} + +type SearchRequest struct { + // search basis + glob string + path string + basename string + basenameGlob string + extension string + mimeTypes []string + // requirements + matchGlob string +} + +func NewSearch() Search { + return &search{} +} + +func (s *search) ByGlob(glob string) SearchRequest { + s.glob = glob + return s.SearchRequest +} + +func (s *search) ByPath(path string) SearchRequest { + s.path = path + return s.SearchRequest +} + +func (s *search) ByBasename(basename string) SearchRequirement { + s.basename = basename + return s +} + +func (s *search) ByBasenameGlob(basenameGlob string) SearchRequirement { + s.basenameGlob = basenameGlob + return s +} + +func (s *search) ByExtension(extension string) SearchRequirement { + s.extension = extension + return s +} + +func (s *search) ByMimeType(tys ...string) SearchRequirement { + s.mimeTypes = tys + return s +} + +func (s *search) MustMatchGlob(glob string) SearchRequest { + s.matchGlob = glob + return s.SearchRequest +} + +func (s *search) Request() SearchRequest { + return s.SearchRequest +} + +func (s SearchRequest) String() string { + var res string + switch { + case s.basename != "": + res += fmt.Sprintf("basename=%s", s.basename) + case s.basenameGlob != "": + res += fmt.Sprintf("basenameGlob=%s", s.basenameGlob) + case s.extension != "": + res += fmt.Sprintf("extension=%s", s.extension) + case len(s.mimeTypes) > 0: + res += fmt.Sprintf("mimeTypes=%s", s.mimeTypes) + case s.glob != "": + res += fmt.Sprintf("glob=%s", s.glob) + case s.path != "": + res += fmt.Sprintf("path=%s", s.path) + default: + res = "no search criteria" + return res + } + + if s.matchGlob != "" { + res += fmt.Sprintf(" and result must match glob=%s", s.matchGlob) + } + return res +} + +func (s SearchRequest) Execute(resolver source.FileResolver) ([]source.Location, error) { + var locations []source.Location + var err error + + switch { + case s.glob != "": + locations, err = resolver.FilesByGlob(s.glob) + if err != nil { + return nil, fmt.Errorf("unable to process search glob=%q: %w", s.glob, err) + } + case s.path != "": + locations, err = resolver.FilesByPath(s.path) + if err != nil { + return nil, fmt.Errorf("unable to process search path=%q: %w", s.path, err) + } + case s.basename != "": + locations, err = resolver.FilesByBasename(s.basename) + if err != nil { + return nil, fmt.Errorf("unable to process search basis basename=%q: %w", s.basename, err) + } + case s.basenameGlob != "": + locations, err = resolver.FilesByBasenameGlob(s.basenameGlob) + if err != nil { + return nil, fmt.Errorf("unable to process search basis basename=%q: %w", s.basename, err) + } + case s.extension != "": + locations, err = resolver.FilesByExtension(s.extension) + if err != nil { + return nil, fmt.Errorf("unable to process search basis extension=%q: %w", s.extension, err) + } + case len(s.mimeTypes) > 0: + for _, t := range s.mimeTypes { + locations, err = resolver.FilesByMIMEType(t) + if err != nil { + return nil, fmt.Errorf("unable to process search basis mimetype=%q: %w", t, err) + } + } + } + + if s.matchGlob != "" { + var globMatches []source.Location + forMatches: + for _, m := range locations { + var matchesGlob bool + for _, path := range []string{m.RealPath, m.VirtualPath} { + matchesGlob, err = doublestar.Match(s.matchGlob, path) + if err != nil { + return nil, fmt.Errorf("unable to validate glob requirement=%q: %w", s.matchGlob, err) + } + if matchesGlob { + globMatches = append(globMatches, m) + continue forMatches + } + } + } + locations = globMatches + } + + return locations, nil +} diff --git a/syft/pkg/cataloger/golang/cataloger.go b/syft/pkg/cataloger/golang/cataloger.go index f49b25e656a..255422670ef 100644 --- a/syft/pkg/cataloger/golang/cataloger.go +++ b/syft/pkg/cataloger/golang/cataloger.go @@ -11,7 +11,7 @@ import ( // NewGoModFileCataloger returns a new Go module cataloger object. func NewGoModFileCataloger() *generic.Cataloger { return generic.NewCataloger("go-mod-file-cataloger"). - WithParserByGlobs(parseGoModFile, "**/go.mod") + WithParserByBasename(parseGoModFile, "go.mod") } // NewGoModuleBinaryCataloger returns a new Golang cataloger object. diff --git a/syft/pkg/cataloger/haskell/cataloger.go b/syft/pkg/cataloger/haskell/cataloger.go index a7638ee837c..4566def6ccf 100644 --- a/syft/pkg/cataloger/haskell/cataloger.go +++ b/syft/pkg/cataloger/haskell/cataloger.go @@ -11,7 +11,7 @@ import ( // NewHackageCataloger returns a new Haskell cataloger object. func NewHackageCataloger() *generic.Cataloger { return generic.NewCataloger("haskell-cataloger"). - WithParserByGlobs(parseStackYaml, "**/stack.yaml"). - WithParserByGlobs(parseStackLock, "**/stack.yaml.lock"). - WithParserByGlobs(parseCabalFreeze, "**/cabal.project.freeze") + WithParserByBasename(parseStackYaml, "stack.yaml"). + WithParserByBasename(parseStackLock, "stack.yaml.lock"). + WithParserByBasename(parseCabalFreeze, "cabal.project.freeze") } diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 3150351c8cc..50923e27572 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -18,15 +18,15 @@ import ( var _ generic.Parser = parseJavaArchive -var archiveFormatGlobs = []string{ - "**/*.jar", - "**/*.war", - "**/*.ear", - "**/*.par", - "**/*.sar", - "**/*.jpi", - "**/*.hpi", - "**/*.lpkg", // Zip-compressed package used to deploy applications +var archiveFormatExtensions = []string{ + ".jar", + ".war", + ".ear", + ".par", + ".sar", + ".jpi", + ".hpi", + ".lpkg", // Zip-compressed package used to deploy applications // (aka plugins) to Liferay Portal server. Those files contains .JAR(s) and a .PROPERTIES file, the latter // has information about the application and installation requirements. // NOTE(jonasagx): If you would like to test it with lpkg file, @@ -36,6 +36,15 @@ var archiveFormatGlobs = []string{ // project that we can build in CI feel free to include it } +// note: this mirrors 'archiveFormatExtensions' as a glob pattern for each element +var archiveFormatGlobs []string + +func init() { + for _, ext := range archiveFormatExtensions { + archiveFormatGlobs = append(archiveFormatGlobs, fmt.Sprintf("**/*%s", ext)) + } +} + // javaArchiveHashes are all the current hash algorithms used to calculate archive digests var javaArchiveHashes = []crypto.Hash{ crypto.SHA1, diff --git a/syft/pkg/cataloger/java/cataloger.go b/syft/pkg/cataloger/java/cataloger.go index 73bdf71541f..b22c68ce44c 100644 --- a/syft/pkg/cataloger/java/cataloger.go +++ b/syft/pkg/cataloger/java/cataloger.go @@ -10,16 +10,16 @@ import ( // NewJavaCataloger returns a new Java archive cataloger object. func NewJavaCataloger(cfg Config) *generic.Cataloger { c := generic.NewCataloger("java-cataloger"). - WithParserByGlobs(parseJavaArchive, archiveFormatGlobs...) + WithParserByExtensions(parseJavaArchive, archiveFormatExtensions...) if cfg.SearchIndexedArchives { // java archives wrapped within zip files - c.WithParserByGlobs(parseZipWrappedJavaArchive, genericZipGlobs...) + c.WithParserByExtensions(parseZipWrappedJavaArchive, genericZipExtensions...) } if cfg.SearchUnindexedArchives { // java archives wrapped within tar files - c.WithParserByGlobs(parseTarWrappedJavaArchive, genericTarGlobs...) + c.WithParserByExtensions(parseTarWrappedJavaArchive, genericTarExtensions...) } return c } @@ -29,5 +29,5 @@ func NewJavaCataloger(cfg Config) *generic.Cataloger { // Pom files list dependencies that maybe not be locally installed yet. func NewJavaPomCataloger() *generic.Cataloger { return generic.NewCataloger("java-pom-cataloger"). - WithParserByGlobs(parserPomXML, pomXMLDirGlob) + WithParserByBasename(parserPomXML, "pom.xml") } diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index eb37dd7209e..0f30e092999 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -18,7 +18,6 @@ import ( ) const pomXMLGlob = "*pom.xml" -const pomXMLDirGlob = "**/pom.xml" var propertyMatcher = regexp.MustCompile("[$][{][^}]+[}]") diff --git a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go index 882493b01a5..12269ff07a2 100644 --- a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go +++ b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go @@ -10,30 +10,30 @@ import ( "github.com/anchore/syft/syft/source" ) -var genericTarGlobs = []string{ - "**/*.tar", +var genericTarExtensions = []string{ + ".tar", // gzipped tar - "**/*.tar.gz", - "**/*.tgz", + ".tar.gz", + ".tgz", // bzip2 - "**/*.tar.bz", - "**/*.tar.bz2", - "**/*.tbz", - "**/*.tbz2", + ".tar.bz", + ".tar.bz2", + ".tbz", + ".tbz2", // brotli - "**/*.tar.br", - "**/*.tbr", + ".tar.br", + ".tbr", // lz4 - "**/*.tar.lz4", - "**/*.tlz4", + ".tar.lz4", + ".tlz4", // sz - "**/*.tar.sz", - "**/*.tsz", + ".tar.sz", + ".tsz", // xz - "**/*.tar.xz", - "**/*.txz", - // zst - "**/*.tar.zst", + ".tar.xz", + ".txz", + // zstandard + ".tar.zst", } // TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246) diff --git a/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go b/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go index dffe5df74a6..e2233735d78 100644 --- a/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go +++ b/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go @@ -10,8 +10,8 @@ import ( "github.com/anchore/syft/syft/source" ) -var genericZipGlobs = []string{ - "**/*.zip", +var genericZipExtensions = []string{ + ".zip", } // TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246) diff --git a/syft/pkg/cataloger/javascript/cataloger.go b/syft/pkg/cataloger/javascript/cataloger.go index 6688abcdc8b..d23aa8107c2 100644 --- a/syft/pkg/cataloger/javascript/cataloger.go +++ b/syft/pkg/cataloger/javascript/cataloger.go @@ -10,12 +10,12 @@ import ( // NewJavascriptPackageCataloger returns a new JavaScript cataloger object based on detection of npm based packages. func NewJavascriptPackageCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-package-cataloger"). - WithParserByGlobs(parsePackageJSON, "**/package.json") + WithParserByBasename(parsePackageJSON, "package.json") } func NewJavascriptLockCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-lock-cataloger"). - WithParserByGlobs(parsePackageLock, "**/package-lock.json"). - WithParserByGlobs(parseYarnLock, "**/yarn.lock"). - WithParserByGlobs(parsePnpmLock, "**/pnpm-lock.yaml") + WithParserByBasename(parsePackageLock, "package-lock.json"). + WithParserByBasename(parseYarnLock, "yarn.lock"). + WithParserByBasename(parsePnpmLock, "pnpm-lock.yaml") } diff --git a/syft/pkg/cataloger/php/cataloger.go b/syft/pkg/cataloger/php/cataloger.go index 35ac46bb354..c5be36fd472 100644 --- a/syft/pkg/cataloger/php/cataloger.go +++ b/syft/pkg/cataloger/php/cataloger.go @@ -10,11 +10,11 @@ import ( // NewPHPComposerInstalledCataloger returns a new cataloger for PHP installed.json files. func NewPHPComposerInstalledCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-installed-cataloger"). - WithParserByGlobs(parseInstalledJSON, "**/installed.json") + WithParserByBasename(parseInstalledJSON, "installed.json") } // NewPHPComposerLockCataloger returns a new cataloger for PHP composer.lock files. func NewPHPComposerLockCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-lock-cataloger"). - WithParserByGlobs(parseComposerLock, "**/composer.lock") + WithParserByBasename(parseComposerLock, "composer.lock") } diff --git a/syft/pkg/cataloger/portage/cataloger.go b/syft/pkg/cataloger/portage/cataloger.go index fb29c024ede..eff4e7ccd89 100644 --- a/syft/pkg/cataloger/portage/cataloger.go +++ b/syft/pkg/cataloger/portage/cataloger.go @@ -29,7 +29,7 @@ var ( func NewPortageCataloger() *generic.Cataloger { return generic.NewCataloger("portage-cataloger"). - WithParserByGlobs(parsePortageContents, "**/var/db/pkg/*/*/CONTENTS") + WithParser(parsePortageContents, generic.NewSearch().ByBasename("CONTENTS").MustMatchGlob("**/var/db/pkg/*/*/CONTENTS")) } func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { diff --git a/syft/pkg/cataloger/python/cataloger.go b/syft/pkg/cataloger/python/cataloger.go index cd2ba358b34..9c051262603 100644 --- a/syft/pkg/cataloger/python/cataloger.go +++ b/syft/pkg/cataloger/python/cataloger.go @@ -4,23 +4,25 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -const ( - eggMetadataGlob = "**/*egg-info/PKG-INFO" - eggFileMetadataGlob = "**/*.egg-info" - wheelMetadataGlob = "**/*dist-info/METADATA" -) +const eggInfoExtension = ".egg-info" // NewPythonIndexCataloger returns a new cataloger for python packages referenced from poetry lock files, requirements.txt files, and setup.py files. func NewPythonIndexCataloger() *generic.Cataloger { return generic.NewCataloger("python-index-cataloger"). - WithParserByGlobs(parseRequirementsTxt, "**/*requirements*.txt"). - WithParserByGlobs(parsePoetryLock, "**/poetry.lock"). - WithParserByGlobs(parsePipfileLock, "**/Pipfile.lock"). - WithParserByGlobs(parseSetup, "**/setup.py") + WithParser(parseRequirementsTxt, generic.NewSearch().ByBasenameGlob("*requirements*.txt").Request()). + WithParserByBasename(parsePoetryLock, "poetry.lock"). + WithParserByBasename(parsePipfileLock, "Pipfile.lock"). + WithParserByBasename(parseSetup, "setup.py") } // NewPythonPackageCataloger returns a new cataloger for python packages within egg or wheel installation directories. func NewPythonPackageCataloger() *generic.Cataloger { return generic.NewCataloger("python-package-cataloger"). - WithParserByGlobs(parseWheelOrEgg, eggMetadataGlob, eggFileMetadataGlob, wheelMetadataGlob) + WithParser(parseWheelOrEgg, + generic.NewSearch().ByBasename("METADATA").MustMatchGlob("**/*dist-info/METADATA"), + generic.NewSearch().ByBasename("PKG-INFO").MustMatchGlob("**/*egg-info/PKG-INFO"), + ). + WithParserByExtensions(parseWheelOrEgg, + eggInfoExtension, + ) } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index f21c31bfa64..44c6efe161d 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -81,7 +81,7 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (pkg.PythonPackageMe // of egg metadata (as opposed to a directory that contains more metadata // files). func isEggRegularFile(path string) bool { - return file.GlobMatch(eggFileMetadataGlob, path) + return file.GlobMatch("**/*"+eggInfoExtension, path) } // determineSitePackagesRootPath returns the path of the site packages root, diff --git a/syft/pkg/cataloger/rpm/cataloger.go b/syft/pkg/cataloger/rpm/cataloger.go index eecebdc9771..427d8ba7c70 100644 --- a/syft/pkg/cataloger/rpm/cataloger.go +++ b/syft/pkg/cataloger/rpm/cataloger.go @@ -11,12 +11,18 @@ import ( // NewRpmDBCataloger returns a new RPM DB cataloger object. func NewRpmDBCataloger() *generic.Cataloger { return generic.NewCataloger("rpm-db-cataloger"). - WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob). - WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob) + WithParser(parseRpmDB, + generic.NewSearch().ByBasename("Packages").MustMatchGlob(pkg.RpmDBGlob), + generic.NewSearch().ByBasename("Packages.db").MustMatchGlob(pkg.RpmDBGlob), + generic.NewSearch().ByBasename("rpmdb.sqlite").MustMatchGlob(pkg.RpmDBGlob), + ). + WithParser(parseRpmManifest, + generic.NewSearch().ByBasename("container-manifest-2").MustMatchGlob(pkg.RpmManifestGlob), + ) } // NewFileCataloger returns a new RPM file cataloger object. func NewFileCataloger() *generic.Cataloger { return generic.NewCataloger("rpm-file-cataloger"). - WithParserByGlobs(parseRpm, "**/*.rpm") + WithParserByExtensions(parseRpm, ".rpm") } diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 8ca68434a04..955d1d6400b 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -19,19 +19,28 @@ type rpmdbTestFileResolverMock struct { ignorePaths bool } +func (r rpmdbTestFileResolverMock) FilesByExtension(extension string) ([]source.Location, error) { + panic("not implemented") +} + +func (r rpmdbTestFileResolverMock) FilesByBasename(filename string) ([]source.Location, error) { + panic("not implemented") +} + +func (r rpmdbTestFileResolverMock) FilesByBasenameGlob(glob string) ([]source.Location, error) { + panic("not implemented") +} + func (r rpmdbTestFileResolverMock) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { - //TODO implement me - panic("implement me") + panic("not implemented") } func (r rpmdbTestFileResolverMock) AllLocations() <-chan source.Location { - //TODO implement me - panic("implement me") + panic("not implemented") } func (r rpmdbTestFileResolverMock) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { - //TODO implement me - panic("implement me") + panic("not implemented") } func newTestFileResolver(ignorePaths bool) *rpmdbTestFileResolverMock { diff --git a/syft/pkg/cataloger/ruby/catalogers.go b/syft/pkg/cataloger/ruby/catalogers.go index e3e173a21d9..61cde77575b 100644 --- a/syft/pkg/cataloger/ruby/catalogers.go +++ b/syft/pkg/cataloger/ruby/catalogers.go @@ -10,11 +10,13 @@ import ( // NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock). func NewGemFileLockCataloger() *generic.Cataloger { return generic.NewCataloger("ruby-gemfile-cataloger"). - WithParserByGlobs(parseGemFileLockEntries, "**/Gemfile.lock") + WithParserByBasename(parseGemFileLockEntries, "Gemfile.lock") } // NewGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec). func NewGemSpecCataloger() *generic.Cataloger { return generic.NewCataloger("ruby-gemspec-cataloger"). - WithParserByGlobs(parseGemSpecEntries, "**/specifications/**/*.gemspec") + WithParser(parseGemSpecEntries, + generic.NewSearch().ByExtension("*.gemspec").MustMatchGlob("**/specifications/**/*.gemspec"), + ) } diff --git a/syft/pkg/cataloger/rust/cataloger.go b/syft/pkg/cataloger/rust/cataloger.go index a5128b24da4..4326ddfcd0b 100644 --- a/syft/pkg/cataloger/rust/cataloger.go +++ b/syft/pkg/cataloger/rust/cataloger.go @@ -11,7 +11,7 @@ import ( // NewCargoLockCataloger returns a new Rust Cargo lock file cataloger object. func NewCargoLockCataloger() *generic.Cataloger { return generic.NewCataloger("rust-cargo-lock-cataloger"). - WithParserByGlobs(parseCargoLock, "**/Cargo.lock") + WithParserByBasename(parseCargoLock, "Cargo.lock") } // NewAuditBinaryCataloger returns a new Rust auditable binary cataloger object that can detect dependencies diff --git a/syft/pkg/cataloger/sbom/cataloger.go b/syft/pkg/cataloger/sbom/cataloger.go index 0c82f452ebd..b80c197252c 100644 --- a/syft/pkg/cataloger/sbom/cataloger.go +++ b/syft/pkg/cataloger/sbom/cataloger.go @@ -13,19 +13,24 @@ const catalogerName = "sbom-cataloger" // NewSBOMCataloger returns a new SBOM cataloger object loaded from saved SBOM JSON. func NewSBOMCataloger() *generic.Cataloger { + generic.NewSearch().ByExtension(".txt").MustMatchGlob("**/somewhere/here/*.txt") return generic.NewCataloger(catalogerName). - WithParserByGlobs(parseSBOM, - "**/*.syft.json", - "**/*.bom.*", - "**/*.bom", - "**/bom", - "**/*.sbom.*", - "**/*.sbom", - "**/sbom", - "**/*.cdx.*", - "**/*.cdx", - "**/*.spdx.*", - "**/*.spdx", + WithParserByBasename(parseSBOM, + "bom", + "sbom", + ). + WithParserByExtensions(parseSBOM, + ".syft.json", + ".bom", + ".sbom", + ".cdx", + ".spdx", + ). + WithParser(parseSBOM, + generic.NewSearch().ByBasenameGlob("*.bom.*").Request(), + generic.NewSearch().ByBasenameGlob("*.sbom.*").Request(), + generic.NewSearch().ByBasenameGlob("*.cdx.*").Request(), + generic.NewSearch().ByBasenameGlob("*.spdx.*").Request(), ) } diff --git a/syft/pkg/cataloger/swift/cataloger.go b/syft/pkg/cataloger/swift/cataloger.go index 5ce504b749a..50dcc7a288e 100644 --- a/syft/pkg/cataloger/swift/cataloger.go +++ b/syft/pkg/cataloger/swift/cataloger.go @@ -10,5 +10,5 @@ import ( // NewCocoapodsCataloger returns a new Swift Cocoapods lock file cataloger object. func NewCocoapodsCataloger() *generic.Cataloger { return generic.NewCataloger("cocoapods-cataloger"). - WithParserByGlobs(parsePodfileLock, "**/Podfile.lock") + WithParserByBasename(parsePodfileLock, "Podfile.lock") } diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index 5f1970c2a44..e0171efb504 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -433,6 +433,67 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) { return result, nil } +func (r directoryResolver) FilesByExtension(extension string) ([]Location, error) { + result := make([]Location, 0) + + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/*"+extension, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } + + return result, nil +} + +func (r directoryResolver) FilesByBasename(filename string) ([]Location, error) { + result := make([]Location, 0) + + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } + + return result, nil +} + +// TODO: duplicate code with FilesByBasename +func (r directoryResolver) FilesByBasenameGlob(filename string) ([]Location, error) { + result := make([]Location, 0) + + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } + + return result, nil +} + // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. // This is helpful when attempting to find a file that is in the same layer or lower as another file. For the // directoryResolver, this is a simple path lookup. diff --git a/syft/source/excluding_file_resolver.go b/syft/source/excluding_file_resolver.go index 50969116a81..e0c7c093338 100644 --- a/syft/source/excluding_file_resolver.go +++ b/syft/source/excluding_file_resolver.go @@ -59,6 +59,21 @@ func (r *excludingResolver) FilesByMIMEType(types ...string) ([]Location, error) return filterLocations(locations, err, r.excludeFn) } +func (r *excludingResolver) FilesByExtension(extension string) ([]Location, error) { + // TODO implement me + panic("implement me") +} + +func (r *excludingResolver) FilesByBasename(filename string) ([]Location, error) { + // TODO implement me + panic("implement me") +} + +func (r *excludingResolver) FilesByBasenameGlob(glob string) ([]Location, error) { + // TODO implement me + panic("implement me") +} + func (r *excludingResolver) RelativeFileByPath(location Location, path string) *Location { l := r.delegate.RelativeFileByPath(location, path) if l != nil && locationMatches(l, r.excludeFn) { diff --git a/syft/source/excluding_file_resolver_test.go b/syft/source/excluding_file_resolver_test.go index 958864e2920..90d30dfe032 100644 --- a/syft/source/excluding_file_resolver_test.go +++ b/syft/source/excluding_file_resolver_test.go @@ -56,20 +56,20 @@ func TestExcludingResolver(t *testing.T) { resolver := &mockResolver{ locations: test.locations, } - excludingResolver := NewExcludingResolver(resolver, test.excludeFn) + er := NewExcludingResolver(resolver, test.excludeFn) - locations, _ := excludingResolver.FilesByPath() + locations, _ := er.FilesByPath() assert.ElementsMatch(t, locationPaths(locations), test.expected) - locations, _ = excludingResolver.FilesByGlob() + locations, _ = er.FilesByGlob() assert.ElementsMatch(t, locationPaths(locations), test.expected) - locations, _ = excludingResolver.FilesByMIMEType() + locations, _ = er.FilesByMIMEType() assert.ElementsMatch(t, locationPaths(locations), test.expected) locations = []Location{} - channel := excludingResolver.AllLocations() + channel := er.AllLocations() for location := range channel { locations = append(locations, location) } @@ -78,26 +78,26 @@ func TestExcludingResolver(t *testing.T) { diff := difference(test.locations, test.expected) for _, path := range diff { - assert.False(t, excludingResolver.HasPath(path)) - c, err := excludingResolver.FileContentsByLocation(makeLocation(path)) + assert.False(t, er.HasPath(path)) + c, err := er.FileContentsByLocation(makeLocation(path)) assert.Nil(t, c) assert.Error(t, err) - m, err := excludingResolver.FileMetadataByLocation(makeLocation(path)) + m, err := er.FileMetadataByLocation(makeLocation(path)) assert.Empty(t, m.LinkDestination) assert.Error(t, err) - l := excludingResolver.RelativeFileByPath(makeLocation(""), path) + l := er.RelativeFileByPath(makeLocation(""), path) assert.Nil(t, l) } for _, path := range test.expected { - assert.True(t, excludingResolver.HasPath(path)) - c, err := excludingResolver.FileContentsByLocation(makeLocation(path)) + assert.True(t, er.HasPath(path)) + c, err := er.FileContentsByLocation(makeLocation(path)) assert.NotNil(t, c) assert.Nil(t, err) - m, err := excludingResolver.FileMetadataByLocation(makeLocation(path)) + m, err := er.FileMetadataByLocation(makeLocation(path)) assert.NotEmpty(t, m.LinkDestination) assert.Nil(t, err) - l := excludingResolver.RelativeFileByPath(makeLocation(""), path) + l := er.RelativeFileByPath(makeLocation(""), path) assert.NotNil(t, l) } }) @@ -142,6 +142,18 @@ type mockResolver struct { locations []string } +func (r *mockResolver) FilesByExtension(extension string) ([]Location, error) { + panic("not implemented") +} + +func (r *mockResolver) FilesByBasename(filename string) ([]Location, error) { + panic("not implemented") +} + +func (r *mockResolver) FilesByBasenameGlob(glob string) ([]Location, error) { + panic("not implemented") +} + func (r *mockResolver) getLocations() ([]Location, error) { out := []Location{} for _, path := range r.locations { diff --git a/syft/source/file_resolver.go b/syft/source/file_resolver.go index b6ccb481595..c8565fc2e62 100644 --- a/syft/source/file_resolver.go +++ b/syft/source/file_resolver.go @@ -27,8 +27,14 @@ type FilePathResolver interface { HasPath(string) bool // FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches) FilesByPath(paths ...string) ([]Location, error) - // FilesByGlob fetches a set of file references which the given glob matches + // FilesByGlob fetches a set of file references for the given glob matches FilesByGlob(patterns ...string) ([]Location, error) + // FilesByExtension fetches a set of file references for the given file extensions + FilesByExtension(extension string) ([]Location, error) + // FilesByBasename fetches a set of file references for the given filenames + FilesByBasename(filename string) ([]Location, error) + // FilesByBasenameGlob fetches a set of file references for the given filename glob patterns (e.g. *requirements*.txt) + FilesByBasenameGlob(glob string) ([]Location, error) // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types FilesByMIMEType(types ...string) ([]Location, error) // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. diff --git a/syft/source/all_layers_resolver.go b/syft/source/image_all_layers_resolver.go similarity index 73% rename from syft/source/all_layers_resolver.go rename to syft/source/image_all_layers_resolver.go index b01d77fab9b..f979f2204f6 100644 --- a/syft/source/all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -11,16 +11,16 @@ import ( "github.com/anchore/syft/internal/log" ) -var _ FileResolver = (*allLayersResolver)(nil) +var _ FileResolver = (*imageAllLayersResolver)(nil) -// allLayersResolver implements path and content access for the AllLayers source option for container image data sources. -type allLayersResolver struct { +// imageAllLayersResolver implements path and content access for the AllLayers source option for container image data sources. +type imageAllLayersResolver struct { img *image.Image layers []int } // newAllLayersResolver returns a new resolver from the perspective of all image layers for the given image. -func newAllLayersResolver(img *image.Image) (*allLayersResolver, error) { +func newAllLayersResolver(img *image.Image) (*imageAllLayersResolver, error) { if len(img.Layers) == 0 { return nil, fmt.Errorf("the image does not contain any layers") } @@ -29,14 +29,14 @@ func newAllLayersResolver(img *image.Image) (*allLayersResolver, error) { for idx := range img.Layers { layers = append(layers, idx) } - return &allLayersResolver{ + return &imageAllLayersResolver{ img: img, layers: layers, }, nil } // HasPath indicates if the given path exists in the underlying source. -func (r *allLayersResolver) HasPath(path string) bool { +func (r *imageAllLayersResolver) HasPath(path string) bool { p := file.Path(path) for _, layerIdx := range r.layers { tree := r.img.Layers[layerIdx].Tree @@ -47,7 +47,7 @@ func (r *allLayersResolver) HasPath(path string) bool { return false } -func (r *allLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.ReferenceSet, layerIdx int) ([]file.Reference, error) { +func (r *imageAllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.ReferenceSet, layerIdx int) ([]file.Reference, error) { uniqueFiles := make([]file.Reference, 0) // since there is potentially considerable work for each symlink/hardlink that needs to be resolved, let's check to see if this is a symlink/hardlink first @@ -78,7 +78,7 @@ func (r *allLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.Ref } // FilesByPath returns all file.References that match the given paths from any layer in the image. -func (r *allLayersResolver) FilesByPath(paths ...string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error) { uniqueFileIDs := file.NewFileReferenceSet() uniqueLocations := make([]Location, 0) @@ -120,7 +120,7 @@ func (r *allLayersResolver) FilesByPath(paths ...string) ([]Location, error) { } // FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image. -func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) { uniqueFileIDs := file.NewFileReferenceSet() uniqueLocations := make([]Location, 0) @@ -161,7 +161,7 @@ func (r *allLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. // This is helpful when attempting to find a file that is in the same layer or lower as another file. -func (r *allLayersResolver) RelativeFileByPath(location Location, path string) *Location { +func (r *imageAllLayersResolver) RelativeFileByPath(location Location, path string) *Location { entry, err := r.img.FileCatalog.Get(location.ref) if err != nil { return nil @@ -183,7 +183,7 @@ func (r *allLayersResolver) RelativeFileByPath(location Location, path string) * // FileContentsByLocation fetches file contents for a single file reference, irregardless of the source layer. // If the path does not exist an error is returned. -func (r *allLayersResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) { +func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) { entry, err := r.img.FileCatalog.Get(location.ref) if err != nil { return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err) @@ -203,7 +203,7 @@ func (r *allLayersResolver) FileContentsByLocation(location Location) (io.ReadCl return r.img.FileContentsByRef(location.ref) } -func (r *allLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) { var locations []Location for _, layerIdx := range r.layers { layer := r.img.Layers[layerIdx] @@ -221,7 +221,61 @@ func (r *allLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) return locations, nil } -func (r *allLayersResolver) AllLocations() <-chan Location { +func (r *imageAllLayersResolver) FilesByExtension(extension string) ([]Location, error) { + var locations []Location + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] + + refs, err := layer.FilesByExtension(extension) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + } + + return locations, nil +} + +func (r *imageAllLayersResolver) FilesByBasename(filename string) ([]Location, error) { + var locations []Location + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] + + refs, err := layer.FilesByBasename(filename) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + } + + return locations, nil +} + +func (r *imageAllLayersResolver) FilesByBasenameGlob(glob string) ([]Location, error) { + var locations []Location + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] + + refs, err := layer.FilesByBasenameGlob(glob) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + } + + return locations, nil +} + +func (r *imageAllLayersResolver) AllLocations() <-chan Location { results := make(chan Location) go func() { defer close(results) @@ -235,6 +289,6 @@ func (r *allLayersResolver) AllLocations() <-chan Location { return results } -func (r *allLayersResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { +func (r *imageAllLayersResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { return fileMetadataByLocation(r.img, location) } diff --git a/syft/source/all_layers_resolver_test.go b/syft/source/image_all_layers_resolver_test.go similarity index 100% rename from syft/source/all_layers_resolver_test.go rename to syft/source/image_all_layers_resolver_test.go diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index 8977023a102..07d70c5edd6 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -189,6 +189,48 @@ func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, erro return locations, nil } +func (r *imageSquashResolver) FilesByExtension(extension string) ([]Location, error) { + refs, err := r.img.FilesByExtensionFromSquash(extension) + if err != nil { + return nil, err + } + + var locations []Location + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + + return locations, nil +} + +func (r *imageSquashResolver) FilesByBasename(filename string) ([]Location, error) { + refs, err := r.img.FilesByBasenameFromSquash(filename) + if err != nil { + return nil, err + } + + var locations []Location + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + + return locations, nil +} + +func (r *imageSquashResolver) FilesByBasenameGlob(glob string) ([]Location, error) { + refs, err := r.img.FilesByBasenameGlobFromSquash(glob) + if err != nil { + return nil, err + } + + var locations []Location + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } + + return locations, nil +} + func (r *imageSquashResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { return fileMetadataByLocation(r.img, location) } diff --git a/syft/source/mock_resolver.go b/syft/source/mock_resolver.go index 51d7edc4944..0c0def5c0c5 100644 --- a/syft/source/mock_resolver.go +++ b/syft/source/mock_resolver.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "path" "github.com/bmatcuk/doublestar/v4" ) @@ -17,34 +18,53 @@ type MockResolver struct { locations []Location metadata map[Location]FileMetadata mimeTypeIndex map[string][]Location + extension map[string][]Location + basename map[string][]Location } // NewMockResolverForPaths creates a new MockResolver, where the only resolvable // files are those specified by the supplied paths. func NewMockResolverForPaths(paths ...string) *MockResolver { var locations []Location + extension := make(map[string][]Location) + basename := make(map[string][]Location) for _, p := range paths { - locations = append(locations, NewLocation(p)) + loc := NewLocation(p) + locations = append(locations, loc) + ext := path.Ext(p) + extension[ext] = append(extension[ext], loc) + bn := path.Base(p) + basename[bn] = append(basename[bn], loc) } return &MockResolver{ locations: locations, metadata: make(map[Location]FileMetadata), + extension: extension, + basename: basename, } } func NewMockResolverForPathsWithMetadata(metadata map[Location]FileMetadata) *MockResolver { var locations []Location var mimeTypeIndex = make(map[string][]Location) + extension := make(map[string][]Location) + basename := make(map[string][]Location) for l, m := range metadata { locations = append(locations, l) mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l) + ext := path.Ext(l.RealPath) + extension[ext] = append(extension[ext], l) + bn := path.Base(l.RealPath) + basename[bn] = append(basename[bn], l) } return &MockResolver{ locations: locations, metadata: metadata, mimeTypeIndex: mimeTypeIndex, + extension: extension, + basename: basename, } } @@ -160,3 +180,17 @@ func (r MockResolver) FilesByMIMEType(types ...string) ([]Location, error) { } return locations, nil } + +func (r MockResolver) FilesByExtension(extension string) ([]Location, error) { + return r.extension[extension], nil + +} + +func (r MockResolver) FilesByBasename(filename string) ([]Location, error) { + return r.basename[filename], nil +} + +func (r MockResolver) FilesByBasenameGlob(glob string) ([]Location, error) { + // TODO implement me + panic("implement me") +} From ebddc9288a39d4ce9b073ab679470f2c89c7920b Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 09:25:48 -0500 Subject: [PATCH 02/54] add cataloger test for alpm cataloger Signed-off-by: Alex Goodman --- internal/string_helpers.go | 7 + internal/string_helpers_test.go | 34 ++++ syft/pkg/cataloger/alpm/cataloger_test.go | 182 ++++++++++++++++++ syft/pkg/cataloger/alpm/package.go | 5 +- syft/pkg/cataloger/alpm/parse_alpm_db.go | 4 + syft/pkg/cataloger/alpm/parse_alpm_db_test.go | 4 +- .../var/lib/pacman/local/gmp-6.2.1-2/desc | 44 +++++ .../var/lib/pacman/local/gmp-6.2.1-2/files | 21 ++ .../var/lib/pacman/local/gmp-6.2.1-2/mtree | Bin 0 -> 1038 bytes syft/pkg/cataloger/apkdb/parse_apk_db.go | 10 +- .../internal/pkgtest/test_generic_parser.go | 5 + 11 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 syft/pkg/cataloger/alpm/cataloger_test.go create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/mtree diff --git a/internal/string_helpers.go b/internal/string_helpers.go index 09a8fda2d5a..4f5c65a4ad5 100644 --- a/internal/string_helpers.go +++ b/internal/string_helpers.go @@ -28,3 +28,10 @@ func StringInSlice(a string, list []string) bool { } return false } + +func SplitAny(s string, seps string) []string { + splitter := func(r rune) bool { + return strings.ContainsRune(seps, r) + } + return strings.FieldsFunc(s, splitter) +} diff --git a/internal/string_helpers_test.go b/internal/string_helpers_test.go index f89a0820fca..45b90195aaa 100644 --- a/internal/string_helpers_test.go +++ b/internal/string_helpers_test.go @@ -104,3 +104,37 @@ func TestTruncateMiddleEllipsis(t *testing.T) { }) } } + +func TestSplitAny(t *testing.T) { + + tests := []struct { + name string + input string + fields string + want []string + }{ + { + name: "simple", + input: "a,b,c", + fields: ",", + want: []string{"a", "b", "c"}, + }, + { + name: "empty", + input: "", + fields: ",", + want: []string{}, + }, + { + name: "multiple separators", + input: "a,b\nc:d", + fields: ",:\n", + want: []string{"a", "b", "c", "d"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, SplitAny(tt.input, tt.fields)) + }) + } +} diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go new file mode 100644 index 00000000000..87d0d3b03cf --- /dev/null +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -0,0 +1,182 @@ +package alpm + +import ( + "testing" + + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" + "github.com/anchore/syft/syft/source" +) + +func TestAlpmCataloger(t *testing.T) { + + expectedPkgs := []pkg.Package{ + { + Name: "gmp", + Version: "6.2.1-2", + Type: pkg.AlpmPkg, + FoundBy: "alpmdb-cataloger", + Licenses: []string{"LGPL3", "GPL"}, + Locations: source.NewLocationSet(source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")), + CPEs: nil, + PURL: "", + MetadataType: "AlpmMetadata", + Metadata: pkg.AlpmMetadata{ + BasePackage: "gmp", + Package: "gmp", + Version: "6.2.1-2", + Description: "A free library for arbitrary precision arithmetic", + Architecture: "x86_64", + Size: 1044438, + Packager: "Antonio Rojas ", + License: "LGPL3\nGPL", + URL: "https://gmplib.org/", + Validation: "pgp", + Reason: 1, + Files: []pkg.AlpmFileRecord{ + { + Path: "/usr", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/include", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/include/gmp.h", + Size: "84140", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "76595f70565c72550eb520809bf86856"}, + {Algorithm: "sha256", Value: "91a614b9202453153fe3b7512d15e89659108b93ce8841c8e13789eb85da9e3a"}, + }, + }, + { + Path: "/usr/include/gmpxx.h", + Size: "129113", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "ea3d21de4bcf7c696799c5c55dd3655b"}, + {Algorithm: "sha256", Value: "0011ae411a0bc1030e07d968b32fdc1343f5ac2a17b7d28f493e7976dde2ac82"}, + }, + }, + { + Path: "/usr/lib", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so", + Type: "link", + Link: "libgmp.so.10.4.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so.10", + Type: "link", + Link: "libgmp.so.10.4.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so.10.4.1", + Size: "663224", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "d6d03eadacdd9048d5b2adf577e9d722"}, + {Algorithm: "sha256", Value: "39898bd3d8d6785222432fa8b8aef7ce3b7e5bbfc66a52b7c0da09bed4adbe6a"}, + }, + }, + { + Path: "/usr/lib/libgmpxx.so", + Type: "link", + Link: "libgmpxx.so.4.6.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmpxx.so.4", + Type: "link", + Link: "libgmpxx.so.4.6.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmpxx.so.4.6.1", + Size: "30680", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "dd5f0c4d635fa599fa7f4339c0e8814d"}, + {Algorithm: "sha256", Value: "0ef67cbde4841f58d2e4b41f59425eb87c9eeaf4e649c060b326342c53bedbec"}, + }, + }, + { + Path: "/usr/lib/pkgconfig", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/pkgconfig/gmp.pc", + Size: "245", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "a91a9f1b66218cb77b9cd2cdf341756d"}, + {Algorithm: "sha256", Value: "4e9de547a48c4e443781e9fa702a1ec5a23ee28b4bc520306cff2541a855be37"}, + }, + }, + { + Path: "/usr/lib/pkgconfig/gmpxx.pc", + Size: "280", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "8c0f54e987934352177a6a30a811b001"}, + {Algorithm: "sha256", Value: "fc5dbfbe75977057ba50953d94b9daecf696c9fdfe5b94692b832b44ecca871b"}, + }, + }, + { + Path: "/usr/share", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/share/info", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/share/info/gmp.info-1.gz", + Size: "85892", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "63304d4d2f0247fb8a999fae66a81c19"}, + {Algorithm: "sha256", Value: "86288c1531a2789db5da8b9838b5cde4db07bda230ae11eba23a1f33698bd14e"}, + }, + }, + { + Path: "/usr/share/info/gmp.info-2.gz", + Size: "48484", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "4bb0dadec416d305232cac6eae712ff7"}, + {Algorithm: "sha256", Value: "b7443c1b529588d98a074266087f79b595657ac7274191c34b10a9ceedfa950e"}, + }, + }, + { + Path: "/usr/share/info/gmp.info.gz", + Size: "2380", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "cf6880fb0d862ee1da0d13c3831b5720"}, + {Algorithm: "sha256", Value: "a13c8eecda3f3e5ad1e09773e47a9686f07d9d494eaddf326f3696bbef1548fd"}, + }, + }, + }, + Backup: []pkg.AlpmFileRecord{}, + }, + }, + } + + // TODO: relationships are not under test yet + var expectedRelationships []artifact.Relationship + + pkgtest.NewCatalogTester(). + FromDirectory(t, "test-fixtures/gmp-fixture"). + WithCompareOptions(cmpopts.IgnoreFields(pkg.AlpmFileRecord{}, "Time")). + Expects(expectedPkgs, expectedRelationships). + TestCataloger(t, NewAlpmdbCataloger()) + +} diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index ac9ac1b6592..1a5bdf13135 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -1,9 +1,8 @@ package alpm import ( - "strings" - "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -15,7 +14,7 @@ func newPackage(m pkg.AlpmMetadata, release *linux.Release, locations ...source. Version: m.Version, Locations: source.NewLocationSet(locations...), Type: pkg.AlpmPkg, - Licenses: strings.Split(m.License, " "), + Licenses: internal.SplitAny(m.License, " \n"), PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, Metadata: m, diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 5bc776a4443..9de9620b429 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -65,6 +65,10 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader metadata.Backup = filesMetadata.Backup } + if metadata.Package == "" { + return nil, nil, nil + } + return []pkg.Package{ newPackage(*metadata, env.LinuxRelease, reader.Location), }, nil, nil diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db_test.go b/syft/pkg/cataloger/alpm/parse_alpm_db_test.go index 9debc22a6d2..dcb458e40f9 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db_test.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db_test.go @@ -16,10 +16,12 @@ import ( func TestDatabaseParser(t *testing.T) { tests := []struct { name string + fixture string expected pkg.AlpmMetadata }{ { - name: "test alpm database parsing", + name: "test alpm database parsing", + fixture: "test-fixtures/files", expected: pkg.AlpmMetadata{ Backup: []pkg.AlpmFileRecord{ { diff --git a/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc b/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc new file mode 100644 index 00000000000..39ff43a9e50 --- /dev/null +++ b/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc @@ -0,0 +1,44 @@ +%NAME% +gmp + +%VERSION% +6.2.1-2 + +%BASE% +gmp + +%DESC% +A free library for arbitrary precision arithmetic + +%URL% +https://gmplib.org/ + +%ARCH% +x86_64 + +%BUILDDATE% +1653121258 + +%INSTALLDATE% +1665878640 + +%PACKAGER% +Antonio Rojas + +%SIZE% +1044438 + +%REASON% +1 + +%LICENSE% +LGPL3 +GPL + +%VALIDATION% +pgp + +%DEPENDS% +gcc-libs +sh + diff --git a/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files b/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files new file mode 100644 index 00000000000..737b8dae42e --- /dev/null +++ b/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files @@ -0,0 +1,21 @@ +%FILES% +usr/ +usr/include/ +usr/include/gmp.h +usr/include/gmpxx.h +usr/lib/ +usr/lib/libgmp.so +usr/lib/libgmp.so.10 +usr/lib/libgmp.so.10.4.1 +usr/lib/libgmpxx.so +usr/lib/libgmpxx.so.4 +usr/lib/libgmpxx.so.4.6.1 +usr/lib/pkgconfig/ +usr/lib/pkgconfig/gmp.pc +usr/lib/pkgconfig/gmpxx.pc +usr/share/ +usr/share/info/ +usr/share/info/gmp.info-1.gz +usr/share/info/gmp.info-2.gz +usr/share/info/gmp.info.gz + diff --git a/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/mtree b/syft/pkg/cataloger/alpm/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/mtree new file mode 100644 index 0000000000000000000000000000000000000000..fe9d46ef34e1b3c2cdfb68aff87e2dddde4d7d5c GIT binary patch literal 1038 zcmV+p1o8VHiwFP!000001Fcp~k6Skkyzj3t&~v?_C`#nPm!fD9v}k+j50FU8F|uD+ z?*eIlefgYiYA5SW7RbR54-h4fhQpcn!}Y_x`^DwnA65_l-1%niu6*^lvrSrU56|mc z^Gz+~Vtu*(>F+nc|NQ2UU;bP@?5=$Sl@Kyy;&oc>cOQIH3dri($aY)bKWxf$;GS%B z02aUrtu*C&=sYX7!ps!!FON>?Hob#y|MJPaB;l+iKA0n>{@GwN+bow#VpI*6;* z(&o?w;a|V~`Vu~*bPP|3NDbRm5KUcXF4a^fg6GL{3eT;z;R2EiP0X${Rquh4IyZ2d z7NFSDj2g|9n1u%9T1RS}ML&{!ptBJL*vI|-GdK@ju-*Mp`A7Dn{Bn1rSC8h;Z2Rxr z|M#ElxZGaft>2!bg$tBpVp=7k*%DRKmWh%N%88SV$(5;|5+l%SG)Zy_W)KLc<7fog zfSe`Hg2Y2mo#T%*I*N_O2a|Pi>|Y{%|NcBy$O3>zV7;SdFfT)A3tiPFq7*_h!%8$x z!KMVzy~HC8g@nmdGpV7;91R34IH_yy&_=V&vj}>VR?R$j<$T=5)}EkW?Zzuw64lFZ z>B#=}oC2uM&cu+hlf zCbi~bE$7oNps?^TG`8B9a-0?XcOKk5nz-o4X^gOm+9{8w$$F^It#lh+`xnqdG2hPR zp*XHtpW)zX?K>W|tbLVFOqGu((2Qo%Vijq25}Cct5}v3jh6sv%CTgFx>4+JK0h`2@ zV;Yt>DVbu>nu>d$#cPQZY6`Je4D%!mN{8#SUb9@}?%h^zZ{}|MN_l_UckrD%JvYo@ z7)O`x;UqF))S7{Hgx!i-R-0I$#br+~T|!t+rS-yEyp%8%@P()p)9Xq-qkHBNz7XaW z1nZoc3Um%raBSz`M%!Khe9|S>G%0X!lQ0g-&|0q@Q)hq?PIC%)D%qIB8%YaqjTECK zgeKvg)+0~CyCQsShV-S>NEXICN^#XCVa5~SIP?BB32>+ncb(78$JaLK zvk5{h`G}c{n2Ht4GeRN4nY%0^_LygeNZV6pxMpUJ4T*vSURCwCOzjDN_ I!7d5_0Aphcg#Z8m literal 0 HcmV?d00001 diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index b5a5be32128..f83b7bb521f 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -350,19 +351,12 @@ func discoverPackageDependencies(pkgs []pkg.Package) (relationships []artifact.R return relationships } -func splitAny(s string, seps string) []string { - splitter := func(r rune) bool { - return strings.ContainsRune(seps, r) - } - return strings.FieldsFunc(s, splitter) -} - func stripVersionSpecifier(s string) string { // examples: // musl>=1 --> musl // cmd:scanelf=1.3.4-r0 --> cmd:scanelf - items := splitAny(s, "<>=") + items := internal.SplitAny(s, "<>=") if len(items) == 0 { return s } diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 59a5d2d52bd..51404ffd91d 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -129,6 +129,11 @@ func (p *CatalogTester) IgnorePackageFields(fields ...string) *CatalogTester { return p } +func (p *CatalogTester) WithCompareOptions(opts ...cmp.Option) *CatalogTester { + p.compareOptions = append(p.compareOptions, opts...) + return p +} + func (p *CatalogTester) Expects(pkgs []pkg.Package, relationships []artifact.Relationship) *CatalogTester { p.expectedPkgs = pkgs p.expectedRelationships = relationships From e69d1b9f6142b88f443dd9168d801f5900dcc4f3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 09:26:52 -0500 Subject: [PATCH 03/54] fix import sorting for binary cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/binary/classifier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/binary/classifier_test.go b/syft/pkg/cataloger/binary/classifier_test.go index 8062713224e..c21aa20bf5d 100644 --- a/syft/pkg/cataloger/binary/classifier_test.go +++ b/syft/pkg/cataloger/binary/classifier_test.go @@ -1,12 +1,12 @@ package binary import ( - "github.com/anchore/syft/syft/pkg/cataloger/generic" "testing" "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/source" ) From f123b6b3407b74291cfcd6a7d39b77b78a794972 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 09:27:18 -0500 Subject: [PATCH 04/54] fix linting for mock resolver Signed-off-by: Alex Goodman --- syft/source/mock_resolver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syft/source/mock_resolver.go b/syft/source/mock_resolver.go index 0c0def5c0c5..9e44abc200d 100644 --- a/syft/source/mock_resolver.go +++ b/syft/source/mock_resolver.go @@ -183,7 +183,6 @@ func (r MockResolver) FilesByMIMEType(types ...string) ([]Location, error) { func (r MockResolver) FilesByExtension(extension string) ([]Location, error) { return r.extension[extension], nil - } func (r MockResolver) FilesByBasename(filename string) ([]Location, error) { From e35245b7711058b1d1b15058e71fa40d32861901 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 09:27:40 -0500 Subject: [PATCH 05/54] separate portage cataloger parser impl from cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/portage/cataloger.go | 149 ----------------- .../portage/parse_portage_contents.go | 154 ++++++++++++++++++ 2 files changed, 154 insertions(+), 149 deletions(-) create mode 100644 syft/pkg/cataloger/portage/parse_portage_contents.go diff --git a/syft/pkg/cataloger/portage/cataloger.go b/syft/pkg/cataloger/portage/cataloger.go index eff4e7ccd89..9ee8328d724 100644 --- a/syft/pkg/cataloger/portage/cataloger.go +++ b/syft/pkg/cataloger/portage/cataloger.go @@ -4,159 +4,10 @@ Package portage provides a concrete Cataloger implementation for Gentoo Portage. package portage import ( - "bufio" - "fmt" - "path" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" - "github.com/anchore/syft/syft/source" -) - -var ( - cpvRe = regexp.MustCompile(`/([^/]*/[\w+][\w+-]*)-((\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)(-r\d+)?)/CONTENTS$`) - _ generic.Parser = parsePortageContents ) func NewPortageCataloger() *generic.Cataloger { return generic.NewCataloger("portage-cataloger"). WithParser(parsePortageContents, generic.NewSearch().ByBasename("CONTENTS").MustMatchGlob("**/var/db/pkg/*/*/CONTENTS")) } - -func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - cpvMatch := cpvRe.FindStringSubmatch(reader.Location.RealPath) - if cpvMatch == nil { - return nil, nil, fmt.Errorf("failed to match package and version in %s", reader.Location.RealPath) - } - - name, version := cpvMatch[1], cpvMatch[2] - if name == "" || version == "" { - log.WithFields("path", reader.Location.RealPath).Warnf("failed to parse portage name and version") - return nil, nil, nil - } - - p := pkg.Package{ - Name: name, - Version: version, - PURL: packageURL(name, version), - Locations: source.NewLocationSet(), - Type: pkg.PortagePkg, - MetadataType: pkg.PortageMetadataType, - Metadata: pkg.PortageMetadata{ - // ensure the default value for a collection is never nil since this may be shown as JSON - Files: make([]pkg.PortageFileRecord, 0), - }, - } - addLicenses(resolver, reader.Location, &p) - addSize(resolver, reader.Location, &p) - addFiles(resolver, reader.Location, &p) - - p.SetID() - - return []pkg.Package{p}, nil, nil -} - -func addFiles(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { - contentsReader, err := resolver.FileContentsByLocation(dbLocation) - if err != nil { - log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage contents (package=%s): %+v", p.Name, err) - return - } - - entry, ok := p.Metadata.(pkg.PortageMetadata) - if !ok { - return - } - - scanner := bufio.NewScanner(contentsReader) - for scanner.Scan() { - line := strings.Trim(scanner.Text(), "\n") - fields := strings.Split(line, " ") - - if fields[0] == "obj" { - record := pkg.PortageFileRecord{ - Path: fields[1], - } - record.Digest = &file.Digest{ - Algorithm: "md5", - Value: fields[2], - } - entry.Files = append(entry.Files, record) - } - } - - p.Metadata = entry - p.Locations.Add(dbLocation) -} - -func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { - parentPath := filepath.Dir(dbLocation.RealPath) - - location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) - - if location == nil { - return - } - - licenseReader, err := resolver.FileContentsByLocation(*location) - if err != nil { - log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) - return - } - - findings := internal.NewStringSet() - scanner := bufio.NewScanner(licenseReader) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - token := scanner.Text() - if token != "||" && token != "(" && token != ")" { - findings.Add(token) - } - } - licenses := findings.ToSlice() - sort.Strings(licenses) - p.Licenses = licenses - p.Locations.Add(*location) -} - -func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { - parentPath := filepath.Dir(dbLocation.RealPath) - - location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "SIZE")) - - if location == nil { - return - } - - entry, ok := p.Metadata.(pkg.PortageMetadata) - if !ok { - return - } - - sizeReader, err := resolver.FileContentsByLocation(*location) - if err != nil { - log.WithFields("name", p.Name).Warnf("failed to fetch portage SIZE: %+v", err) - return - } - - scanner := bufio.NewScanner(sizeReader) - for scanner.Scan() { - line := strings.Trim(scanner.Text(), "\n") - size, err := strconv.Atoi(line) - if err == nil { - entry.InstalledSize = size - } - } - - p.Metadata = entry - p.Locations.Add(*location) -} diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go new file mode 100644 index 00000000000..dba65ace0dd --- /dev/null +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -0,0 +1,154 @@ +package portage + +import ( + "bufio" + "fmt" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/source" +) + +var ( + cpvRe = regexp.MustCompile(`/([^/]*/[\w+][\w+-]*)-((\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)(-r\d+)?)/CONTENTS$`) + _ generic.Parser = parsePortageContents +) + +func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + cpvMatch := cpvRe.FindStringSubmatch(reader.Location.RealPath) + if cpvMatch == nil { + return nil, nil, fmt.Errorf("failed to match package and version in %s", reader.Location.RealPath) + } + + name, version := cpvMatch[1], cpvMatch[2] + if name == "" || version == "" { + log.WithFields("path", reader.Location.RealPath).Warnf("failed to parse portage name and version") + return nil, nil, nil + } + + p := pkg.Package{ + Name: name, + Version: version, + PURL: packageURL(name, version), + Locations: source.NewLocationSet(), + Type: pkg.PortagePkg, + MetadataType: pkg.PortageMetadataType, + Metadata: pkg.PortageMetadata{ + // ensure the default value for a collection is never nil since this may be shown as JSON + Files: make([]pkg.PortageFileRecord, 0), + }, + } + addLicenses(resolver, reader.Location, &p) + addSize(resolver, reader.Location, &p) + addFiles(resolver, reader.Location, &p) + + p.SetID() + + return []pkg.Package{p}, nil, nil +} + +func addFiles(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + contentsReader, err := resolver.FileContentsByLocation(dbLocation) + if err != nil { + log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage contents (package=%s): %+v", p.Name, err) + return + } + + entry, ok := p.Metadata.(pkg.PortageMetadata) + if !ok { + return + } + + scanner := bufio.NewScanner(contentsReader) + for scanner.Scan() { + line := strings.Trim(scanner.Text(), "\n") + fields := strings.Split(line, " ") + + if fields[0] == "obj" { + record := pkg.PortageFileRecord{ + Path: fields[1], + } + record.Digest = &file.Digest{ + Algorithm: "md5", + Value: fields[2], + } + entry.Files = append(entry.Files, record) + } + } + + p.Metadata = entry + p.Locations.Add(dbLocation) +} + +func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + parentPath := filepath.Dir(dbLocation.RealPath) + + location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) + + if location == nil { + return + } + + licenseReader, err := resolver.FileContentsByLocation(*location) + if err != nil { + log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) + return + } + + findings := internal.NewStringSet() + scanner := bufio.NewScanner(licenseReader) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + token := scanner.Text() + if token != "||" && token != "(" && token != ")" { + findings.Add(token) + } + } + licenses := findings.ToSlice() + sort.Strings(licenses) + p.Licenses = licenses + p.Locations.Add(*location) +} + +func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + parentPath := filepath.Dir(dbLocation.RealPath) + + location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "SIZE")) + + if location == nil { + return + } + + entry, ok := p.Metadata.(pkg.PortageMetadata) + if !ok { + return + } + + sizeReader, err := resolver.FileContentsByLocation(*location) + if err != nil { + log.WithFields("name", p.Name).Warnf("failed to fetch portage SIZE: %+v", err) + return + } + + scanner := bufio.NewScanner(sizeReader) + for scanner.Scan() { + line := strings.Trim(scanner.Text(), "\n") + size, err := strconv.Atoi(line) + if err == nil { + entry.InstalledSize = size + } + } + + p.Metadata = entry + p.Locations.Add(*location) +} From fc2c84641c864bfa018bb2f76b7c21b97a45b1ef Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:52:30 -0500 Subject: [PATCH 06/54] enhance cataloger pkgtest utils to account for resolver responses Signed-off-by: Alex Goodman --- .../internal/pkgtest/observing_resolver.go | 229 ++++++++++++++++++ .../internal/pkgtest/test_generic_parser.go | 129 ++++++++-- 2 files changed, 343 insertions(+), 15 deletions(-) create mode 100644 syft/pkg/cataloger/internal/pkgtest/observing_resolver.go diff --git a/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go b/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go new file mode 100644 index 00000000000..79102d7c351 --- /dev/null +++ b/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go @@ -0,0 +1,229 @@ +package pkgtest + +import ( + "fmt" + "io" + "sort" + + "github.com/anchore/syft/syft/source" +) + +var _ source.FileResolver = (*observingResolver)(nil) + +type observingResolver struct { + decorated source.FileResolver + pathQuery []source.Location + contentQuery []source.Location + emptyPathQueryResults map[string][]string +} + +func newObservingResolver(resolver source.FileResolver) *observingResolver { + return &observingResolver{ + decorated: resolver, + pathQuery: make([]source.Location, 0), + emptyPathQueryResults: make(map[string][]string), + } +} + +// testing helpers... + +//nolint:unused +func (r *observingResolver) observedQuery(path string) bool { + return r.observedPathQuery(path) || r.observedContentQuery(path) +} + +func (r *observingResolver) observedPathQuery(path string) bool { + for _, loc := range r.pathQuery { + if loc.RealPath == path { + return true + } + } + return false +} + +//nolint:unused +func (r *observingResolver) observedContentQuery(path string) bool { + for _, loc := range r.pathQuery { + if loc.RealPath == path { + return true + } + } + return false +} + +func (r *observingResolver) observedContentQueries() []string { + var observed []string + for _, loc := range r.pathQuery { + observed = append(observed, loc.RealPath) + } + return observed +} + +func (r *observingResolver) pruneUnfulfilledPathQueries(ignore map[string][]string, ignorePaths ...string) { + if ignore == nil { + return + } + // remove any paths that were ignored for specific calls + for k, v := range ignore { + results := r.emptyPathQueryResults[k] + for _, ig := range v { + for i, result := range results { + if result == ig { + results = append(results[:i], results[i+1:]...) + break + } + } + } + if len(results) > 0 { + r.emptyPathQueryResults[k] = results + } else { + delete(r.emptyPathQueryResults, k) + } + } + + // remove any paths that were ignored for all calls + for _, ig := range ignorePaths { + for k, v := range r.emptyPathQueryResults { + for i, result := range v { + if result == ig { + v = append(v[:i], v[i+1:]...) + break + } + } + if len(v) > 0 { + r.emptyPathQueryResults[k] = v + } else { + delete(r.emptyPathQueryResults, k) + } + } + } +} + +func (r *observingResolver) hasUnfulfilledPathRequests() bool { + return len(r.emptyPathQueryResults) > 0 +} + +func (r *observingResolver) prettyUnfulfilledPathRequests() string { + var res string + var keys []string + + for k := range r.emptyPathQueryResults { + keys = append(keys, k) + } + + sort.Strings(keys) + + for _, k := range keys { + res += fmt.Sprintf(" %s: %+v\n", k, r.emptyPathQueryResults[k]) + } + return res +} + +// For the file path resolver... + +func (r *observingResolver) FilesByPath(paths ...string) ([]source.Location, error) { + locs, err := r.decorated.FilesByPath(paths...) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByPath" + results := r.emptyPathQueryResults[key] + results = append(results, paths...) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) FilesByGlob(patterns ...string) ([]source.Location, error) { + locs, err := r.decorated.FilesByGlob(patterns...) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByGlob" + results := r.emptyPathQueryResults[key] + results = append(results, patterns...) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) FilesByExtension(extension string) ([]source.Location, error) { + locs, err := r.decorated.FilesByExtension(extension) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByExtension" + results := r.emptyPathQueryResults[key] + results = append(results, extension) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) FilesByBasename(filename string) ([]source.Location, error) { + locs, err := r.decorated.FilesByBasename(filename) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByBasename" + results := r.emptyPathQueryResults[key] + results = append(results, filename) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { + locs, err := r.decorated.FilesByBasenameGlob(glob) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByBasenameGlob" + results := r.emptyPathQueryResults[key] + results = append(results, glob) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { + locs, err := r.decorated.FilesByMIMEType(types...) + r.pathQuery = append(r.pathQuery, locs...) + if len(locs) == 0 { + key := "FilesByMIMEType" + results := r.emptyPathQueryResults[key] + results = append(results, types...) + r.emptyPathQueryResults[key] = results + } + return locs, err +} + +func (r *observingResolver) RelativeFileByPath(l source.Location, path string) *source.Location { + loc := r.decorated.RelativeFileByPath(l, path) + if loc != nil { + r.pathQuery = append(r.pathQuery, *loc) + } else { + key := "RelativeFileByPath" + results := r.emptyPathQueryResults[key] + results = append(results, path) + r.emptyPathQueryResults[key] = results + } + return loc +} + +// For the content resolver methods... + +func (r *observingResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { + reader, err := r.decorated.FileContentsByLocation(location) + r.contentQuery = append(r.contentQuery, location) + return reader, err +} + +// For the remaining resolver methods... + +func (r *observingResolver) AllLocations() <-chan source.Location { + return r.decorated.AllLocations() +} + +func (r *observingResolver) HasPath(s string) bool { + return r.decorated.HasPath(s) +} + +func (r *observingResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { + return r.decorated.FileMetadataByLocation(location) +} diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 51404ffd91d..f92fd358114 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -1,6 +1,7 @@ package pkgtest import ( + "fmt" "io" "os" "strings" @@ -8,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/imagetest" @@ -21,20 +23,35 @@ import ( type locationComparer func(x, y source.Location) bool type CatalogTester struct { - expectedPkgs []pkg.Package - expectedRelationships []artifact.Relationship - env *generic.Environment - reader source.LocationReadCloser - resolver source.FileResolver - wantErr require.ErrorAssertionFunc - compareOptions []cmp.Option - locationComparer locationComparer + expectedPkgs []pkg.Package + expectedRelationships []artifact.Relationship + assertResultExpectations bool + expectedPathQueries []string // this is a minimum set, the resolver may return more that just this list + expectedContentQueries []string // this is a full set, any other queries are unexpected (and will fail the test) + ignoreUnfulfilledQueries map[string][]string + ignoreUnfulfilledPaths []string + env *generic.Environment + reader source.LocationReadCloser + resolver source.FileResolver + wantErr require.ErrorAssertionFunc + compareOptions []cmp.Option + locationComparer locationComparer } func NewCatalogTester() *CatalogTester { return &CatalogTester{ wantErr: require.NoError, locationComparer: DefaultLocationComparer, + ignoreUnfulfilledQueries: map[string][]string{ + "FilesByPath": { + // most catalogers search for a linux release, which will not be fulfilled in testing + "/etc/os-release", + "/usr/lib/os-release", + "/etc/system-release-cpe", + "/etc/redhat-release", + "/bin/busybox", + }, + }, } } @@ -90,6 +107,7 @@ func (p *CatalogTester) WithEnv(env *generic.Environment) *CatalogTester { } func (p *CatalogTester) WithError() *CatalogTester { + p.assertResultExpectations = true p.wantErr = require.Error return p } @@ -135,11 +153,27 @@ func (p *CatalogTester) WithCompareOptions(opts ...cmp.Option) *CatalogTester { } func (p *CatalogTester) Expects(pkgs []pkg.Package, relationships []artifact.Relationship) *CatalogTester { + p.assertResultExpectations = true p.expectedPkgs = pkgs p.expectedRelationships = relationships return p } +func (p *CatalogTester) ExpectsPathQueries(locations []string) *CatalogTester { + p.expectedPathQueries = locations + return p +} + +func (p *CatalogTester) ExpectsContentQueries(locations []string) *CatalogTester { + p.expectedContentQueries = locations + return p +} + +func (p *CatalogTester) IgnoreUnfulfilledContentQueries(paths ...string) *CatalogTester { + p.ignoreUnfulfilledPaths = append(p.ignoreUnfulfilledPaths, paths...) + return p +} + func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) { t.Helper() pkgs, relationships, err := parser(p.resolver, p.env, p.reader) @@ -149,9 +183,30 @@ func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) { func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) { t.Helper() - pkgs, relationships, err := cataloger.Catalog(p.resolver) - p.wantErr(t, err) - p.assertPkgs(t, pkgs, relationships) + + resolver := newObservingResolver(p.resolver) + + pkgs, relationships, err := cataloger.Catalog(resolver) + + // this is a minimum set, the resolver may return more that just this list + for _, path := range p.expectedPathQueries { + assert.Truef(t, resolver.observedPathQuery(path), "expected path query for %q was not observed", path) + } + + // this is a full set, any other queries are unexpected (and will fail the test) + if len(p.expectedContentQueries) > 0 { + assert.ElementsMatchf(t, p.expectedContentQueries, resolver.observedContentQueries(), "unexpected content queries observed: diff %s", cmp.Diff(p.expectedContentQueries, resolver.observedContentQueries())) + } + + if p.assertResultExpectations { + p.wantErr(t, err) + p.assertPkgs(t, pkgs, relationships) + } else { + resolver.pruneUnfulfilledPathQueries(p.ignoreUnfulfilledQueries, p.ignoreUnfulfilledPaths...) + + // if we aren't testing the results, we should focus on what was searched for (for glob-centric tests) + assert.Falsef(t, resolver.hasUnfulfilledPathRequests(), "unfulfilled path requests: \n%v", resolver.prettyUnfulfilledPathRequests()) + } } func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) { @@ -180,12 +235,31 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi ), ) - if diff := cmp.Diff(p.expectedPkgs, pkgs, p.compareOptions...); diff != "" { - t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff) + { + var r diffReporter + var opts []cmp.Option + + opts = append(opts, p.compareOptions...) + opts = append(opts, cmp.Reporter(&r)) + + if diff := cmp.Diff(p.expectedPkgs, pkgs, opts...); diff != "" { + t.Log("Specific Differences:\n" + r.String()) + t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff) + } } - if diff := cmp.Diff(p.expectedRelationships, relationships, p.compareOptions...); diff != "" { - t.Errorf("unexpected relationships from parsing (-expected +actual)\n%s", diff) + { + var r diffReporter + var opts []cmp.Option + + opts = append(opts, p.compareOptions...) + opts = append(opts, cmp.Reporter(&r)) + + if diff := cmp.Diff(p.expectedRelationships, relationships, opts...); diff != "" { + t.Log("Specific Differences:\n" + r.String()) + + t.Errorf("unexpected relationships from parsing (-expected +actual)\n%s", diff) + } } } @@ -228,3 +302,28 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) { t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff) } } + +// diffReporter is a simple custom reporter that only records differences detected during comparison. +type diffReporter struct { + path cmp.Path + diffs []string +} + +func (r *diffReporter) PushStep(ps cmp.PathStep) { + r.path = append(r.path, ps) +} + +func (r *diffReporter) Report(rs cmp.Result) { + if !rs.Equal() { + vx, vy := r.path.Last().Values() + r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy)) + } +} + +func (r *diffReporter) PopStep() { + r.path = r.path[:len(r.path)-1] +} + +func (r *diffReporter) String() string { + return strings.Join(r.diffs, "\n") +} From e9ac344d59b4ba9ce8c907cebf193c5fb840902a Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:53:05 -0500 Subject: [PATCH 07/54] add glob-based cataloger tests for alpm cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/alpm/cataloger_test.go | 27 +++++++++++++++++++ .../var/lib/pacman/local/base-1.0/desc | 1 + .../var/lib/pacman/local/base-1.0/files | 1 + .../var/lib/pacman/local/dive-0.10.0/desc | 1 + 4 files changed, 30 insertions(+) create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/desc create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/files create mode 100644 syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/dive-0.10.0/desc diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index 87d0d3b03cf..b570e23f152 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -180,3 +180,30 @@ func TestAlpmCataloger(t *testing.T) { TestCataloger(t, NewAlpmdbCataloger()) } + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain description files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "var/lib/pacman/local/base-1.0/desc", + "var/lib/pacman/local/dive-0.10.0/desc", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + IgnoreUnfulfilledContentQueries("var/lib/pacman/local/base-1.0/mtree", "var/lib/pacman/local/dive-0.10.0/mtree"). + TestCataloger(t, NewAlpmdbCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/desc b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/desc new file mode 100644 index 00000000000..34ca538d2b8 --- /dev/null +++ b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/desc @@ -0,0 +1 @@ +bogus desc file \ No newline at end of file diff --git a/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/files b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/files new file mode 100644 index 00000000000..eb016bb551f --- /dev/null +++ b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/base-1.0/files @@ -0,0 +1 @@ +bogus files \ No newline at end of file diff --git a/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/dive-0.10.0/desc b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/dive-0.10.0/desc new file mode 100644 index 00000000000..34ca538d2b8 --- /dev/null +++ b/syft/pkg/cataloger/alpm/test-fixtures/glob-paths/var/lib/pacman/local/dive-0.10.0/desc @@ -0,0 +1 @@ +bogus desc file \ No newline at end of file From 67b131c63e05960ccdc3d72361233de0f743f21e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:53:25 -0500 Subject: [PATCH 08/54] add glob-based cataloger tests for apkdb cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/apkdb/cataloger_test.go | 30 +++++++++++++++++++ .../glob-paths/lib/apk/db/installed | 1 + 2 files changed, 31 insertions(+) create mode 100644 syft/pkg/cataloger/apkdb/cataloger_test.go create mode 100644 syft/pkg/cataloger/apkdb/test-fixtures/glob-paths/lib/apk/db/installed diff --git a/syft/pkg/cataloger/apkdb/cataloger_test.go b/syft/pkg/cataloger/apkdb/cataloger_test.go new file mode 100644 index 00000000000..15af869d051 --- /dev/null +++ b/syft/pkg/cataloger/apkdb/cataloger_test.go @@ -0,0 +1,30 @@ +package apkdb + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain DB files", + fixture: "test-fixtures/glob-paths", + expected: []string{"lib/apk/db/installed"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewApkdbCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/apkdb/test-fixtures/glob-paths/lib/apk/db/installed b/syft/pkg/cataloger/apkdb/test-fixtures/glob-paths/lib/apk/db/installed new file mode 100644 index 00000000000..616203752e6 --- /dev/null +++ b/syft/pkg/cataloger/apkdb/test-fixtures/glob-paths/lib/apk/db/installed @@ -0,0 +1 @@ +bogus db contents \ No newline at end of file From 33a902a57ba8d1c0a53338bb5ec61fccf7e6df83 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:54:01 -0500 Subject: [PATCH 09/54] add glob-based cataloger tests for dpkg cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/deb/cataloger_test.go | 26 +++++++++++++++++++ syft/pkg/cataloger/deb/package.go | 4 +-- .../glob-paths/var/lib/dpkg/status | 1 + .../glob-paths/var/lib/dpkg/status.d/pkg-1.0 | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status create mode 100644 syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status.d/pkg-1.0 diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 7e627b1c9a7..b87bf126a01 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -81,3 +81,29 @@ func TestDpkgCataloger(t *testing.T) { Expects(expected, nil). TestCataloger(t, c) } + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain db status files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "var/lib/dpkg/status", + "var/lib/dpkg/status.d/pkg-1.0", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewDpkgdbCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index 8ef890b1719..3f10dbe3fe1 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -83,7 +83,7 @@ func packageURL(m pkg.DpkgMetadata, distro *linux.Release) string { func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { metadata, ok := p.Metadata.(pkg.DpkgMetadata) if !ok { - log.WithFields("package", p.String()).Warn("unable to extract DPKG metadata to add licenses") + log.WithFields("package", p).Warn("unable to extract DPKG metadata to add licenses") return } @@ -103,7 +103,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk func mergeFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { metadata, ok := p.Metadata.(pkg.DpkgMetadata) if !ok { - log.WithFields("package", p.String()).Warn("unable to extract DPKG metadata to file listing") + log.WithFields("package", p).Warn("unable to extract DPKG metadata to file listing") return } diff --git a/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status b/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status new file mode 100644 index 00000000000..90786794c1e --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status @@ -0,0 +1 @@ +bogus status \ No newline at end of file diff --git a/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status.d/pkg-1.0 b/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status.d/pkg-1.0 new file mode 100644 index 00000000000..b09abcf2897 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/glob-paths/var/lib/dpkg/status.d/pkg-1.0 @@ -0,0 +1 @@ +bogus package \ No newline at end of file From 3ea801db6665bc1bb2f641e75ccad52026f823f3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:54:19 -0500 Subject: [PATCH 10/54] add glob-based cataloger tests for cpp cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/cpp/cataloger_test.go | 33 +++++++++++++++++++ .../glob-paths/somewhere/src/conan.lock | 1 + .../glob-paths/somewhere/src/conanfile.txt | 1 + 3 files changed, 35 insertions(+) create mode 100644 syft/pkg/cataloger/cpp/cataloger_test.go create mode 100644 syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conan.lock create mode 100644 syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conanfile.txt diff --git a/syft/pkg/cataloger/cpp/cataloger_test.go b/syft/pkg/cataloger/cpp/cataloger_test.go new file mode 100644 index 00000000000..8a1f921d842 --- /dev/null +++ b/syft/pkg/cataloger/cpp/cataloger_test.go @@ -0,0 +1,33 @@ +package cpp + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain conan files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "somewhere/src/conanfile.txt", + "somewhere/src/conan.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewConanCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conan.lock b/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conan.lock new file mode 100644 index 00000000000..1068548a948 --- /dev/null +++ b/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conan.lock @@ -0,0 +1 @@ +bogus conan.lock \ No newline at end of file diff --git a/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conanfile.txt b/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conanfile.txt new file mode 100644 index 00000000000..7159f327782 --- /dev/null +++ b/syft/pkg/cataloger/cpp/test-fixtures/glob-paths/somewhere/src/conanfile.txt @@ -0,0 +1 @@ +bogus conan file \ No newline at end of file From 46d9f6e99f3f5116a9c7a3a0303b1397841f2cd6 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:54:36 -0500 Subject: [PATCH 11/54] add glob-based cataloger tests for dart cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/dart/cataloger_test.go | 32 +++++++++++++++++++ .../test-fixtures/glob-paths/src/pubspec.lock | 1 + 2 files changed, 33 insertions(+) create mode 100644 syft/pkg/cataloger/dart/cataloger_test.go create mode 100644 syft/pkg/cataloger/dart/test-fixtures/glob-paths/src/pubspec.lock diff --git a/syft/pkg/cataloger/dart/cataloger_test.go b/syft/pkg/cataloger/dart/cataloger_test.go new file mode 100644 index 00000000000..ce72f5d4cc4 --- /dev/null +++ b/syft/pkg/cataloger/dart/cataloger_test.go @@ -0,0 +1,32 @@ +package dart + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain pubspec files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/pubspec.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewPubspecLockCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/dart/test-fixtures/glob-paths/src/pubspec.lock b/syft/pkg/cataloger/dart/test-fixtures/glob-paths/src/pubspec.lock new file mode 100644 index 00000000000..bf061c80b28 --- /dev/null +++ b/syft/pkg/cataloger/dart/test-fixtures/glob-paths/src/pubspec.lock @@ -0,0 +1 @@ +bogus pubspec.lock \ No newline at end of file From a70bef6c0db8b3ba3870722f7867fcb9e810fb41 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:54:53 -0500 Subject: [PATCH 12/54] add glob-based cataloger tests for dotnet cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/dotnet/cataloger_test.go | 32 +++++++++++++++++++ .../glob-paths/src/something.deps.json | 1 + 2 files changed, 33 insertions(+) create mode 100644 syft/pkg/cataloger/dotnet/cataloger_test.go create mode 100644 syft/pkg/cataloger/dotnet/test-fixtures/glob-paths/src/something.deps.json diff --git a/syft/pkg/cataloger/dotnet/cataloger_test.go b/syft/pkg/cataloger/dotnet/cataloger_test.go new file mode 100644 index 00000000000..c9175244a21 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/cataloger_test.go @@ -0,0 +1,32 @@ +package dotnet + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain deps.json files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/something.deps.json", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewDotnetDepsCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/dotnet/test-fixtures/glob-paths/src/something.deps.json b/syft/pkg/cataloger/dotnet/test-fixtures/glob-paths/src/something.deps.json new file mode 100644 index 00000000000..8bdc51dca4f --- /dev/null +++ b/syft/pkg/cataloger/dotnet/test-fixtures/glob-paths/src/something.deps.json @@ -0,0 +1 @@ +bogus deps.json \ No newline at end of file From 9d41e5b23a6dd1f5de6a3757d9093f6b1b5832ae Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:55:11 -0500 Subject: [PATCH 13/54] add glob-based cataloger tests for elixir cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/elixir/cataloger_test.go | 32 +++++++++++++++++++ .../test-fixtures/glob-paths/src/mix.lock | 1 + 2 files changed, 33 insertions(+) create mode 100644 syft/pkg/cataloger/elixir/cataloger_test.go create mode 100644 syft/pkg/cataloger/elixir/test-fixtures/glob-paths/src/mix.lock diff --git a/syft/pkg/cataloger/elixir/cataloger_test.go b/syft/pkg/cataloger/elixir/cataloger_test.go new file mode 100644 index 00000000000..d2a13459af6 --- /dev/null +++ b/syft/pkg/cataloger/elixir/cataloger_test.go @@ -0,0 +1,32 @@ +package elixir + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain mix.lock files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/mix.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewMixLockCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/elixir/test-fixtures/glob-paths/src/mix.lock b/syft/pkg/cataloger/elixir/test-fixtures/glob-paths/src/mix.lock new file mode 100644 index 00000000000..52d9b1e433e --- /dev/null +++ b/syft/pkg/cataloger/elixir/test-fixtures/glob-paths/src/mix.lock @@ -0,0 +1 @@ +bogus mix.lock \ No newline at end of file From 8d25ead7e449551b3f810842760496bc796915ee Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:55:27 -0500 Subject: [PATCH 14/54] add glob-based cataloger tests for erlang cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/erlang/cataloger_test.go | 32 +++++++++++++++++++ .../test-fixtures/glob-paths/src/rebar.lock | 1 + 2 files changed, 33 insertions(+) create mode 100644 syft/pkg/cataloger/erlang/cataloger_test.go create mode 100644 syft/pkg/cataloger/erlang/test-fixtures/glob-paths/src/rebar.lock diff --git a/syft/pkg/cataloger/erlang/cataloger_test.go b/syft/pkg/cataloger/erlang/cataloger_test.go new file mode 100644 index 00000000000..2abe3eb68b3 --- /dev/null +++ b/syft/pkg/cataloger/erlang/cataloger_test.go @@ -0,0 +1,32 @@ +package erlang + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain rebar.lock files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/rebar.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewRebarLockCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/erlang/test-fixtures/glob-paths/src/rebar.lock b/syft/pkg/cataloger/erlang/test-fixtures/glob-paths/src/rebar.lock new file mode 100644 index 00000000000..68baa46d48c --- /dev/null +++ b/syft/pkg/cataloger/erlang/test-fixtures/glob-paths/src/rebar.lock @@ -0,0 +1 @@ +bogus rebar.lock \ No newline at end of file From b24ceee52865067de0c0020bdf4825103b30705b Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:55:40 -0500 Subject: [PATCH 15/54] add glob-based cataloger tests for golang cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/golang/cataloger_test.go | 58 +++++++++++++++++++ .../test-fixtures/glob-paths/partial-binary | 1 + .../test-fixtures/glob-paths/src/go.mod | 1 + 3 files changed, 60 insertions(+) create mode 100644 syft/pkg/cataloger/golang/cataloger_test.go create mode 100644 syft/pkg/cataloger/golang/test-fixtures/glob-paths/partial-binary create mode 100644 syft/pkg/cataloger/golang/test-fixtures/glob-paths/src/go.mod diff --git a/syft/pkg/cataloger/golang/cataloger_test.go b/syft/pkg/cataloger/golang/cataloger_test.go new file mode 100644 index 00000000000..1f9000c3613 --- /dev/null +++ b/syft/pkg/cataloger/golang/cataloger_test.go @@ -0,0 +1,58 @@ +package golang + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_Mod_Cataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain go.mod files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/go.mod", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + IgnoreUnfulfilledContentQueries("src/go.sum"). + TestCataloger(t, NewGoModFileCataloger()) + }) + } +} + +func Test_Binary_Cataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain binary files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "partial-binary", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewGoModuleBinaryCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/golang/test-fixtures/glob-paths/partial-binary b/syft/pkg/cataloger/golang/test-fixtures/glob-paths/partial-binary new file mode 100644 index 00000000000..125d737c55f --- /dev/null +++ b/syft/pkg/cataloger/golang/test-fixtures/glob-paths/partial-binary @@ -0,0 +1 @@ +Ïúíþ \ No newline at end of file diff --git a/syft/pkg/cataloger/golang/test-fixtures/glob-paths/src/go.mod b/syft/pkg/cataloger/golang/test-fixtures/glob-paths/src/go.mod new file mode 100644 index 00000000000..2a49df8b21d --- /dev/null +++ b/syft/pkg/cataloger/golang/test-fixtures/glob-paths/src/go.mod @@ -0,0 +1 @@ +// bogus go.mod \ No newline at end of file From f60f3eb727000c01b4e764bf4914f0ff2b1f96aa Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:55:59 -0500 Subject: [PATCH 16/54] add glob-based cataloger tests for haskell cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/haskell/cataloger_test.go | 34 +++++++++++++++++++ .../glob-paths/src/cabal.project.freeze | 1 + .../test-fixtures/glob-paths/src/stack.yaml | 1 + .../glob-paths/src/stack.yaml.lock | 1 + 4 files changed, 37 insertions(+) create mode 100644 syft/pkg/cataloger/haskell/cataloger_test.go create mode 100644 syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/cabal.project.freeze create mode 100644 syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml create mode 100644 syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml.lock diff --git a/syft/pkg/cataloger/haskell/cataloger_test.go b/syft/pkg/cataloger/haskell/cataloger_test.go new file mode 100644 index 00000000000..a7dcc16cd37 --- /dev/null +++ b/syft/pkg/cataloger/haskell/cataloger_test.go @@ -0,0 +1,34 @@ +package haskell + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain stack and cabal files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/stack.yaml", + "src/stack.yaml.lock", + "src/cabal.project.freeze", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewHackageCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/cabal.project.freeze b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/cabal.project.freeze new file mode 100644 index 00000000000..ba0e55e316c --- /dev/null +++ b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/cabal.project.freeze @@ -0,0 +1 @@ +cabal.project.freeze \ No newline at end of file diff --git a/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml new file mode 100644 index 00000000000..f48f35b7351 --- /dev/null +++ b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml @@ -0,0 +1 @@ +bogus stack.yaml \ No newline at end of file diff --git a/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml.lock b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml.lock new file mode 100644 index 00000000000..447e1a04697 --- /dev/null +++ b/syft/pkg/cataloger/haskell/test-fixtures/glob-paths/src/stack.yaml.lock @@ -0,0 +1 @@ +bogus stack.yaml.lock \ No newline at end of file From fc0468523398fdc4f994d85ebbd34890553e426f Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:56:12 -0500 Subject: [PATCH 17/54] add glob-based cataloger tests for java cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/java/cataloger_test.go | 85 +++++++++++++++++++ .../java/tar_wrapped_archive_parser.go | 1 + .../glob-paths/archives/example.tar.br | 1 + .../glob-paths/archives/example.tar.bz | 1 + .../glob-paths/archives/example.tar.bz2 | 1 + .../glob-paths/archives/example.tar.gz | 1 + .../glob-paths/archives/example.tar.lz4 | 1 + .../glob-paths/archives/example.tar.sz | 1 + .../glob-paths/archives/example.tar.xz | 1 + .../glob-paths/archives/example.tar.zst | 1 + .../glob-paths/archives/example.tbr | 1 + .../glob-paths/archives/example.tbz | 1 + .../glob-paths/archives/example.tbz2 | 1 + .../glob-paths/archives/example.tgz | 1 + .../glob-paths/archives/example.tlz4 | 1 + .../glob-paths/archives/example.tsz | 1 + .../glob-paths/archives/example.txz | 1 + .../glob-paths/archives/example.tzst | 1 + .../glob-paths/java-archives/example.lpkg | 1 + .../glob-paths/java-archives/example.par | 1 + .../glob-paths/java-archives/example.sar | 1 + .../java/test-fixtures/glob-paths/src/pom.xml | 1 + 22 files changed, 106 insertions(+) create mode 100644 syft/pkg/cataloger/java/cataloger_test.go create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.br create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz2 create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.gz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.lz4 create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.sz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.xz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zst create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbr create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz2 create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tgz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tlz4 create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tsz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.txz create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzst create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.lpkg create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.par create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.sar create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/src/pom.xml diff --git a/syft/pkg/cataloger/java/cataloger_test.go b/syft/pkg/cataloger/java/cataloger_test.go new file mode 100644 index 00000000000..d550d28e764 --- /dev/null +++ b/syft/pkg/cataloger/java/cataloger_test.go @@ -0,0 +1,85 @@ +package java + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_ArchiveCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain java archive files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "java-archives/example.jar", + "java-archives/example.war", + "java-archives/example.ear", + "java-archives/example.par", + "java-archives/example.sar", + "java-archives/example.jpi", + "java-archives/example.hpi", + "java-archives/example.lpkg", + "archives/example.zip", + "archives/example.tar", + "archives/example.tar.gz", + "archives/example.tgz", + "archives/example.tar.bz", + "archives/example.tar.bz2", + "archives/example.tbz", + "archives/example.tbz2", + "archives/example.tar.br", + "archives/example.tbr", + "archives/example.tar.lz4", + "archives/example.tlz4", + "archives/example.tar.sz", + "archives/example.tsz", + "archives/example.tar.xz", + "archives/example.txz", + "archives/example.tar.zst", + "archives/example.tzst", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewJavaCataloger(Config{ + SearchUnindexedArchives: true, + SearchIndexedArchives: true, + })) + }) + } +} + +func Test_POMCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain java pom files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/pom.xml", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewJavaPomCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go index 12269ff07a2..18b189dd5d0 100644 --- a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go +++ b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go @@ -34,6 +34,7 @@ var genericTarExtensions = []string{ ".txz", // zstandard ".tar.zst", + ".tzst", } // TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246) diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.br b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.br new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.br @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz2 b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz2 new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.bz2 @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.gz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.gz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.gz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.lz4 b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.lz4 new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.lz4 @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.sz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.sz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.sz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.xz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.xz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.xz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zst b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zst new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zst @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbr b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbr new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbr @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz2 b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz2 new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tbz2 @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tgz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tgz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tgz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tlz4 b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tlz4 new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tlz4 @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tsz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tsz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tsz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.txz b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.txz new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.txz @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzst b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzst new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzst @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.lpkg b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.lpkg new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.lpkg @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.par b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.par new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.par @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.sar b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.sar new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.sar @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/src/pom.xml b/syft/pkg/cataloger/java/test-fixtures/glob-paths/src/pom.xml new file mode 100644 index 00000000000..0fbd1e3b4d5 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/src/pom.xml @@ -0,0 +1 @@ +bogus pom.xml \ No newline at end of file From 2793b2f8e7814251315a48ef915f043e77911d49 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:56:35 -0500 Subject: [PATCH 18/54] add glob-based cataloger tests for javascript cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/javascript/cataloger.go | 7 +-- .../cataloger/javascript/cataloger_test.go | 54 ++++++++++++++++++- .../glob-paths/src/package-lock.json | 1 + .../test-fixtures/glob-paths/src/package.json | 0 .../glob-paths/src/pnpm-lock.yaml | 1 + .../test-fixtures/glob-paths/src/yarn.lock | 1 + 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package-lock.json create mode 100644 syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package.json create mode 100644 syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/pnpm-lock.yaml create mode 100644 syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/yarn.lock diff --git a/syft/pkg/cataloger/javascript/cataloger.go b/syft/pkg/cataloger/javascript/cataloger.go index d23aa8107c2..cea0a7c7ffb 100644 --- a/syft/pkg/cataloger/javascript/cataloger.go +++ b/syft/pkg/cataloger/javascript/cataloger.go @@ -7,13 +7,14 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -// NewJavascriptPackageCataloger returns a new JavaScript cataloger object based on detection of npm based packages. -func NewJavascriptPackageCataloger() *generic.Cataloger { +// NewPackageCataloger returns a new JavaScript cataloger object based on detection of npm based packages. +func NewPackageCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-package-cataloger"). WithParserByBasename(parsePackageJSON, "package.json") } -func NewJavascriptLockCataloger() *generic.Cataloger { +// NewLockCataloger returns a new JavaScript cataloger object based on detection of lock files. +func NewLockCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-lock-cataloger"). WithParserByBasename(parsePackageLock, "package-lock.json"). WithParserByBasename(parseYarnLock, "yarn.lock"). diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index 2d310932baa..4226a3ebc10 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -139,6 +139,58 @@ func Test_JavascriptCataloger(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, "test-fixtures/pkg-lock"). Expects(expectedPkgs, nil). - TestCataloger(t, NewJavascriptLockCataloger()) + TestCataloger(t, NewLockCataloger()) } + +func Test_PackageCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain package files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/package.json", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewPackageCataloger()) + }) + } +} + +func Test_LockCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain package files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/package-lock.json", + "src/pnpm-lock.yaml", + "src/yarn.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewLockCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package-lock.json b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package-lock.json new file mode 100644 index 00000000000..9f8049938dc --- /dev/null +++ b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package-lock.json @@ -0,0 +1 @@ +bogus package-lock.json \ No newline at end of file diff --git a/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package.json b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/pnpm-lock.yaml b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/pnpm-lock.yaml new file mode 100644 index 00000000000..b3979e3ba3a --- /dev/null +++ b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/pnpm-lock.yaml @@ -0,0 +1 @@ +bogus pnpm-lock.yaml \ No newline at end of file diff --git a/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/yarn.lock b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/yarn.lock new file mode 100644 index 00000000000..f3260a1809d --- /dev/null +++ b/syft/pkg/cataloger/javascript/test-fixtures/glob-paths/src/yarn.lock @@ -0,0 +1 @@ +bogus yarn.lock \ No newline at end of file From 575ffab3b1e7d9958d573185d449a37fda39dccb Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:56:54 -0500 Subject: [PATCH 19/54] add glob-based cataloger tests for php cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/php/cataloger.go | 8 +-- syft/pkg/cataloger/php/cataloger_test.go | 57 +++++++++++++++++++ .../glob-paths/src/composer.lock | 1 + .../glob-paths/src/installed.json | 1 + 4 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 syft/pkg/cataloger/php/cataloger_test.go create mode 100644 syft/pkg/cataloger/php/test-fixtures/glob-paths/src/composer.lock create mode 100644 syft/pkg/cataloger/php/test-fixtures/glob-paths/src/installed.json diff --git a/syft/pkg/cataloger/php/cataloger.go b/syft/pkg/cataloger/php/cataloger.go index c5be36fd472..ff37719be07 100644 --- a/syft/pkg/cataloger/php/cataloger.go +++ b/syft/pkg/cataloger/php/cataloger.go @@ -7,14 +7,14 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -// NewPHPComposerInstalledCataloger returns a new cataloger for PHP installed.json files. -func NewPHPComposerInstalledCataloger() *generic.Cataloger { +// NewComposerInstalledCataloger returns a new cataloger for PHP installed.json files. +func NewComposerInstalledCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-installed-cataloger"). WithParserByBasename(parseInstalledJSON, "installed.json") } -// NewPHPComposerLockCataloger returns a new cataloger for PHP composer.lock files. -func NewPHPComposerLockCataloger() *generic.Cataloger { +// NewComposerLockCataloger returns a new cataloger for PHP composer.lock files. +func NewComposerLockCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-lock-cataloger"). WithParserByBasename(parseComposerLock, "composer.lock") } diff --git a/syft/pkg/cataloger/php/cataloger_test.go b/syft/pkg/cataloger/php/cataloger_test.go new file mode 100644 index 00000000000..63fb2ee35da --- /dev/null +++ b/syft/pkg/cataloger/php/cataloger_test.go @@ -0,0 +1,57 @@ +package php + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_ComposerInstalledCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain composer files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/installed.json", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewComposerInstalledCataloger()) + }) + } +} + +func Test_ComposerLockCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain composer lock files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/composer.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewComposerLockCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/composer.lock b/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/composer.lock new file mode 100644 index 00000000000..bad0c04cf42 --- /dev/null +++ b/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/composer.lock @@ -0,0 +1 @@ +bogus composer.lock \ No newline at end of file diff --git a/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/installed.json b/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/installed.json new file mode 100644 index 00000000000..49fd591e4c5 --- /dev/null +++ b/syft/pkg/cataloger/php/test-fixtures/glob-paths/src/installed.json @@ -0,0 +1 @@ +bogus installed.json \ No newline at end of file From 75a753ded6892354e2978bf097f8df89a4e9cfda Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:57:20 -0500 Subject: [PATCH 20/54] add glob-based cataloger tests for portage cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/portage/cataloger_test.go | 25 +++++++++++++++++++ .../glob-paths/var/db/pkg/x/y/CONTENTS | 1 + 2 files changed, 26 insertions(+) create mode 100644 syft/pkg/cataloger/portage/test-fixtures/glob-paths/var/db/pkg/x/y/CONTENTS diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 6623f27e713..3aaa760affc 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -71,3 +71,28 @@ func TestPortageCataloger(t *testing.T) { TestCataloger(t, NewPortageCataloger()) } + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain portage contents file", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "var/db/pkg/x/y/CONTENTS", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewPortageCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/portage/test-fixtures/glob-paths/var/db/pkg/x/y/CONTENTS b/syft/pkg/cataloger/portage/test-fixtures/glob-paths/var/db/pkg/x/y/CONTENTS new file mode 100644 index 00000000000..f8d606cae96 --- /dev/null +++ b/syft/pkg/cataloger/portage/test-fixtures/glob-paths/var/db/pkg/x/y/CONTENTS @@ -0,0 +1 @@ +bogus contents \ No newline at end of file From f90f9613d89fe349fcb3d00d5c13b33144dfaba9 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:57:36 -0500 Subject: [PATCH 21/54] add glob-based cataloger tests for python cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/python/cataloger_test.go | 58 +++++++++++++++++++ .../site-packages/x.dist-info/METADATA | 1 + .../site-packages/y.egg-info/PKG-INFO | 1 + .../glob-paths/site-packages/z.egg-info | 1 + .../glob-paths/src/1-requirements-dev.txt | 1 + .../test-fixtures/glob-paths/src/Pipfile.lock | 1 + .../glob-paths/src/extra-requirements.txt | 1 + .../test-fixtures/glob-paths/src/poetry.lock | 1 + .../glob-paths/src/requirements-dev.txt | 1 + .../glob-paths/src/requirements.txt | 1 + .../test-fixtures/glob-paths/src/setup.py | 1 + 11 files changed, 68 insertions(+) create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/x.dist-info/METADATA create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/y.egg-info/PKG-INFO create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/z.egg-info create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/1-requirements-dev.txt create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/Pipfile.lock create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/extra-requirements.txt create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/poetry.lock create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements-dev.txt create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements.txt create mode 100644 syft/pkg/cataloger/python/test-fixtures/glob-paths/src/setup.py diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 0c02ac36fed..27df8eeb335 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -233,3 +233,61 @@ func Test_PackageCataloger_IgnorePackage(t *testing.T) { }) } } + +func Test_IndexCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain index files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/requirements.txt", + "src/extra-requirements.txt", + "src/requirements-dev.txt", + "src/1-requirements-dev.txt", + "src/setup.py", + "src/poetry.lock", + "src/Pipfile.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewPythonIndexCataloger()) + }) + } +} + +func Test_PackageCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain index files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "site-packages/x.dist-info/METADATA", + "site-packages/y.egg-info/PKG-INFO", + "site-packages/z.egg-info", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewPythonPackageCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/x.dist-info/METADATA b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/x.dist-info/METADATA new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/x.dist-info/METADATA @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/y.egg-info/PKG-INFO b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/y.egg-info/PKG-INFO new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/y.egg-info/PKG-INFO @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/z.egg-info b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/z.egg-info new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/site-packages/z.egg-info @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/1-requirements-dev.txt b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/1-requirements-dev.txt new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/1-requirements-dev.txt @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/Pipfile.lock b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/Pipfile.lock new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/Pipfile.lock @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/extra-requirements.txt b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/extra-requirements.txt new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/extra-requirements.txt @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/poetry.lock b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/poetry.lock new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/poetry.lock @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements-dev.txt b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements-dev.txt new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements-dev.txt @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements.txt b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements.txt new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/requirements.txt @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/setup.py b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/setup.py new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/python/test-fixtures/glob-paths/src/setup.py @@ -0,0 +1 @@ +bogus From cbb63e7185fc74d6f163dadccf500911b7a7a889 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:57:53 -0500 Subject: [PATCH 22/54] add glob-based cataloger tests for rpm cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/rpm/cataloger_test.go | 60 +++++++++++++++++++ .../test-fixtures/glob-paths/dive-0.10.0.rpm | 1 + .../glob-paths/var/lib/rpm/Packages | 1 + .../glob-paths/var/lib/rpm/Packages.db | 1 + .../glob-paths/var/lib/rpm/rpmdb.sqlite | 1 + .../var/lib/rpmmanifest/container-manifest-2 | 1 + 6 files changed, 65 insertions(+) create mode 100644 syft/pkg/cataloger/rpm/cataloger_test.go create mode 100644 syft/pkg/cataloger/rpm/test-fixtures/glob-paths/dive-0.10.0.rpm create mode 100644 syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages create mode 100644 syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages.db create mode 100644 syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/rpmdb.sqlite create mode 100644 syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpmmanifest/container-manifest-2 diff --git a/syft/pkg/cataloger/rpm/cataloger_test.go b/syft/pkg/cataloger/rpm/cataloger_test.go new file mode 100644 index 00000000000..1bcfd23322e --- /dev/null +++ b/syft/pkg/cataloger/rpm/cataloger_test.go @@ -0,0 +1,60 @@ +package rpm + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_DBCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain DB files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "var/lib/rpm/Packages", + "var/lib/rpm/Packages.db", + "var/lib/rpm/rpmdb.sqlite", + "var/lib/rpmmanifest/container-manifest-2", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewRpmDBCataloger()) + }) + } +} + +func Test_RPMFileCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain rpm files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "dive-0.10.0.rpm", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewFileCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/dive-0.10.0.rpm b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/dive-0.10.0.rpm new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/dive-0.10.0.rpm @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages.db b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages.db new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/Packages.db @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/rpmdb.sqlite b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/rpmdb.sqlite new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpm/rpmdb.sqlite @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpmmanifest/container-manifest-2 b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpmmanifest/container-manifest-2 new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rpm/test-fixtures/glob-paths/var/lib/rpmmanifest/container-manifest-2 @@ -0,0 +1 @@ +bogus \ No newline at end of file From 78e723564e2d22ac1d63a3016b45ac37fff6caf6 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:58:08 -0500 Subject: [PATCH 23/54] add glob-based cataloger tests for rust cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/rust/cataloger_test.go | 50 +++++++++++++++++++ .../test-fixtures/glob-paths/partial-binary | 1 + .../test-fixtures/glob-paths/src/Cargo.lock | 1 + 3 files changed, 52 insertions(+) create mode 100644 syft/pkg/cataloger/rust/test-fixtures/glob-paths/partial-binary create mode 100644 syft/pkg/cataloger/rust/test-fixtures/glob-paths/src/Cargo.lock diff --git a/syft/pkg/cataloger/rust/cataloger_test.go b/syft/pkg/cataloger/rust/cataloger_test.go index c4c152854e3..cffcd74f698 100644 --- a/syft/pkg/cataloger/rust/cataloger_test.go +++ b/syft/pkg/cataloger/rust/cataloger_test.go @@ -49,3 +49,53 @@ func TestNewAuditBinaryCataloger(t *testing.T) { Expects(expectedPkgs, nil). TestCataloger(t, NewAuditBinaryCataloger()) } + +func Test_CargoLockCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain Cargo.lock files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/Cargo.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewCargoLockCataloger()) + }) + } +} + +func Test_AuditBinaryCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain audit binary files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "partial-binary", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewAuditBinaryCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/rust/test-fixtures/glob-paths/partial-binary b/syft/pkg/cataloger/rust/test-fixtures/glob-paths/partial-binary new file mode 100644 index 00000000000..125d737c55f --- /dev/null +++ b/syft/pkg/cataloger/rust/test-fixtures/glob-paths/partial-binary @@ -0,0 +1 @@ +Ïúíþ \ No newline at end of file diff --git a/syft/pkg/cataloger/rust/test-fixtures/glob-paths/src/Cargo.lock b/syft/pkg/cataloger/rust/test-fixtures/glob-paths/src/Cargo.lock new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/rust/test-fixtures/glob-paths/src/Cargo.lock @@ -0,0 +1 @@ +bogus \ No newline at end of file From 59491e34755845dce53a6c32b7a6f56127432623 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:58:20 -0500 Subject: [PATCH 24/54] add glob-based cataloger tests for sbom cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/sbom/cataloger_test.go | 35 +++++++++++++++++++ .../sbom/test-fixtures/glob-paths/app.bom | 1 + .../test-fixtures/glob-paths/app.bom.json | 1 + .../sbom/test-fixtures/glob-paths/app.cdx | 1 + .../test-fixtures/glob-paths/app.cdx.json | 1 + .../sbom/test-fixtures/glob-paths/app.sbom | 1 + .../test-fixtures/glob-paths/app.sbom.json | 1 + .../sbom/test-fixtures/glob-paths/app.spdx | 1 + .../test-fixtures/glob-paths/app.spdx.json | 1 + .../test-fixtures/glob-paths/app.syft.json | 1 + .../sbom/test-fixtures/glob-paths/bom | 1 + .../sbom/test-fixtures/glob-paths/sbom | 1 + 12 files changed, 46 insertions(+) create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom.json create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx.json create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom.json create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx.json create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.syft.json create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/bom create mode 100644 syft/pkg/cataloger/sbom/test-fixtures/glob-paths/sbom diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index b7489712de4..73bff7202cf 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -290,3 +290,38 @@ func Test_parseSBOM(t *testing.T) { }) } } + +func Test_Cataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain sbom files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "bom", + "sbom", + "app.syft.json", + "app.bom", + "app.sbom", + "app.cdx", + "app.spdx", + "app.bom.json", + "app.sbom.json", + "app.cdx.json", + "app.spdx.json", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewSBOMCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom.json b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom.json new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.bom.json @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx.json b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx.json new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.cdx.json @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom.json b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom.json new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.sbom.json @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx.json b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx.json new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.spdx.json @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.syft.json b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.syft.json new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/app.syft.json @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/bom b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/bom new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/bom @@ -0,0 +1 @@ +bogus diff --git a/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/sbom b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/sbom new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/pkg/cataloger/sbom/test-fixtures/glob-paths/sbom @@ -0,0 +1 @@ +bogus From b36fc6918d508dd7c4d76e8dbf58418c1faa3a13 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 13:58:36 -0500 Subject: [PATCH 25/54] add glob-based cataloger tests for swift cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/swift/cataloger_test.go | 32 +++++++++++++++++++ .../test-fixtures/glob-paths/src/Podfile.lock | 1 + 2 files changed, 33 insertions(+) create mode 100644 syft/pkg/cataloger/swift/cataloger_test.go create mode 100644 syft/pkg/cataloger/swift/test-fixtures/glob-paths/src/Podfile.lock diff --git a/syft/pkg/cataloger/swift/cataloger_test.go b/syft/pkg/cataloger/swift/cataloger_test.go new file mode 100644 index 00000000000..ed15bc1b1be --- /dev/null +++ b/syft/pkg/cataloger/swift/cataloger_test.go @@ -0,0 +1,32 @@ +package swift + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_Cataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain swift files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/Podfile.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsContentQueries(test.expected). + TestCataloger(t, NewCocoapodsCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/swift/test-fixtures/glob-paths/src/Podfile.lock b/syft/pkg/cataloger/swift/test-fixtures/glob-paths/src/Podfile.lock new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/swift/test-fixtures/glob-paths/src/Podfile.lock @@ -0,0 +1 @@ +bogus \ No newline at end of file From 3b80450e6b585c728f9d75bc709d63aeddcd4b14 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 14:00:11 -0500 Subject: [PATCH 26/54] allow generic catloger to run all mimetype searches at once Signed-off-by: Alex Goodman --- syft/pkg/cataloger/generic/cataloger.go | 15 ++++------ syft/pkg/cataloger/generic/search.go | 39 +++++++++++++++---------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/syft/pkg/cataloger/generic/cataloger.go b/syft/pkg/cataloger/generic/cataloger.go index 4096363c6e6..e40168d33a2 100644 --- a/syft/pkg/cataloger/generic/cataloger.go +++ b/syft/pkg/cataloger/generic/cataloger.go @@ -68,16 +68,13 @@ func (c *Cataloger) WithParserByMimeTypes(parser Parser, types ...string) *Catal c.processor = append(c.processor, func(resolver source.FileResolver, env Environment) []request { var requests []request - for _, t := range types { - log.WithFields("mimetype", t).Trace("searching for paths matching mimetype") - - matches, err := resolver.FilesByMIMEType(t) - if err != nil { - log.Warnf("unable to process mimetype=%q: %+v", t, err) - continue - } - requests = append(requests, makeRequests(parser, matches)...) + log.WithFields("mimetypes", types).Trace("searching for paths matching mimetype") + matches, err := resolver.FilesByMIMEType(types...) + if err != nil { + log.Warnf("unable to process mimetypes=%+v: %+v", types, err) + return nil } + requests = append(requests, makeRequests(parser, matches)...) return requests }, ) diff --git a/syft/pkg/cataloger/generic/search.go b/syft/pkg/cataloger/generic/search.go index 0eaffbeb690..0c1483879a1 100644 --- a/syft/pkg/cataloger/generic/search.go +++ b/syft/pkg/cataloger/generic/search.go @@ -147,23 +147,32 @@ func (s SearchRequest) Execute(resolver source.FileResolver) ([]source.Location, } if s.matchGlob != "" { - var globMatches []source.Location - forMatches: - for _, m := range locations { - var matchesGlob bool - for _, path := range []string{m.RealPath, m.VirtualPath} { - matchesGlob, err = doublestar.Match(s.matchGlob, path) - if err != nil { - return nil, fmt.Errorf("unable to validate glob requirement=%q: %w", s.matchGlob, err) - } - if matchesGlob { - globMatches = append(globMatches, m) - continue forMatches - } - } + // overwrite the locations with the filtered set + locations, err = s.matchRequirementGlob(locations) + if err != nil { + return nil, err } - locations = globMatches } return locations, nil } + +func (s SearchRequest) matchRequirementGlob(locations []source.Location) ([]source.Location, error) { + var globMatches []source.Location + var err error +forMatches: + for _, m := range locations { + var matchesGlob bool + for _, path := range []string{m.RealPath, m.VirtualPath} { + matchesGlob, err = doublestar.Match(s.matchGlob, path) + if err != nil { + return nil, fmt.Errorf("unable to validate glob requirement=%q: %w", s.matchGlob, err) + } + if matchesGlob { + globMatches = append(globMatches, m) + continue forMatches + } + } + } + return globMatches, nil +} From 534649049f321122c1f967fed2ab3fb667f3a89b Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 14:00:55 -0500 Subject: [PATCH 27/54] remove stutter from php and javascript cataloger constructors Signed-off-by: Alex Goodman --- syft/pkg/cataloger/cataloger.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index bfc2e1e9cd6..e59388ff07f 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -41,8 +41,8 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger { alpm.NewAlpmdbCataloger(), ruby.NewGemSpecCataloger(), python.NewPythonPackageCataloger(), - php.NewPHPComposerInstalledCataloger(), - javascript.NewJavascriptPackageCataloger(), + php.NewComposerInstalledCataloger(), + javascript.NewPackageCataloger(), deb.NewDpkgdbCataloger(), rpm.NewRpmDBCataloger(), java.NewJavaCataloger(cfg.Java()), @@ -63,8 +63,8 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger { ruby.NewGemFileLockCataloger(), python.NewPythonIndexCataloger(), python.NewPythonPackageCataloger(), - php.NewPHPComposerLockCataloger(), - javascript.NewJavascriptLockCataloger(), + php.NewComposerLockCataloger(), + javascript.NewLockCataloger(), deb.NewDpkgdbCataloger(), rpm.NewRpmDBCataloger(), rpm.NewFileCataloger(), @@ -96,8 +96,8 @@ func AllCatalogers(cfg Config) []pkg.Cataloger { ruby.NewGemSpecCataloger(), python.NewPythonIndexCataloger(), python.NewPythonPackageCataloger(), - javascript.NewJavascriptLockCataloger(), - javascript.NewJavascriptPackageCataloger(), + javascript.NewLockCataloger(), + javascript.NewPackageCataloger(), deb.NewDpkgdbCataloger(), rpm.NewRpmDBCataloger(), rpm.NewFileCataloger(), @@ -111,8 +111,8 @@ func AllCatalogers(cfg Config) []pkg.Cataloger { rust.NewAuditBinaryCataloger(), dart.NewPubspecLockCataloger(), dotnet.NewDotnetDepsCataloger(), - php.NewPHPComposerInstalledCataloger(), - php.NewPHPComposerLockCataloger(), + php.NewComposerInstalledCataloger(), + php.NewComposerLockCataloger(), swift.NewCocoapodsCataloger(), cpp.NewConanCataloger(), portage.NewPortageCataloger(), From 2ff499105a0789ceea7682e7e08779473b52bb15 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 14:01:12 -0500 Subject: [PATCH 28/54] bump stereoscope Signed-off-by: Alex Goodman --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index ae048f5523a..a65f40ab188 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,6 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230124175104-043b34b511a5 h1:64XL7Pnji0vdWlfUizwdUtagSpndpJD2g1yUb70stWU= -github.com/anchore/stereoscope v0.0.0-20230124175104-043b34b511a5/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb h1:fZhCHUYNl/+rf44PhwgeiLYlC2eL7YMj/Zy9s2tM/iA= github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= From 7736d2eb27a3ab24800bfd74fbd9032f66f155ba Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:01:23 -0500 Subject: [PATCH 29/54] add tests for generic.Search Signed-off-by: Alex Goodman --- syft/pkg/cataloger/alpm/cataloger_test.go | 4 +- syft/pkg/cataloger/apkdb/cataloger_test.go | 2 +- syft/pkg/cataloger/cpp/cataloger_test.go | 2 +- syft/pkg/cataloger/dart/cataloger_test.go | 2 +- syft/pkg/cataloger/deb/cataloger_test.go | 2 +- syft/pkg/cataloger/dotnet/cataloger_test.go | 2 +- syft/pkg/cataloger/elixir/cataloger_test.go | 2 +- syft/pkg/cataloger/erlang/cataloger_test.go | 2 +- syft/pkg/cataloger/generic/search_test.go | 206 ++++++++++++++ syft/pkg/cataloger/golang/cataloger_test.go | 6 +- syft/pkg/cataloger/haskell/cataloger_test.go | 2 +- .../internal/pkgtest/observing_resolver.go | 229 ---------------- .../internal/pkgtest/test_generic_parser.go | 51 ++-- .../internal/srctest/observing_resolver.go | 255 ++++++++++++++++++ syft/pkg/cataloger/java/cataloger_test.go | 4 +- .../cataloger/javascript/cataloger_test.go | 4 +- syft/pkg/cataloger/php/cataloger_test.go | 4 +- syft/pkg/cataloger/portage/cataloger_test.go | 2 +- syft/pkg/cataloger/python/cataloger_test.go | 4 +- syft/pkg/cataloger/rpm/cataloger_test.go | 4 +- syft/pkg/cataloger/rust/cataloger_test.go | 4 +- syft/pkg/cataloger/sbom/cataloger_test.go | 2 +- syft/pkg/cataloger/swift/cataloger_test.go | 2 +- 23 files changed, 515 insertions(+), 282 deletions(-) create mode 100644 syft/pkg/cataloger/generic/search_test.go delete mode 100644 syft/pkg/cataloger/internal/pkgtest/observing_resolver.go create mode 100644 syft/pkg/cataloger/internal/srctest/observing_resolver.go diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index b570e23f152..65447d66d8b 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -201,8 +201,8 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). - IgnoreUnfulfilledContentQueries("var/lib/pacman/local/base-1.0/mtree", "var/lib/pacman/local/dive-0.10.0/mtree"). + ExpectsResolverContentQueries(test.expected). + IgnoreUnfulfilledPathResponses("var/lib/pacman/local/base-1.0/mtree", "var/lib/pacman/local/dive-0.10.0/mtree"). TestCataloger(t, NewAlpmdbCataloger()) }) } diff --git a/syft/pkg/cataloger/apkdb/cataloger_test.go b/syft/pkg/cataloger/apkdb/cataloger_test.go index 15af869d051..5a29607913a 100644 --- a/syft/pkg/cataloger/apkdb/cataloger_test.go +++ b/syft/pkg/cataloger/apkdb/cataloger_test.go @@ -23,7 +23,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewApkdbCataloger()) }) } diff --git a/syft/pkg/cataloger/cpp/cataloger_test.go b/syft/pkg/cataloger/cpp/cataloger_test.go index 8a1f921d842..144d4cc915c 100644 --- a/syft/pkg/cataloger/cpp/cataloger_test.go +++ b/syft/pkg/cataloger/cpp/cataloger_test.go @@ -26,7 +26,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewConanCataloger()) }) } diff --git a/syft/pkg/cataloger/dart/cataloger_test.go b/syft/pkg/cataloger/dart/cataloger_test.go index ce72f5d4cc4..94aa8cfa0f7 100644 --- a/syft/pkg/cataloger/dart/cataloger_test.go +++ b/syft/pkg/cataloger/dart/cataloger_test.go @@ -25,7 +25,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewPubspecLockCataloger()) }) } diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index b87bf126a01..8fac459c613 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -102,7 +102,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewDpkgdbCataloger()) }) } diff --git a/syft/pkg/cataloger/dotnet/cataloger_test.go b/syft/pkg/cataloger/dotnet/cataloger_test.go index c9175244a21..8b131449148 100644 --- a/syft/pkg/cataloger/dotnet/cataloger_test.go +++ b/syft/pkg/cataloger/dotnet/cataloger_test.go @@ -25,7 +25,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewDotnetDepsCataloger()) }) } diff --git a/syft/pkg/cataloger/elixir/cataloger_test.go b/syft/pkg/cataloger/elixir/cataloger_test.go index d2a13459af6..538da15a5f2 100644 --- a/syft/pkg/cataloger/elixir/cataloger_test.go +++ b/syft/pkg/cataloger/elixir/cataloger_test.go @@ -25,7 +25,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewMixLockCataloger()) }) } diff --git a/syft/pkg/cataloger/erlang/cataloger_test.go b/syft/pkg/cataloger/erlang/cataloger_test.go index 2abe3eb68b3..1d959b10389 100644 --- a/syft/pkg/cataloger/erlang/cataloger_test.go +++ b/syft/pkg/cataloger/erlang/cataloger_test.go @@ -25,7 +25,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewRebarLockCataloger()) }) } diff --git a/syft/pkg/cataloger/generic/search_test.go b/syft/pkg/cataloger/generic/search_test.go new file mode 100644 index 00000000000..bbfad5b411e --- /dev/null +++ b/syft/pkg/cataloger/generic/search_test.go @@ -0,0 +1,206 @@ +package generic + +import ( + "io" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/srctest" + "github.com/anchore/syft/syft/source" +) + +func TestSearchRequest_Execute(t *testing.T) { + + tests := []struct { + name string + request SearchRequest + responsePath string + wantPathQueries map[string][]string + wantLocations []source.Location + }{ + { + name: "search by glob", + request: NewSearch().ByGlob("**/*test?/*.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByGlob": {"**/*test?/*.txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by path", + request: NewSearch().ByPath("result/will/match/x-test2/file.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByPath": {"result/will/match/x-test2/file.txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by extension", + request: NewSearch().ByExtension(".txt").Request(), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByExtension": {".txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by extension, with matching requirement", + request: NewSearch().ByExtension(".txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByExtension": {".txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by extension, with unmatched requirement", + request: NewSearch().ByExtension(".txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "somewhere-else/file.txt", + wantPathQueries: map[string][]string{ + "FilesByExtension": {".txt"}, + }, + wantLocations: nil, + }, + { + name: "search by basename, with matching requirement", + request: NewSearch().ByBasename("file.txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByBasename": {"file.txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by basename, with unmatched requirement", + request: NewSearch().ByBasename("file.txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "somewhere-else/file.txt", + wantPathQueries: map[string][]string{ + "FilesByBasename": {"file.txt"}, + }, + wantLocations: nil, + }, + { + name: "search by basename glob, with matching requirement", + request: NewSearch().ByBasenameGlob("?i*.txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByBasenameGlob": {"?i*.txt"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by basename glob, with unmatched requirement", + request: NewSearch().ByBasenameGlob("?i*.txt").MustMatchGlob("**/*test?/*.txt"), + responsePath: "somewhere-else/file.txt", + wantPathQueries: map[string][]string{ + "FilesByBasenameGlob": {"?i*.txt"}, + }, + wantLocations: nil, + }, + { + name: "search by mimetype, with matching requirement", + request: NewSearch().ByMimeType("plain/text").MustMatchGlob("**/*test?/*.txt"), + responsePath: "result/will/match/x-test2/file.txt", + wantPathQueries: map[string][]string{ + "FilesByMIMEType": {"plain/text"}, + }, + wantLocations: []source.Location{ + source.NewLocation("result/will/match/x-test2/file.txt"), + }, + }, + { + name: "search by mimetype, with unmatched requirement", + request: NewSearch().ByMimeType("plain/text").MustMatchGlob("**/*test?/*.txt"), + responsePath: "somewhere-else/file.txt", + wantPathQueries: map[string][]string{ + "FilesByMIMEType": {"plain/text"}, + }, + wantLocations: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &nopResolver{} + if tt.responsePath != "" { + n.loc = []source.Location{source.NewLocation(tt.responsePath)} + } + resolver := srctest.NewObservingResolver(n) + + locations, err := tt.request.Execute(resolver) + require.NoError(t, err) + + assert.Equal(t, tt.wantLocations, locations) + + if d := cmp.Diff(tt.wantPathQueries, resolver.AllPathQueries()); d != "" { + t.Errorf("unexpected path queries (-want +got):\n%s", d) + } + }) + } +} + +var _ source.FileResolver = (*nopResolver)(nil) + +type nopResolver struct { + loc []source.Location +} + +func (n nopResolver) FileContentsByLocation(_ source.Location) (io.ReadCloser, error) { + return nil, nil +} + +func (n nopResolver) HasPath(_ string) bool { + return false +} + +func (n nopResolver) FilesByPath(_ ...string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) FilesByGlob(_ ...string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) FilesByExtension(_ string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) FilesByBasename(_ string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) FilesByBasenameGlob(_ string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) FilesByMIMEType(_ ...string) ([]source.Location, error) { + return n.loc, nil +} + +func (n nopResolver) RelativeFileByPath(_ source.Location, _ string) *source.Location { + return nil +} + +func (n nopResolver) AllLocations() <-chan source.Location { + return nil +} + +func (n nopResolver) FileMetadataByLocation(_ source.Location) (source.FileMetadata, error) { + return source.FileMetadata{}, nil +} diff --git a/syft/pkg/cataloger/golang/cataloger_test.go b/syft/pkg/cataloger/golang/cataloger_test.go index 1f9000c3613..55a18e6af71 100644 --- a/syft/pkg/cataloger/golang/cataloger_test.go +++ b/syft/pkg/cataloger/golang/cataloger_test.go @@ -25,8 +25,8 @@ func Test_Mod_Cataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). - IgnoreUnfulfilledContentQueries("src/go.sum"). + ExpectsResolverContentQueries(test.expected). + IgnoreUnfulfilledPathResponses("src/go.sum"). TestCataloger(t, NewGoModFileCataloger()) }) } @@ -51,7 +51,7 @@ func Test_Binary_Cataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewGoModuleBinaryCataloger()) }) } diff --git a/syft/pkg/cataloger/haskell/cataloger_test.go b/syft/pkg/cataloger/haskell/cataloger_test.go index a7dcc16cd37..86a4b3670b3 100644 --- a/syft/pkg/cataloger/haskell/cataloger_test.go +++ b/syft/pkg/cataloger/haskell/cataloger_test.go @@ -27,7 +27,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewHackageCataloger()) }) } diff --git a/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go b/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go deleted file mode 100644 index 79102d7c351..00000000000 --- a/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go +++ /dev/null @@ -1,229 +0,0 @@ -package pkgtest - -import ( - "fmt" - "io" - "sort" - - "github.com/anchore/syft/syft/source" -) - -var _ source.FileResolver = (*observingResolver)(nil) - -type observingResolver struct { - decorated source.FileResolver - pathQuery []source.Location - contentQuery []source.Location - emptyPathQueryResults map[string][]string -} - -func newObservingResolver(resolver source.FileResolver) *observingResolver { - return &observingResolver{ - decorated: resolver, - pathQuery: make([]source.Location, 0), - emptyPathQueryResults: make(map[string][]string), - } -} - -// testing helpers... - -//nolint:unused -func (r *observingResolver) observedQuery(path string) bool { - return r.observedPathQuery(path) || r.observedContentQuery(path) -} - -func (r *observingResolver) observedPathQuery(path string) bool { - for _, loc := range r.pathQuery { - if loc.RealPath == path { - return true - } - } - return false -} - -//nolint:unused -func (r *observingResolver) observedContentQuery(path string) bool { - for _, loc := range r.pathQuery { - if loc.RealPath == path { - return true - } - } - return false -} - -func (r *observingResolver) observedContentQueries() []string { - var observed []string - for _, loc := range r.pathQuery { - observed = append(observed, loc.RealPath) - } - return observed -} - -func (r *observingResolver) pruneUnfulfilledPathQueries(ignore map[string][]string, ignorePaths ...string) { - if ignore == nil { - return - } - // remove any paths that were ignored for specific calls - for k, v := range ignore { - results := r.emptyPathQueryResults[k] - for _, ig := range v { - for i, result := range results { - if result == ig { - results = append(results[:i], results[i+1:]...) - break - } - } - } - if len(results) > 0 { - r.emptyPathQueryResults[k] = results - } else { - delete(r.emptyPathQueryResults, k) - } - } - - // remove any paths that were ignored for all calls - for _, ig := range ignorePaths { - for k, v := range r.emptyPathQueryResults { - for i, result := range v { - if result == ig { - v = append(v[:i], v[i+1:]...) - break - } - } - if len(v) > 0 { - r.emptyPathQueryResults[k] = v - } else { - delete(r.emptyPathQueryResults, k) - } - } - } -} - -func (r *observingResolver) hasUnfulfilledPathRequests() bool { - return len(r.emptyPathQueryResults) > 0 -} - -func (r *observingResolver) prettyUnfulfilledPathRequests() string { - var res string - var keys []string - - for k := range r.emptyPathQueryResults { - keys = append(keys, k) - } - - sort.Strings(keys) - - for _, k := range keys { - res += fmt.Sprintf(" %s: %+v\n", k, r.emptyPathQueryResults[k]) - } - return res -} - -// For the file path resolver... - -func (r *observingResolver) FilesByPath(paths ...string) ([]source.Location, error) { - locs, err := r.decorated.FilesByPath(paths...) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByPath" - results := r.emptyPathQueryResults[key] - results = append(results, paths...) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) FilesByGlob(patterns ...string) ([]source.Location, error) { - locs, err := r.decorated.FilesByGlob(patterns...) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByGlob" - results := r.emptyPathQueryResults[key] - results = append(results, patterns...) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) FilesByExtension(extension string) ([]source.Location, error) { - locs, err := r.decorated.FilesByExtension(extension) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByExtension" - results := r.emptyPathQueryResults[key] - results = append(results, extension) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) FilesByBasename(filename string) ([]source.Location, error) { - locs, err := r.decorated.FilesByBasename(filename) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByBasename" - results := r.emptyPathQueryResults[key] - results = append(results, filename) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { - locs, err := r.decorated.FilesByBasenameGlob(glob) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByBasenameGlob" - results := r.emptyPathQueryResults[key] - results = append(results, glob) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { - locs, err := r.decorated.FilesByMIMEType(types...) - r.pathQuery = append(r.pathQuery, locs...) - if len(locs) == 0 { - key := "FilesByMIMEType" - results := r.emptyPathQueryResults[key] - results = append(results, types...) - r.emptyPathQueryResults[key] = results - } - return locs, err -} - -func (r *observingResolver) RelativeFileByPath(l source.Location, path string) *source.Location { - loc := r.decorated.RelativeFileByPath(l, path) - if loc != nil { - r.pathQuery = append(r.pathQuery, *loc) - } else { - key := "RelativeFileByPath" - results := r.emptyPathQueryResults[key] - results = append(results, path) - r.emptyPathQueryResults[key] = results - } - return loc -} - -// For the content resolver methods... - -func (r *observingResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { - reader, err := r.decorated.FileContentsByLocation(location) - r.contentQuery = append(r.contentQuery, location) - return reader, err -} - -// For the remaining resolver methods... - -func (r *observingResolver) AllLocations() <-chan source.Location { - return r.decorated.AllLocations() -} - -func (r *observingResolver) HasPath(s string) bool { - return r.decorated.HasPath(s) -} - -func (r *observingResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { - return r.decorated.FileMetadataByLocation(location) -} diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index f92fd358114..325dd0309db 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -17,32 +17,33 @@ import ( "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/pkg/cataloger/internal/srctest" "github.com/anchore/syft/syft/source" ) type locationComparer func(x, y source.Location) bool type CatalogTester struct { - expectedPkgs []pkg.Package - expectedRelationships []artifact.Relationship - assertResultExpectations bool - expectedPathQueries []string // this is a minimum set, the resolver may return more that just this list - expectedContentQueries []string // this is a full set, any other queries are unexpected (and will fail the test) - ignoreUnfulfilledQueries map[string][]string - ignoreUnfulfilledPaths []string - env *generic.Environment - reader source.LocationReadCloser - resolver source.FileResolver - wantErr require.ErrorAssertionFunc - compareOptions []cmp.Option - locationComparer locationComparer + expectedPkgs []pkg.Package + expectedRelationships []artifact.Relationship + assertResultExpectations bool + expectedPathResponses []string // this is a minimum set, the resolver may return more that just this list + expectedContentQueries []string // this is a full set, any other queries are unexpected (and will fail the test) + ignoreUnfulfilledPathResponses map[string][]string + ignoreAnyUnfulfilledPaths []string + env *generic.Environment + reader source.LocationReadCloser + resolver source.FileResolver + wantErr require.ErrorAssertionFunc + compareOptions []cmp.Option + locationComparer locationComparer } func NewCatalogTester() *CatalogTester { return &CatalogTester{ wantErr: require.NoError, locationComparer: DefaultLocationComparer, - ignoreUnfulfilledQueries: map[string][]string{ + ignoreUnfulfilledPathResponses: map[string][]string{ "FilesByPath": { // most catalogers search for a linux release, which will not be fulfilled in testing "/etc/os-release", @@ -159,18 +160,18 @@ func (p *CatalogTester) Expects(pkgs []pkg.Package, relationships []artifact.Rel return p } -func (p *CatalogTester) ExpectsPathQueries(locations []string) *CatalogTester { - p.expectedPathQueries = locations +func (p *CatalogTester) ExpectsResolverPathResponses(locations []string) *CatalogTester { + p.expectedPathResponses = locations return p } -func (p *CatalogTester) ExpectsContentQueries(locations []string) *CatalogTester { +func (p *CatalogTester) ExpectsResolverContentQueries(locations []string) *CatalogTester { p.expectedContentQueries = locations return p } -func (p *CatalogTester) IgnoreUnfulfilledContentQueries(paths ...string) *CatalogTester { - p.ignoreUnfulfilledPaths = append(p.ignoreUnfulfilledPaths, paths...) +func (p *CatalogTester) IgnoreUnfulfilledPathResponses(paths ...string) *CatalogTester { + p.ignoreAnyUnfulfilledPaths = append(p.ignoreAnyUnfulfilledPaths, paths...) return p } @@ -184,28 +185,28 @@ func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) { func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) { t.Helper() - resolver := newObservingResolver(p.resolver) + resolver := srctest.NewObservingResolver(p.resolver) pkgs, relationships, err := cataloger.Catalog(resolver) // this is a minimum set, the resolver may return more that just this list - for _, path := range p.expectedPathQueries { - assert.Truef(t, resolver.observedPathQuery(path), "expected path query for %q was not observed", path) + for _, path := range p.expectedPathResponses { + assert.Truef(t, resolver.ObservedPathResponses(path), "expected path query for %q was not observed", path) } // this is a full set, any other queries are unexpected (and will fail the test) if len(p.expectedContentQueries) > 0 { - assert.ElementsMatchf(t, p.expectedContentQueries, resolver.observedContentQueries(), "unexpected content queries observed: diff %s", cmp.Diff(p.expectedContentQueries, resolver.observedContentQueries())) + assert.ElementsMatchf(t, p.expectedContentQueries, resolver.AllContentQueries(), "unexpected content queries observed: diff %s", cmp.Diff(p.expectedContentQueries, resolver.AllContentQueries())) } if p.assertResultExpectations { p.wantErr(t, err) p.assertPkgs(t, pkgs, relationships) } else { - resolver.pruneUnfulfilledPathQueries(p.ignoreUnfulfilledQueries, p.ignoreUnfulfilledPaths...) + resolver.PruneUnfulfilledPathResponses(p.ignoreUnfulfilledPathResponses, p.ignoreAnyUnfulfilledPaths...) // if we aren't testing the results, we should focus on what was searched for (for glob-centric tests) - assert.Falsef(t, resolver.hasUnfulfilledPathRequests(), "unfulfilled path requests: \n%v", resolver.prettyUnfulfilledPathRequests()) + assert.Falsef(t, resolver.HasUnfulfilledPathRequests(), "unfulfilled path requests: \n%v", resolver.PrettyUnfulfilledPathRequests()) } } diff --git a/syft/pkg/cataloger/internal/srctest/observing_resolver.go b/syft/pkg/cataloger/internal/srctest/observing_resolver.go new file mode 100644 index 00000000000..3b71646d13f --- /dev/null +++ b/syft/pkg/cataloger/internal/srctest/observing_resolver.go @@ -0,0 +1,255 @@ +package srctest + +import ( + "fmt" + "io" + "sort" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/syft/syft/source" +) + +var _ source.FileResolver = (*ObservingResolver)(nil) + +type ObservingResolver struct { + decorated source.FileResolver + pathQueries map[string][]string + pathResponses []source.Location + contentQueries []source.Location + emptyPathResponses map[string][]string +} + +func NewObservingResolver(resolver source.FileResolver) *ObservingResolver { + return &ObservingResolver{ + decorated: resolver, + pathResponses: make([]source.Location, 0), + emptyPathResponses: make(map[string][]string), + pathQueries: make(map[string][]string), + } +} + +// testing helpers... + +func (r *ObservingResolver) ObservedPathQuery(input string) bool { + for _, vs := range r.pathQueries { + for _, v := range vs { + if v == input { + return true + } + } + } + return false +} + +func (r *ObservingResolver) ObservedPathResponses(path string) bool { + for _, loc := range r.pathResponses { + if loc.RealPath == path { + return true + } + } + return false +} + +func (r *ObservingResolver) ObservedContentQueries(path string) bool { + for _, loc := range r.contentQueries { + if loc.RealPath == path { + return true + } + } + return false +} + +func (r *ObservingResolver) AllContentQueries() []string { + observed := strset.New() + for _, loc := range r.contentQueries { + observed.Add(loc.RealPath) + } + return observed.List() +} + +func (r *ObservingResolver) AllPathQueries() map[string][]string { + return r.pathQueries +} + +func (r *ObservingResolver) PruneUnfulfilledPathResponses(ignore map[string][]string, ignorePaths ...string) { + if ignore == nil { + return + } + // remove any paths that were ignored for specific calls + for k, v := range ignore { + results := r.emptyPathResponses[k] + for _, ig := range v { + for i, result := range results { + if result == ig { + results = append(results[:i], results[i+1:]...) + break + } + } + } + if len(results) > 0 { + r.emptyPathResponses[k] = results + } else { + delete(r.emptyPathResponses, k) + } + } + + // remove any paths that were ignored for all calls + for _, ig := range ignorePaths { + for k, v := range r.emptyPathResponses { + for i, result := range v { + if result == ig { + v = append(v[:i], v[i+1:]...) + break + } + } + if len(v) > 0 { + r.emptyPathResponses[k] = v + } else { + delete(r.emptyPathResponses, k) + } + } + } +} + +func (r *ObservingResolver) HasUnfulfilledPathRequests() bool { + return len(r.emptyPathResponses) > 0 +} + +func (r *ObservingResolver) PrettyUnfulfilledPathRequests() string { + var res string + var keys []string + + for k := range r.emptyPathResponses { + keys = append(keys, k) + } + + sort.Strings(keys) + + for _, k := range keys { + res += fmt.Sprintf(" %s: %+v\n", k, r.emptyPathResponses[k]) + } + return res +} + +// For the file path resolver... + +func (r *ObservingResolver) addPathQuery(name string, input ...string) { + r.pathQueries[name] = append(r.pathQueries[name], input...) +} + +func (r *ObservingResolver) addPathResponse(locs ...source.Location) { + r.pathResponses = append(r.pathResponses, locs...) +} + +func (r *ObservingResolver) addEmptyPathResponse(name string, locs []source.Location, paths ...string) { + if len(locs) == 0 { + results := r.emptyPathResponses[name] + results = append(results, paths...) + r.emptyPathResponses[name] = results + } +} + +func (r *ObservingResolver) FilesByPath(paths ...string) ([]source.Location, error) { + name := "FilesByPath" + r.addPathQuery(name, paths...) + + locs, err := r.decorated.FilesByPath(paths...) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, paths...) + return locs, err +} + +func (r *ObservingResolver) FilesByGlob(patterns ...string) ([]source.Location, error) { + name := "FilesByGlob" + r.addPathQuery(name, patterns...) + + locs, err := r.decorated.FilesByGlob(patterns...) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, patterns...) + return locs, err +} + +func (r *ObservingResolver) FilesByExtension(extension string) ([]source.Location, error) { + name := "FilesByExtension" + r.addPathQuery(name, extension) + + locs, err := r.decorated.FilesByExtension(extension) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, extension) + return locs, err +} + +func (r *ObservingResolver) FilesByBasename(filename string) ([]source.Location, error) { + name := "FilesByBasename" + r.addPathQuery(name, filename) + + locs, err := r.decorated.FilesByBasename(filename) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, filename) + return locs, err +} + +func (r *ObservingResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { + name := "FilesByBasenameGlob" + r.addPathQuery(name, glob) + + locs, err := r.decorated.FilesByBasenameGlob(glob) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, glob) + return locs, err +} + +func (r *ObservingResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { + name := "FilesByMIMEType" + r.addPathQuery(name, types...) + + locs, err := r.decorated.FilesByMIMEType(types...) + + r.addPathResponse(locs...) + r.addEmptyPathResponse(name, locs, types...) + return locs, err +} + +func (r *ObservingResolver) RelativeFileByPath(l source.Location, path string) *source.Location { + name := "RelativeFileByPath" + r.addPathQuery(name, path) + + loc := r.decorated.RelativeFileByPath(l, path) + + if loc != nil { + r.addPathResponse(*loc) + } else { + results := r.emptyPathResponses[name] + results = append(results, path) + r.emptyPathResponses[name] = results + } + return loc +} + +// For the content resolver methods... + +func (r *ObservingResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { + r.contentQueries = append(r.contentQueries, location) + reader, err := r.decorated.FileContentsByLocation(location) + return reader, err +} + +// For the remaining resolver methods... + +func (r *ObservingResolver) AllLocations() <-chan source.Location { + return r.decorated.AllLocations() +} + +func (r *ObservingResolver) HasPath(s string) bool { + return r.decorated.HasPath(s) +} + +func (r *ObservingResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { + return r.decorated.FileMetadataByLocation(location) +} diff --git a/syft/pkg/cataloger/java/cataloger_test.go b/syft/pkg/cataloger/java/cataloger_test.go index d550d28e764..0bb0752926a 100644 --- a/syft/pkg/cataloger/java/cataloger_test.go +++ b/syft/pkg/cataloger/java/cataloger_test.go @@ -50,7 +50,7 @@ func Test_ArchiveCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewJavaCataloger(Config{ SearchUnindexedArchives: true, SearchIndexedArchives: true, @@ -78,7 +78,7 @@ func Test_POMCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewJavaPomCataloger()) }) } diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index 4226a3ebc10..6e0ba16b613 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -162,7 +162,7 @@ func Test_PackageCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewPackageCataloger()) }) } @@ -189,7 +189,7 @@ func Test_LockCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewLockCataloger()) }) } diff --git a/syft/pkg/cataloger/php/cataloger_test.go b/syft/pkg/cataloger/php/cataloger_test.go index 63fb2ee35da..8d5f49093eb 100644 --- a/syft/pkg/cataloger/php/cataloger_test.go +++ b/syft/pkg/cataloger/php/cataloger_test.go @@ -25,7 +25,7 @@ func Test_ComposerInstalledCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewComposerInstalledCataloger()) }) } @@ -50,7 +50,7 @@ func Test_ComposerLockCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewComposerLockCataloger()) }) } diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 3aaa760affc..3f4494b29c1 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -91,7 +91,7 @@ func TestCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewPortageCataloger()) }) } diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 27df8eeb335..9d4bbeef7ab 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -259,7 +259,7 @@ func Test_IndexCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewPythonIndexCataloger()) }) } @@ -286,7 +286,7 @@ func Test_PackageCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewPythonPackageCataloger()) }) } diff --git a/syft/pkg/cataloger/rpm/cataloger_test.go b/syft/pkg/cataloger/rpm/cataloger_test.go index 1bcfd23322e..ca8907e2101 100644 --- a/syft/pkg/cataloger/rpm/cataloger_test.go +++ b/syft/pkg/cataloger/rpm/cataloger_test.go @@ -28,7 +28,7 @@ func Test_DBCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewRpmDBCataloger()) }) } @@ -53,7 +53,7 @@ func Test_RPMFileCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewFileCataloger()) }) } diff --git a/syft/pkg/cataloger/rust/cataloger_test.go b/syft/pkg/cataloger/rust/cataloger_test.go index cffcd74f698..73b442c817b 100644 --- a/syft/pkg/cataloger/rust/cataloger_test.go +++ b/syft/pkg/cataloger/rust/cataloger_test.go @@ -69,7 +69,7 @@ func Test_CargoLockCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewCargoLockCataloger()) }) } @@ -94,7 +94,7 @@ func Test_AuditBinaryCataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewAuditBinaryCataloger()) }) } diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 73bff7202cf..ea372d50ccc 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -320,7 +320,7 @@ func Test_Cataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewSBOMCataloger()) }) } diff --git a/syft/pkg/cataloger/swift/cataloger_test.go b/syft/pkg/cataloger/swift/cataloger_test.go index ed15bc1b1be..8100ce8b15c 100644 --- a/syft/pkg/cataloger/swift/cataloger_test.go +++ b/syft/pkg/cataloger/swift/cataloger_test.go @@ -25,7 +25,7 @@ func Test_Cataloger_Globs(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). - ExpectsContentQueries(test.expected). + ExpectsResolverContentQueries(test.expected). TestCataloger(t, NewCocoapodsCataloger()) }) } From dc9a40017f6a0c4c04621261fb67477fffda289e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:09:40 -0500 Subject: [PATCH 30/54] add exceptions for java archive git ignore entries Signed-off-by: Alex Goodman --- .../cataloger/java/test-fixtures/glob-paths/archives/.gitignore | 2 ++ .../java/test-fixtures/glob-paths/archives/example.tar | 1 + .../java/test-fixtures/glob-paths/archives/example.zip | 1 + .../java/test-fixtures/glob-paths/java-archives/.gitignore | 2 ++ .../java/test-fixtures/glob-paths/java-archives/example.ear | 1 + .../java/test-fixtures/glob-paths/java-archives/example.hpi | 1 + .../java/test-fixtures/glob-paths/java-archives/example.jar | 1 + .../java/test-fixtures/glob-paths/java-archives/example.jpi | 1 + .../java/test-fixtures/glob-paths/java-archives/example.war | 1 + 9 files changed, 11 insertions(+) create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/.gitignore create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.zip create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/.gitignore create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.ear create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.hpi create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jar create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jpi create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.war diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/.gitignore b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/.gitignore new file mode 100644 index 00000000000..4ec97210de8 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/.gitignore @@ -0,0 +1,2 @@ +# we want to override some of the root level ignores just for the fixutes that we know are safe +!* \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.zip b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.zip new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.zip @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/.gitignore b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/.gitignore new file mode 100644 index 00000000000..4ec97210de8 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/.gitignore @@ -0,0 +1,2 @@ +# we want to override some of the root level ignores just for the fixutes that we know are safe +!* \ No newline at end of file diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.ear b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.ear new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.ear @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.hpi b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.hpi new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.hpi @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jar b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jar new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jar @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jpi b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jpi new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.jpi @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.war b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.war new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/java-archives/example.war @@ -0,0 +1 @@ +example archive From b991177b4e5025adc2cf2eb5b214af74021b88c1 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:26:57 -0500 Subject: [PATCH 31/54] enhance basename and extension resolver methods to be variadic Signed-off-by: Alex Goodman --- syft/pkg/cataloger/binary/cataloger_test.go | 18 ++-- syft/pkg/cataloger/generic/search_test.go | 6 +- .../internal/srctest/observing_resolver.go | 24 +++--- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 6 +- syft/source/directory_resolver.go | 84 ++++++++++--------- syft/source/excluding_file_resolver.go | 18 ++-- syft/source/excluding_file_resolver_test.go | 33 +++++--- syft/source/file_resolver.go | 6 +- syft/source/image_all_layers_resolver.go | 60 +++++++------ syft/source/image_squash_resolver.go | 55 ++++++------ syft/source/mock_resolver.go | 18 ++-- 11 files changed, 182 insertions(+), 146 deletions(-) diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index 8cbdded3e21..b64c250c8ab 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -440,22 +440,22 @@ type panicyResolver struct { searchCalled bool } -func (p *panicyResolver) FilesByExtension(extension string) ([]source.Location, error) { +func (p *panicyResolver) FilesByExtension(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) FilesByBasename(filename string) ([]source.Location, error) { +func (p *panicyResolver) FilesByBasename(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { +func (p *panicyResolver) FilesByBasenameGlob(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) FileContentsByLocation(location source.Location) (io.ReadCloser, error) { +func (p *panicyResolver) FileContentsByLocation(_ source.Location) (io.ReadCloser, error) { p.searchCalled = true return nil, errors.New("not implemented") } @@ -464,22 +464,22 @@ func (p *panicyResolver) HasPath(s string) bool { return true } -func (p *panicyResolver) FilesByPath(paths ...string) ([]source.Location, error) { +func (p *panicyResolver) FilesByPath(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) FilesByGlob(patterns ...string) ([]source.Location, error) { +func (p *panicyResolver) FilesByGlob(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { +func (p *panicyResolver) FilesByMIMEType(_ ...string) ([]source.Location, error) { p.searchCalled = true return nil, errors.New("not implemented") } -func (p *panicyResolver) RelativeFileByPath(_ source.Location, path string) *source.Location { +func (p *panicyResolver) RelativeFileByPath(_ source.Location, _ string) *source.Location { return nil } @@ -487,7 +487,7 @@ func (p *panicyResolver) AllLocations() <-chan source.Location { return nil } -func (p *panicyResolver) FileMetadataByLocation(location source.Location) (source.FileMetadata, error) { +func (p *panicyResolver) FileMetadataByLocation(_ source.Location) (source.FileMetadata, error) { return source.FileMetadata{}, errors.New("not implemented") } diff --git a/syft/pkg/cataloger/generic/search_test.go b/syft/pkg/cataloger/generic/search_test.go index bbfad5b411e..acc3b755288 100644 --- a/syft/pkg/cataloger/generic/search_test.go +++ b/syft/pkg/cataloger/generic/search_test.go @@ -177,15 +177,15 @@ func (n nopResolver) FilesByGlob(_ ...string) ([]source.Location, error) { return n.loc, nil } -func (n nopResolver) FilesByExtension(_ string) ([]source.Location, error) { +func (n nopResolver) FilesByExtension(_ ...string) ([]source.Location, error) { return n.loc, nil } -func (n nopResolver) FilesByBasename(_ string) ([]source.Location, error) { +func (n nopResolver) FilesByBasename(_ ...string) ([]source.Location, error) { return n.loc, nil } -func (n nopResolver) FilesByBasenameGlob(_ string) ([]source.Location, error) { +func (n nopResolver) FilesByBasenameGlob(_ ...string) ([]source.Location, error) { return n.loc, nil } diff --git a/syft/pkg/cataloger/internal/srctest/observing_resolver.go b/syft/pkg/cataloger/internal/srctest/observing_resolver.go index 3b71646d13f..69c8cc5cbea 100644 --- a/syft/pkg/cataloger/internal/srctest/observing_resolver.go +++ b/syft/pkg/cataloger/internal/srctest/observing_resolver.go @@ -172,36 +172,36 @@ func (r *ObservingResolver) FilesByGlob(patterns ...string) ([]source.Location, return locs, err } -func (r *ObservingResolver) FilesByExtension(extension string) ([]source.Location, error) { +func (r *ObservingResolver) FilesByExtension(extensions ...string) ([]source.Location, error) { name := "FilesByExtension" - r.addPathQuery(name, extension) + r.addPathQuery(name, extensions...) - locs, err := r.decorated.FilesByExtension(extension) + locs, err := r.decorated.FilesByExtension(extensions...) r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, extension) + r.addEmptyPathResponse(name, locs, extensions...) return locs, err } -func (r *ObservingResolver) FilesByBasename(filename string) ([]source.Location, error) { +func (r *ObservingResolver) FilesByBasename(filenames ...string) ([]source.Location, error) { name := "FilesByBasename" - r.addPathQuery(name, filename) + r.addPathQuery(name, filenames...) - locs, err := r.decorated.FilesByBasename(filename) + locs, err := r.decorated.FilesByBasename(filenames...) r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, filename) + r.addEmptyPathResponse(name, locs, filenames...) return locs, err } -func (r *ObservingResolver) FilesByBasenameGlob(glob string) ([]source.Location, error) { +func (r *ObservingResolver) FilesByBasenameGlob(globs ...string) ([]source.Location, error) { name := "FilesByBasenameGlob" - r.addPathQuery(name, glob) + r.addPathQuery(name, globs...) - locs, err := r.decorated.FilesByBasenameGlob(glob) + locs, err := r.decorated.FilesByBasenameGlob(globs...) r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, glob) + r.addEmptyPathResponse(name, locs, globs...) return locs, err } diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 955d1d6400b..111195fd34b 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -19,15 +19,15 @@ type rpmdbTestFileResolverMock struct { ignorePaths bool } -func (r rpmdbTestFileResolverMock) FilesByExtension(extension string) ([]source.Location, error) { +func (r rpmdbTestFileResolverMock) FilesByExtension(extensions ...string) ([]source.Location, error) { panic("not implemented") } -func (r rpmdbTestFileResolverMock) FilesByBasename(filename string) ([]source.Location, error) { +func (r rpmdbTestFileResolverMock) FilesByBasename(filenames ...string) ([]source.Location, error) { panic("not implemented") } -func (r rpmdbTestFileResolverMock) FilesByBasenameGlob(glob string) ([]source.Location, error) { +func (r rpmdbTestFileResolverMock) FilesByBasenameGlob(globs ...string) ([]source.Location, error) { panic("not implemented") } diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index e0171efb504..0851f483e7d 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -433,62 +433,68 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) { return result, nil } -func (r directoryResolver) FilesByExtension(extension string) ([]Location, error) { +func (r directoryResolver) FilesByExtension(extensions ...string) ([]Location, error) { result := make([]Location, 0) - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/*"+extension, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) + for _, extension := range extensions { + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/*"+extension, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } } return result, nil } -func (r directoryResolver) FilesByBasename(filename string) ([]Location, error) { +func (r directoryResolver) FilesByBasename(filenames ...string) ([]Location, error) { result := make([]Location, 0) - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) + for _, filename := range filenames { + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } } return result, nil } // TODO: duplicate code with FilesByBasename -func (r directoryResolver) FilesByBasenameGlob(filename string) ([]Location, error) { +func (r directoryResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { result := make([]Location, 0) - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) + for _, glob := range globs { + // TODO: is there a faster way to do this? + globResults, err := r.fileTree.FilesByGlob("**/"+glob, filetree.FollowBasenameLinks) + if err != nil { + return nil, err + } + for _, globResult := range globResults { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root + globResult.Reference, + ) + result = append(result, loc) + } } return result, nil diff --git a/syft/source/excluding_file_resolver.go b/syft/source/excluding_file_resolver.go index e0c7c093338..3207b5a9aec 100644 --- a/syft/source/excluding_file_resolver.go +++ b/syft/source/excluding_file_resolver.go @@ -59,19 +59,19 @@ func (r *excludingResolver) FilesByMIMEType(types ...string) ([]Location, error) return filterLocations(locations, err, r.excludeFn) } -func (r *excludingResolver) FilesByExtension(extension string) ([]Location, error) { - // TODO implement me - panic("implement me") +func (r *excludingResolver) FilesByExtension(extensions ...string) ([]Location, error) { + locations, err := r.delegate.FilesByExtension(extensions...) + return filterLocations(locations, err, r.excludeFn) } -func (r *excludingResolver) FilesByBasename(filename string) ([]Location, error) { - // TODO implement me - panic("implement me") +func (r *excludingResolver) FilesByBasename(filenames ...string) ([]Location, error) { + locations, err := r.delegate.FilesByBasename(filenames...) + return filterLocations(locations, err, r.excludeFn) } -func (r *excludingResolver) FilesByBasenameGlob(glob string) ([]Location, error) { - // TODO implement me - panic("implement me") +func (r *excludingResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { + locations, err := r.delegate.FilesByBasenameGlob(globs...) + return filterLocations(locations, err, r.excludeFn) } func (r *excludingResolver) RelativeFileByPath(location Location, path string) *Location { diff --git a/syft/source/excluding_file_resolver_test.go b/syft/source/excluding_file_resolver_test.go index 90d30dfe032..9b8b3325134 100644 --- a/syft/source/excluding_file_resolver_test.go +++ b/syft/source/excluding_file_resolver_test.go @@ -67,6 +67,15 @@ func TestExcludingResolver(t *testing.T) { locations, _ = er.FilesByMIMEType() assert.ElementsMatch(t, locationPaths(locations), test.expected) + locations, _ = er.FilesByExtension() + assert.ElementsMatch(t, locationPaths(locations), test.expected) + + locations, _ = er.FilesByBasename() + assert.ElementsMatch(t, locationPaths(locations), test.expected) + + locations, _ = er.FilesByBasenameGlob() + assert.ElementsMatch(t, locationPaths(locations), test.expected) + locations = []Location{} channel := er.AllLocations() @@ -142,18 +151,6 @@ type mockResolver struct { locations []string } -func (r *mockResolver) FilesByExtension(extension string) ([]Location, error) { - panic("not implemented") -} - -func (r *mockResolver) FilesByBasename(filename string) ([]Location, error) { - panic("not implemented") -} - -func (r *mockResolver) FilesByBasenameGlob(glob string) ([]Location, error) { - panic("not implemented") -} - func (r *mockResolver) getLocations() ([]Location, error) { out := []Location{} for _, path := range r.locations { @@ -188,6 +185,18 @@ func (r *mockResolver) FilesByMIMEType(_ ...string) ([]Location, error) { return r.getLocations() } +func (r *mockResolver) FilesByExtension(_ ...string) ([]Location, error) { + return r.getLocations() +} + +func (r *mockResolver) FilesByBasename(_ ...string) ([]Location, error) { + return r.getLocations() +} + +func (r *mockResolver) FilesByBasenameGlob(_ ...string) ([]Location, error) { + return r.getLocations() +} + func (r *mockResolver) RelativeFileByPath(_ Location, path string) *Location { return &Location{ Coordinates: Coordinates{ diff --git a/syft/source/file_resolver.go b/syft/source/file_resolver.go index c8565fc2e62..a16daf30bcd 100644 --- a/syft/source/file_resolver.go +++ b/syft/source/file_resolver.go @@ -30,11 +30,11 @@ type FilePathResolver interface { // FilesByGlob fetches a set of file references for the given glob matches FilesByGlob(patterns ...string) ([]Location, error) // FilesByExtension fetches a set of file references for the given file extensions - FilesByExtension(extension string) ([]Location, error) + FilesByExtension(extensions ...string) ([]Location, error) // FilesByBasename fetches a set of file references for the given filenames - FilesByBasename(filename string) ([]Location, error) + FilesByBasename(basenames ...string) ([]Location, error) // FilesByBasenameGlob fetches a set of file references for the given filename glob patterns (e.g. *requirements*.txt) - FilesByBasenameGlob(glob string) ([]Location, error) + FilesByBasenameGlob(patterns ...string) ([]Location, error) // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types FilesByMIMEType(types ...string) ([]Location, error) // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index f979f2204f6..b2a2c2d4448 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -221,54 +221,60 @@ func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, e return locations, nil } -func (r *imageAllLayersResolver) FilesByExtension(extension string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByExtension(extensions ...string) ([]Location, error) { var locations []Location - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] + for _, extension := range extensions { + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] - refs, err := layer.FilesByExtension(extension) - if err != nil { - return nil, err - } + refs, err := layer.FilesByExtension(extension) + if err != nil { + return nil, err + } - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } } return locations, nil } -func (r *imageAllLayersResolver) FilesByBasename(filename string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByBasename(filenames ...string) ([]Location, error) { var locations []Location - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] + for _, filename := range filenames { + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] - refs, err := layer.FilesByBasename(filename) - if err != nil { - return nil, err - } + refs, err := layer.FilesByBasename(filename) + if err != nil { + return nil, err + } - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } } return locations, nil } -func (r *imageAllLayersResolver) FilesByBasenameGlob(glob string) ([]Location, error) { +func (r *imageAllLayersResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { var locations []Location - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] + for _, glob := range globs { + for _, layerIdx := range r.layers { + layer := r.img.Layers[layerIdx] - refs, err := layer.FilesByBasenameGlob(glob) - if err != nil { - return nil, err - } + refs, err := layer.FilesByBasenameGlob(glob) + if err != nil { + return nil, err + } - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } } diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index 07d70c5edd6..e99b10f6e9a 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -189,43 +189,50 @@ func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, erro return locations, nil } -func (r *imageSquashResolver) FilesByExtension(extension string) ([]Location, error) { - refs, err := r.img.FilesByExtensionFromSquash(extension) - if err != nil { - return nil, err - } - +func (r *imageSquashResolver) FilesByExtension(extensions ...string) ([]Location, error) { var locations []Location - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + for _, extension := range extensions { + refs, err := r.img.FilesByExtensionFromSquash(extension) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } return locations, nil } -func (r *imageSquashResolver) FilesByBasename(filename string) ([]Location, error) { - refs, err := r.img.FilesByBasenameFromSquash(filename) - if err != nil { - return nil, err - } - +func (r *imageSquashResolver) FilesByBasename(basenames ...string) ([]Location, error) { var locations []Location - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + for _, basename := range basenames { + refs, err := r.img.FilesByBasenameFromSquash(basename) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } return locations, nil } -func (r *imageSquashResolver) FilesByBasenameGlob(glob string) ([]Location, error) { - refs, err := r.img.FilesByBasenameGlobFromSquash(glob) - if err != nil { - return nil, err - } - +func (r *imageSquashResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { var locations []Location - for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + + for _, glob := range globs { + refs, err := r.img.FilesByBasenameGlobFromSquash(glob) + if err != nil { + return nil, err + } + + for _, ref := range refs { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + } } return locations, nil diff --git a/syft/source/mock_resolver.go b/syft/source/mock_resolver.go index 9e44abc200d..f9af1ed2958 100644 --- a/syft/source/mock_resolver.go +++ b/syft/source/mock_resolver.go @@ -181,15 +181,23 @@ func (r MockResolver) FilesByMIMEType(types ...string) ([]Location, error) { return locations, nil } -func (r MockResolver) FilesByExtension(extension string) ([]Location, error) { - return r.extension[extension], nil +func (r MockResolver) FilesByExtension(extensions ...string) ([]Location, error) { + var results []Location + for _, ext := range extensions { + results = append(results, r.extension[ext]...) + } + return results, nil } -func (r MockResolver) FilesByBasename(filename string) ([]Location, error) { - return r.basename[filename], nil +func (r MockResolver) FilesByBasename(filenames ...string) ([]Location, error) { + var results []Location + for _, filename := range filenames { + results = append(results, r.basename[filename]...) + } + return results, nil } -func (r MockResolver) FilesByBasenameGlob(glob string) ([]Location, error) { +func (r MockResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { // TODO implement me panic("implement me") } From 81dea3adf740beba28a6454b215b36ed7051a882 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:49:44 -0500 Subject: [PATCH 32/54] dont allow * prefix on extension searches Signed-off-by: Alex Goodman --- syft/pkg/cataloger/generic/search.go | 4 ++++ syft/pkg/cataloger/generic/search_test.go | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/syft/pkg/cataloger/generic/search.go b/syft/pkg/cataloger/generic/search.go index 0c1483879a1..8cdc0332ba4 100644 --- a/syft/pkg/cataloger/generic/search.go +++ b/syft/pkg/cataloger/generic/search.go @@ -2,6 +2,7 @@ package generic import ( "fmt" + "strings" "github.com/bmatcuk/doublestar/v4" @@ -133,6 +134,9 @@ func (s SearchRequest) Execute(resolver source.FileResolver) ([]source.Location, return nil, fmt.Errorf("unable to process search basis basename=%q: %w", s.basename, err) } case s.extension != "": + if strings.HasPrefix(s.extension, "*") { + return nil, fmt.Errorf("extensions must not include a leading wildcard: %q", s.extension) + } locations, err = resolver.FilesByExtension(s.extension) if err != nil { return nil, fmt.Errorf("unable to process search basis extension=%q: %w", s.extension, err) diff --git a/syft/pkg/cataloger/generic/search_test.go b/syft/pkg/cataloger/generic/search_test.go index acc3b755288..75fc822e684 100644 --- a/syft/pkg/cataloger/generic/search_test.go +++ b/syft/pkg/cataloger/generic/search_test.go @@ -155,6 +155,29 @@ func TestSearchRequest_Execute(t *testing.T) { } } +func TestSearchRequest_Execute_ExceptionWithWildcard(t *testing.T) { + + tests := []struct { + name string + request SearchRequest + responsePath string + wantPathQueries map[string][]string + wantLocations []source.Location + }{ + { + name: "search by extension", + request: NewSearch().ByExtension("*.txt").Request(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resolver := srctest.NewObservingResolver(&nopResolver{}) + _, err := tt.request.Execute(resolver) + require.Error(t, err) + }) + } +} + var _ source.FileResolver = (*nopResolver)(nil) type nopResolver struct { From 402132d1426432b57902b48ebc5c98f541593afe Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:50:35 -0500 Subject: [PATCH 33/54] add glob-based cataloger tests for ruby cataloger Signed-off-by: Alex Goodman --- syft/pkg/cataloger/ruby/catalogers.go | 2 +- syft/pkg/cataloger/ruby/catalogers_test.go | 58 +++++++++++++++++++ .../specifications/pkg/nested.gemspec | 1 + .../glob-paths/specifications/root.gemspec | 1 + .../test-fixtures/glob-paths/src/Gemfile.lock | 1 + 5 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 syft/pkg/cataloger/ruby/catalogers_test.go create mode 100644 syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/pkg/nested.gemspec create mode 100644 syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/root.gemspec create mode 100644 syft/pkg/cataloger/ruby/test-fixtures/glob-paths/src/Gemfile.lock diff --git a/syft/pkg/cataloger/ruby/catalogers.go b/syft/pkg/cataloger/ruby/catalogers.go index 61cde77575b..7007e309b4e 100644 --- a/syft/pkg/cataloger/ruby/catalogers.go +++ b/syft/pkg/cataloger/ruby/catalogers.go @@ -17,6 +17,6 @@ func NewGemFileLockCataloger() *generic.Cataloger { func NewGemSpecCataloger() *generic.Cataloger { return generic.NewCataloger("ruby-gemspec-cataloger"). WithParser(parseGemSpecEntries, - generic.NewSearch().ByExtension("*.gemspec").MustMatchGlob("**/specifications/**/*.gemspec"), + generic.NewSearch().ByExtension(".gemspec").MustMatchGlob("**/specifications/**/*.gemspec"), ) } diff --git a/syft/pkg/cataloger/ruby/catalogers_test.go b/syft/pkg/cataloger/ruby/catalogers_test.go new file mode 100644 index 00000000000..3d79c7a9375 --- /dev/null +++ b/syft/pkg/cataloger/ruby/catalogers_test.go @@ -0,0 +1,58 @@ +package ruby + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_GemFileLock_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain gemfile lock files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "src/Gemfile.lock", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsResolverContentQueries(test.expected). + TestCataloger(t, NewGemFileLockCataloger()) + }) + } +} + +func Test_GemSpec_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain gemspec files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "specifications/root.gemspec", + "specifications/pkg/nested.gemspec", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsResolverContentQueries(test.expected). + TestCataloger(t, NewGemSpecCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/pkg/nested.gemspec b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/pkg/nested.gemspec new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/pkg/nested.gemspec @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/root.gemspec b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/root.gemspec new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/specifications/root.gemspec @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/src/Gemfile.lock b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/src/Gemfile.lock new file mode 100644 index 00000000000..882b6040c5d --- /dev/null +++ b/syft/pkg/cataloger/ruby/test-fixtures/glob-paths/src/Gemfile.lock @@ -0,0 +1 @@ +bogus \ No newline at end of file From cc8c14f6686e440f619e61ee18423605f4e5da20 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 25 Jan 2023 15:50:51 -0500 Subject: [PATCH 34/54] remove unnecessary string casting Signed-off-by: Alex Goodman --- test/integration/catalog_packages_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index df73d14ed1e..2251e2fab46 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -67,7 +67,7 @@ func TestPkgCoverageImage(t *testing.T) { definedLanguages.Remove(pkg.Rust.String()) definedLanguages.Remove(pkg.Dart.String()) definedLanguages.Remove(pkg.Dotnet.String()) - definedLanguages.Remove(string(pkg.Swift.String())) + definedLanguages.Remove(pkg.Swift.String()) definedLanguages.Remove(pkg.CPP.String()) definedLanguages.Remove(pkg.Haskell.String()) definedLanguages.Remove(pkg.Erlang.String()) From bfc9efd2c4602fb7283c32e50e09a03c4010e77e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 27 Jan 2023 17:43:08 -0500 Subject: [PATCH 35/54] incorporate surfacing of leaf link resolitions from stereoscope results Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 +- syft/file/all_regular_files_test.go | 5 +- syft/file/digest_cataloger_test.go | 2 +- syft/file/metadata_cataloger_test.go | 2 +- syft/formats/common/testutils/utils.go | 4 +- syft/source/directory_resolver.go | 28 +- syft/source/directory_resolver_test.go | 277 ++++++++++++++++++ syft/source/image_all_layers_resolver.go | 34 ++- syft/source/image_all_layers_resolver_test.go | 136 ++++++++- syft/source/image_squash_resolver.go | 43 +-- syft/source/image_squash_resolver_test.go | 171 ++++++++++- .../test-fixtures/image-symlinks/Dockerfile | 17 +- .../file-1.txt | 1 + .../file-2.txt | 1 + .../file-3.txt | 1 + .../link-1 | 1 + .../link-2 | 1 + .../link-dead | 1 + .../link-indirect | 1 + .../link-within | 1 + .../parent-link | 1 + .../parent/file-4.txt | 1 + 23 files changed, 660 insertions(+), 75 deletions(-) create mode 100644 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-1.txt create mode 100644 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-2.txt create mode 100644 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-3.txt create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-1 create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-2 create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-dead create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-indirect create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-within create mode 120000 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent-link create mode 100644 syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent/file-4.txt diff --git a/go.mod b/go.mod index bfdfc3407fb..55e0218d634 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb + github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085 github.com/docker/docker v20.10.23+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 diff --git a/go.sum b/go.sum index a65f40ab188..72eebc4b93d 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb h1:fZhCHUYNl/+rf44PhwgeiLYlC2eL7YMj/Zy9s2tM/iA= -github.com/anchore/stereoscope v0.0.0-20230125132855-afdd50afaddb/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= +github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085 h1:hRBM/EL59uj/46sI8DII6UydOPww6ywJPw1lBIdgXn4= +github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= 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= diff --git a/syft/file/all_regular_files_test.go b/syft/file/all_regular_files_test.go index d1e261652fc..096480721a5 100644 --- a/syft/file/all_regular_files_test.go +++ b/syft/file/all_regular_files_test.go @@ -3,6 +3,7 @@ package file import ( "testing" + "github.com/google/go-cmp/cmp" "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -69,8 +70,8 @@ func Test_allRegularFiles(t *testing.T) { virtualLocations.Add(l.VirtualPath) } } - assert.ElementsMatch(t, tt.wantRealPaths.List(), realLocations.List(), "mismatched real paths") - assert.ElementsMatch(t, tt.wantVirtualPaths.List(), virtualLocations.List(), "mismatched virtual paths") + assert.ElementsMatch(t, tt.wantRealPaths.List(), realLocations.List(), "real paths differ: "+cmp.Diff(tt.wantRealPaths.List(), realLocations.List())) + assert.ElementsMatch(t, tt.wantVirtualPaths.List(), virtualLocations.List(), "virtual paths differ: "+cmp.Diff(tt.wantVirtualPaths.List(), virtualLocations.List())) }) } } diff --git a/syft/file/digest_cataloger_test.go b/syft/file/digest_cataloger_test.go index dc017fbf957..57a1a8071ee 100644 --- a/syft/file/digest_cataloger_test.go +++ b/syft/file/digest_cataloger_test.go @@ -146,7 +146,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) { if err != nil { t.Fatalf("unable to get file=%q : %+v", test.path, err) } - l := source.NewLocationFromImage(test.path, *ref, img) + l := source.NewLocationFromImage(test.path, *ref.Reference, img) if len(actual[l.Coordinates]) == 0 { if test.expected != "" { diff --git a/syft/file/metadata_cataloger_test.go b/syft/file/metadata_cataloger_test.go index c7633bb7c8c..7a04baf89d6 100644 --- a/syft/file/metadata_cataloger_test.go +++ b/syft/file/metadata_cataloger_test.go @@ -135,7 +135,7 @@ func TestFileMetadataCataloger(t *testing.T) { t.Fatalf("unable to get file: %+v", err) } - l := source.NewLocationFromImage(test.path, *ref, img) + l := source.NewLocationFromImage(test.path, *ref.Reference, img) assert.Equal(t, test.expected, actual[l.Coordinates], "mismatched metadata") diff --git a/syft/formats/common/testutils/utils.go b/syft/formats/common/testutils/utils.go index 39090d29192..f214c4f07f3 100644 --- a/syft/formats/common/testutils/utils.go +++ b/syft/formats/common/testutils/utils.go @@ -168,7 +168,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) { Name: "package-1", Version: "1.0.1", Locations: source.NewLocationSet( - source.NewLocationFromImage(string(ref1.RealPath), *ref1, img), + source.NewLocationFromImage(string(ref1.RealPath), *ref1.Reference, img), ), Type: pkg.PythonPkg, FoundBy: "the-cataloger-1", @@ -188,7 +188,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) { Name: "package-2", Version: "2.0.1", Locations: source.NewLocationSet( - source.NewLocationFromImage(string(ref2.RealPath), *ref2, img), + source.NewLocationFromImage(string(ref2.RealPath), *ref2.Reference, img), ), Type: pkg.DebPkg, FoundBy: "the-cataloger-2", diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index 0851f483e7d..f6af81ab26a 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -214,7 +214,7 @@ func (r directoryResolver) hasBeenIndexed(p string) bool { } exists, ref, err := r.fileTree.File(filePath) - if err != nil || !exists || ref == nil { + if err != nil || !exists || !ref.HasReference() { return false } @@ -279,11 +279,19 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string } } + // previouslyIndexedDestination := r.hasBeenIndexed(linkTarget) + ref, err := r.fileTree.AddSymLink(file.Path(p), file.Path(linkTarget)) if err != nil { return "", err } + // if previouslyIndexedDestination { + // // this symlink is pointing to a spot on the filesystem that has already been indexed. We don't want to continue + // // searching this branch + // return "", nil + //} + targetAbsPath := linkTarget if !filepath.IsAbs(targetAbsPath) { targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) @@ -291,7 +299,7 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string location := NewLocationFromDirectory(p, *ref) location.VirtualPath = p - metadata := fileMetadataFromPath(p, usedInfo, r.isInIndex(location)) + metadata := fileMetadataFromPath(p, usedInfo, false) // note: to be consistent with other resolvers, don't record mime type for symlink destinations metadata.LinkDestination = linkTarget r.addFileMetadataToIndex(ref, metadata) @@ -400,12 +408,15 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) userStrPath = windowsToPosix(userStrPath) } - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root - r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root - *ref, - ) - references = append(references, loc) + if ref.HasReference() { + references = append(references, + NewVirtualLocationFromDirectory( + r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root + r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root + *ref.Reference, + ), + ) + } } return references, nil @@ -477,7 +488,6 @@ func (r directoryResolver) FilesByBasename(filenames ...string) ([]Location, err return result, nil } -// TODO: duplicate code with FilesByBasename func (r directoryResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { result := make([]Location, 0) diff --git a/syft/source/directory_resolver_test.go b/syft/source/directory_resolver_test.go index 7305c225b32..95ce43243b6 100644 --- a/syft/source/directory_resolver_test.go +++ b/syft/source/directory_resolver_test.go @@ -957,4 +957,281 @@ func TestDirectoryResolver_FilesByPath_baseRoot(t *testing.T) { assert.ElementsMatch(t, c.expected, s.List()) }) } + +} + +func Test_directoryResolver_resolvesLinks(t *testing.T) { + tests := []struct { + name string + runner func(FileResolver) []Location + expected []Location + }{ + { + name: "by mimetype", + runner: func(resolver FileResolver) []Location { + // links should not show up when searching mimetype + actualLocations, err := resolver.FilesByMIMEType("text/plain") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "file-1.txt", + }, + //VirtualPath: "file-1.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-3.txt", + }, + //VirtualPath: "file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + //VirtualPath: "file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "parent/file-4.txt", + }, + //VirtualPath: "parent/file-4.txt", + }, + }, + }, + { + name: "by glob to links", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + // for that reason we need to place **/ in front (which is not the same for other resolvers) + actualLocations, err := resolver.FilesByGlob("**/*ink-*") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "file-1.txt", + }, + VirtualPath: "link-1", + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-2", + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-indirect", + }, + { + Coordinates: Coordinates{ + RealPath: "file-3.txt", + }, + VirtualPath: "link-within", + }, + }, + }, + { + name: "by basename", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasename("file-2.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + // this has two copies in the base image, which overwrites the same location + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + //VirtualPath: "file-2.txt", + }, + }, + }, + { + name: "by basename glob", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "file-1.txt", + }, + //VirtualPath: "file-1.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + //VirtualPath: "file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-3.txt", + }, + //VirtualPath: "file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "parent/file-4.txt", + }, + //VirtualPath: "parent/file-4.txt", + }, + // note: this is a unique entry relative to the other resolvers. The other resolvers by nature + // do not walk full paths with symlinks, they treat symlinks as a single file. This resolver + // walks a real filsystem thus symlinks are followed and the same file can be found twice. + + // TODO: this is NOT good, since we should not be following symlinks in this case. This needs to be fixed. + { + Coordinates: Coordinates{ + RealPath: "parent/file-4.txt", + }, + VirtualPath: "parent-link/file-4.txt", + }, + }, + }, + { + name: "by basename glob to links", + runner: func(resolver FileResolver) []Location { + actualLocations, err := resolver.FilesByBasenameGlob("link-*") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "file-1.txt", + }, + VirtualPath: "link-1", + ref: file.Reference{RealPath: "file-1.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-2", + ref: file.Reference{RealPath: "file-2.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-indirect", + ref: file.Reference{RealPath: "file-2.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "file-3.txt", + }, + VirtualPath: "link-within", + ref: file.Reference{RealPath: "file-3.txt"}, + }, + }, + }, + { + name: "by extension", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByExtension(".txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "file-1.txt", + }, + //VirtualPath: "file-1.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + //VirtualPath: "file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "file-3.txt", + }, + //VirtualPath: "file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "parent/file-4.txt", + }, + //VirtualPath: "parent/file-4.txt", + }, + // note: this is a unique entry relative to the other resolvers. The other resolvers by nature + // do not walk full paths with symlinks, they treat symlinks as a single file. This resolver + // walks a real filsystem thus symlinks are followed and the same file can be found twice. + + // TODO: this is NOT good, since we should not be following symlinks in this case. This needs to be fixed. + { + Coordinates: Coordinates{ + RealPath: "parent/file-4.txt", + }, + VirtualPath: "parent-link/file-4.txt", + }, + }, + }, + { + name: "by path to degree 1 link", + runner: func(resolver FileResolver) []Location { + // links resolve to the final file + actualLocations, err := resolver.FilesByPath("/link-2") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + // we have multiple copies across layers + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-2", + }, + }, + }, + { + name: "by path to degree 2 link", + runner: func(resolver FileResolver) []Location { + // multiple links resolves to the final file + actualLocations, err := resolver.FilesByPath("/link-indirect") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + // we have multiple copies across layers + { + Coordinates: Coordinates{ + RealPath: "file-2.txt", + }, + VirtualPath: "link-indirect", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + resolver, err := newDirectoryResolver("./test-fixtures/symlinks-from-image-symlinks-fixture", "") + require.NoError(t, err) + assert.NoError(t, err) + + actual := test.runner(resolver) + + compareLocations(t, test.expected, actual) + }) + } } diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index b2a2c2d4448..2ec23045c9b 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -64,9 +64,9 @@ func (r *imageAllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs fil if err != nil { return nil, fmt.Errorf("failed to resolve link from layer (layer=%d ref=%+v): %w", subLayerIdx, ref, err) } - if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) { - uniqueFileIDs.Add(*resolvedRef) - uniqueFiles = append(uniqueFiles, *resolvedRef) + if resolvedRef.HasReference() && !uniqueFileIDs.Contains(*resolvedRef.Reference) { + uniqueFileIDs.Add(*resolvedRef.Reference) + uniqueFiles = append(uniqueFiles, *resolvedRef.Reference) } } } else if !uniqueFileIDs.Contains(ref) { @@ -89,7 +89,7 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error if err != nil { return nil, err } - if ref == nil { + if !ref.HasReference() { // no file found, keep looking through layers continue } @@ -97,8 +97,8 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error // don't consider directories (special case: there is no path information for /) if ref.RealPath == "/" { continue - } else if r.img.FileCatalog.Exists(*ref) { - metadata, err := r.img.FileCatalog.Get(*ref) + } else if r.img.FileCatalog.Exists(*ref.Reference) { + metadata, err := r.img.FileCatalog.Get(*ref.Reference) if err != nil { return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err) } @@ -107,7 +107,7 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error } } - results, err := r.fileByRef(*ref, uniqueFileIDs, idx) + results, err := r.fileByRef(*ref.Reference, uniqueFileIDs, idx) if err != nil { return nil, err } @@ -172,11 +172,11 @@ func (r *imageAllLayersResolver) RelativeFileByPath(location Location, path stri log.Errorf("failed to find path=%q in squash: %+w", path, err) return nil } - if !exists && relativeRef == nil { + if !exists && !relativeRef.HasReference() { return nil } - relativeLocation := NewLocationFromImage(path, *relativeRef, r.img) + relativeLocation := NewLocationFromImage(path, *relativeRef.Reference, r.img) return &relativeLocation } @@ -214,7 +214,9 @@ func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, e } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) + } } } @@ -233,7 +235,9 @@ func (r *imageAllLayersResolver) FilesByExtension(extensions ...string) ([]Locat } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) + } } } } @@ -253,7 +257,9 @@ func (r *imageAllLayersResolver) FilesByBasename(filenames ...string) ([]Locatio } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) + } } } } @@ -273,7 +279,9 @@ func (r *imageAllLayersResolver) FilesByBasenameGlob(globs ...string) ([]Locatio } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) + } } } } diff --git a/syft/source/image_all_layers_resolver_test.go b/syft/source/image_all_layers_resolver_test.go index 2eb17b82b24..50b690c4fe3 100644 --- a/syft/source/image_all_layers_resolver_test.go +++ b/syft/source/image_all_layers_resolver_test.go @@ -437,7 +437,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { }, }, { - name: "by glob", + name: "by glob to links", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files actualLocations, err := resolver.FilesByGlob("*ink-*") @@ -473,6 +473,131 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { }, }, }, + { + name: "by basename", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasename("file-2.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + // copy 1 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + // copy 2 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + }, + }, + { + name: "by basename glob", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "/file-1.txt", + }, + VirtualPath: "/file-1.txt", + }, + // copy 1 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + // copy 2 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-3.txt", + }, + VirtualPath: "/file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", + }, + // when we copy into the link path, the same file-4.txt is copied + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", + }, + }, + }, + { + name: "by extension", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByExtension(".txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "/file-1.txt", + }, + VirtualPath: "/file-1.txt", + }, + // copy 1 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + // copy 2 + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-3.txt", + }, + VirtualPath: "/file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", + }, + // when we copy into the link path, the same file-4.txt is copied + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", + }, + }, + }, { name: "by path to degree 1 link", runner: func(resolver FileResolver) []Location { @@ -531,12 +656,9 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { resolver, err := newAllLayersResolver(img) assert.NoError(t, err) - actualLocations := test.runner(resolver) - assert.Len(t, actualLocations, len(test.expected)) - for i, actual := range actualLocations { - assert.Equal(t, test.expected[i].RealPath, actual.RealPath) - assert.Equal(t, test.expected[i].VirtualPath, actual.VirtualPath) - } + actual := test.runner(resolver) + + compareLocations(t, test.expected, actual) }) } diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index e99b10f6e9a..863fe231c68 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -44,7 +44,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { if err != nil { return nil, err } - if ref == nil { + if !ref.HasReference() { // no file found, keep looking through layers continue } @@ -52,8 +52,8 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { // don't consider directories (special case: there is no path information for /) if ref.RealPath == "/" { continue - } else if r.img.FileCatalog.Exists(*ref) { - metadata, err := r.img.FileCatalog.Get(*ref) + } else if r.img.FileCatalog.Exists(*ref.Reference) { + metadata, err := r.img.FileCatalog.Get(*ref.Reference) if err != nil { return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err) } @@ -63,14 +63,14 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { } // a file may be a symlink, process it as such and resolve it - resolvedRef, err := r.img.ResolveLinkByImageSquash(*ref) + resolvedRef, err := r.img.ResolveLinkByImageSquash(*ref.Reference) if err != nil { return nil, fmt.Errorf("failed to resolve link from img (ref=%+v): %w", ref, err) } - if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) { - uniqueFileIDs.Add(*resolvedRef) - uniqueLocations = append(uniqueLocations, NewLocationFromImage(path, *resolvedRef, r.img)) + if resolvedRef.HasReference() && !uniqueFileIDs.Contains(*resolvedRef.Reference) { + uniqueFileIDs.Add(*resolvedRef.Reference) + uniqueLocations = append(uniqueLocations, NewLocationFromImage(path, *resolvedRef.Reference, r.img)) } } @@ -79,9 +79,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { // FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image. func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) { - uniqueFileIDs := file.NewFileReferenceSet() - uniqueLocations := make([]Location, 0) - + var locations []Location for _, pattern := range patterns { results, err := r.img.SquashedTree().FilesByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { @@ -108,16 +106,11 @@ func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error if err != nil { return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err) } - for _, resolvedLocation := range resolvedLocations { - if !uniqueFileIDs.Contains(resolvedLocation.ref) { - uniqueFileIDs.Add(resolvedLocation.ref) - uniqueLocations = append(uniqueLocations, resolvedLocation) - } - } + locations = append(locations, resolvedLocations...) } } - return uniqueLocations, nil + return locations, nil } // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. @@ -183,7 +176,9 @@ func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, erro var locations []Location for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) + } } return locations, nil @@ -198,7 +193,9 @@ func (r *imageSquashResolver) FilesByExtension(extensions ...string) ([]Location } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) + } } } @@ -214,7 +211,9 @@ func (r *imageSquashResolver) FilesByBasename(basenames ...string) ([]Location, } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) + } } } @@ -231,7 +230,9 @@ func (r *imageSquashResolver) FilesByBasenameGlob(globs ...string) ([]Location, } for _, ref := range refs { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), ref, r.img)) + if ref.HasReference() { + locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) + } } } diff --git a/syft/source/image_squash_resolver_test.go b/syft/source/image_squash_resolver_test.go index 7bb7c958769..1c82eac4d66 100644 --- a/syft/source/image_squash_resolver_test.go +++ b/syft/source/image_squash_resolver_test.go @@ -2,12 +2,16 @@ package source import ( "io" + "sort" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" ) @@ -344,6 +348,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { }) } } + func Test_imageSquashResolver_resolvesLinks(t *testing.T) { tests := []struct { name string @@ -404,7 +409,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { }, }, { - name: "by glob", + name: "by glob to links", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files actualLocations, err := resolver.FilesByGlob("*ink-*") @@ -412,23 +417,155 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { return actualLocations }, expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "/file-1.txt", + }, + VirtualPath: "/link-1", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/link-2", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/link-indirect", + }, { Coordinates: Coordinates{ RealPath: "/file-3.txt", }, VirtualPath: "/link-within", }, + }, + }, + { + name: "by basename", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasename("file-2.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + // this has two copies in the base image, which overwrites the same location { Coordinates: Coordinates{ RealPath: "/file-2.txt", }, - VirtualPath: "/link-2", + VirtualPath: "/file-2.txt", }, + }, + }, + { + name: "by basename glob", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "/file-1.txt", + }, + VirtualPath: "/file-1.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-3.txt", + }, + VirtualPath: "/file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", + }, + }, + }, + { + name: "by basename glob to links", + runner: func(resolver FileResolver) []Location { + actualLocations, err := resolver.FilesByBasenameGlob("link-*") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ { Coordinates: Coordinates{ RealPath: "/file-1.txt", }, VirtualPath: "/link-1", + ref: file.Reference{RealPath: "/file-1.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/link-2", + ref: file.Reference{RealPath: "/file-2.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/link-indirect", + ref: file.Reference{RealPath: "/file-2.txt"}, + }, + { + Coordinates: Coordinates{ + RealPath: "/file-3.txt", + }, + VirtualPath: "/link-within", + ref: file.Reference{RealPath: "/file-3.txt"}, + }, + }, + }, + { + name: "by extension", + runner: func(resolver FileResolver) []Location { + // links are searched, but resolve to the real files + actualLocations, err := resolver.FilesByExtension(".txt") + assert.NoError(t, err) + return actualLocations + }, + expected: []Location{ + { + Coordinates: Coordinates{ + RealPath: "/file-1.txt", + }, + VirtualPath: "/file-1.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-2.txt", + }, + VirtualPath: "/file-2.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/file-3.txt", + }, + VirtualPath: "/file-3.txt", + }, + { + Coordinates: Coordinates{ + RealPath: "/parent/file-4.txt", + }, + VirtualPath: "/parent/file-4.txt", }, }, }, @@ -478,23 +615,27 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { resolver, err := newImageSquashResolver(img) assert.NoError(t, err) - actualLocations := test.runner(resolver) - require.Len(t, actualLocations, len(test.expected)) + actual := test.runner(resolver) - // some operations on this resolver do not return stable results (order may be different across runs) + compareLocations(t, test.expected, actual) + }) + } - expectedMap := make(map[string]string) - for _, e := range test.expected { - expectedMap[e.VirtualPath] = e.RealPath - } +} - actualMap := make(map[string]string) - for _, a := range test.expected { - actualMap[a.VirtualPath] = a.RealPath - } +func compareLocations(t *testing.T, expected, actual []Location) { + ignoreUnexported := cmpopts.IgnoreFields(Location{}, "ref") + ignoreFS := cmpopts.IgnoreFields(Coordinates{}, "FileSystemID") - assert.Equal(t, expectedMap, actualMap) - }) + sort.Sort(Locations(expected)) + sort.Sort(Locations(actual)) + + if d := cmp.Diff(expected, actual, + ignoreUnexported, + ignoreFS, + ); d != "" { + + t.Errorf("unexpected locations (-want +got):\n%s", d) } } diff --git a/syft/source/test-fixtures/image-symlinks/Dockerfile b/syft/source/test-fixtures/image-symlinks/Dockerfile index afb2a30b32b..3b4e5125f56 100644 --- a/syft/source/test-fixtures/image-symlinks/Dockerfile +++ b/syft/source/test-fixtures/image-symlinks/Dockerfile @@ -9,6 +9,7 @@ RUN ln -s ./file-1.txt link-1 # LAYER 3: link with future data RUN ln -s ./file-2.txt link-2 + # LAYER 4: ADD file-2.txt . @@ -31,4 +32,18 @@ ADD parent /parent RUN ln -s /parent parent-link # LAYER 11: parent is a symlink and the child target is overridden -COPY new-file-4.txt /parent-link/file-4.txt \ No newline at end of file +COPY new-file-4.txt /parent-link/file-4.txt + +# squash representation +# . +# ├── file-1.txt +# ├── file-2.txt +# ├── file-3.txt +# ├── link-1 -> ./file-1.txt +# ├── link-2 -> ./file-2.txt +# ├── link-dead -> [./i-dont-exist.txt] (dead link) +# ├── link-indirect -> ./link-2 +# ├── link-within -> ./file-3.txt +# ├── parent +# │ └── file-4.txt +# └── parent-link -> /parent diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-1.txt b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-1.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-1.txt @@ -0,0 +1 @@ +bogus diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-2.txt b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-2.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-2.txt @@ -0,0 +1 @@ +bogus diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-3.txt b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-3.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/file-3.txt @@ -0,0 +1 @@ +bogus diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-1 b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-1 new file mode 120000 index 00000000000..4036f0fd4d3 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-1 @@ -0,0 +1 @@ +file-1.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-2 b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-2 new file mode 120000 index 00000000000..5042efb8f3b --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-2 @@ -0,0 +1 @@ +file-2.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-dead b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-dead new file mode 120000 index 00000000000..e41fb17fb39 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-dead @@ -0,0 +1 @@ +./i-dont-exist.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-indirect b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-indirect new file mode 120000 index 00000000000..c3e9a545ed5 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-indirect @@ -0,0 +1 @@ +./link-2 \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-within b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-within new file mode 120000 index 00000000000..89dfefbca44 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/link-within @@ -0,0 +1 @@ +file-3.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent-link b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent-link new file mode 120000 index 00000000000..25dd2a9fd67 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent-link @@ -0,0 +1 @@ +parent \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent/file-4.txt b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent/file-4.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-from-image-symlinks-fixture/parent/file-4.txt @@ -0,0 +1 @@ +bogus From 56c42ae52af0c2e4f44b12edb52b25aafd80b482 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 30 Jan 2023 16:58:48 -0500 Subject: [PATCH 36/54] [wip] switch to stereoscope file metadata Signed-off-by: Alex Goodman --- go.mod | 2 ++ go.sum | 2 -- syft/file/all_regular_files.go | 3 ++- syft/file/digest_cataloger.go | 3 ++- syft/formats/formats.go | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 55e0218d634..80d743e3618 100644 --- a/go.mod +++ b/go.mod @@ -157,3 +157,5 @@ retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) + +replace github.com/anchore/stereoscope => ../stereoscope diff --git a/go.sum b/go.sum index 72eebc4b93d..7858078d8c7 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,6 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085 h1:hRBM/EL59uj/46sI8DII6UydOPww6ywJPw1lBIdgXn4= -github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085/go.mod h1:/+siZsCwRngp5KbyTGmRsJ3u/ecVaMubE56vJ4fhuZM= 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= diff --git a/syft/file/all_regular_files.go b/syft/file/all_regular_files.go index e7612c8b242..313c1e91442 100644 --- a/syft/file/all_regular_files.go +++ b/syft/file/all_regular_files.go @@ -1,6 +1,7 @@ package file import ( + "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/source" ) @@ -20,7 +21,7 @@ func allRegularFiles(resolver source.FileResolver) (locations []source.Location) continue } - if metadata.Type != source.RegularFile { + if metadata.Type != file.TypeReg { continue } locations = append(locations, resolvedLocation) diff --git a/syft/file/digest_cataloger.go b/syft/file/digest_cataloger.go index c435dd107c9..d24637f632a 100644 --- a/syft/file/digest_cataloger.go +++ b/syft/file/digest_cataloger.go @@ -4,6 +4,7 @@ import ( "crypto" "errors" "fmt" + "github.com/anchore/stereoscope/pkg/file" "hash" "io" "strings" @@ -65,7 +66,7 @@ func (i *DigestsCataloger) catalogLocation(resolver source.FileResolver, locatio } // we should only attempt to report digests for files that are regular files (don't attempt to resolve links) - if meta.Type != source.RegularFile { + if meta.Type != file.TypeReg { return nil, errUndigestableFile } diff --git a/syft/formats/formats.go b/syft/formats/formats.go index 7275e54a638..6d8f30a300d 100644 --- a/syft/formats/formats.go +++ b/syft/formats/formats.go @@ -38,7 +38,7 @@ func Identify(by []byte) sbom.Format { for _, f := range Formats() { if err := f.Validate(bytes.NewReader(by)); err != nil { if !errors.Is(err, sbom.ErrValidationNotSupported) { - log.Debugf("format %s returned err: %+v", f.ID(), err) + log.WithFields("error", err).Tracef("format validation for %s failed", f.ID()) } continue } From e42eb8552f4ab20488cdf8ffdf5d2eed34d4842e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 31 Jan 2023 10:12:40 -0500 Subject: [PATCH 37/54] [wip + failing] revert to old globs but keep new resolvers Signed-off-by: Alex Goodman --- syft/file/digest_cataloger.go | 2 +- syft/file/metadata_cataloger_test.go | 28 ++- syft/formats/syftjson/model/file.go | 13 +- syft/pkg/cataloger/alpm/cataloger.go | 4 +- syft/pkg/cataloger/apkdb/cataloger.go | 4 +- syft/pkg/cataloger/binary/cataloger.go | 4 +- syft/pkg/cataloger/binary/classifier.go | 5 +- syft/pkg/cataloger/binary/classifier_test.go | 7 +- .../cataloger/binary/default_classifiers.go | 65 +++-- syft/pkg/cataloger/cpp/cataloger.go | 4 +- syft/pkg/cataloger/dart/cataloger.go | 2 +- syft/pkg/cataloger/deb/cataloger.go | 1 - syft/pkg/cataloger/dotnet/cataloger.go | 2 +- syft/pkg/cataloger/elixir/cataloger.go | 2 +- syft/pkg/cataloger/erlang/cataloger.go | 2 +- syft/pkg/cataloger/generic/cataloger.go | 61 ----- syft/pkg/cataloger/generic/search.go | 182 -------------- syft/pkg/cataloger/generic/search_test.go | 229 ------------------ syft/pkg/cataloger/golang/cataloger.go | 2 +- syft/pkg/cataloger/haskell/cataloger.go | 6 +- .../internal/srctest/observing_resolver.go | 33 --- syft/pkg/cataloger/java/archive_parser.go | 27 +-- syft/pkg/cataloger/java/cataloger.go | 8 +- .../java/tar_wrapped_archive_parser.go | 40 +-- .../java/zip_wrapped_archive_parser.go | 4 +- syft/pkg/cataloger/javascript/cataloger.go | 8 +- syft/pkg/cataloger/php/cataloger.go | 4 +- syft/pkg/cataloger/portage/cataloger.go | 2 +- syft/pkg/cataloger/python/cataloger.go | 16 +- syft/pkg/cataloger/rpm/cataloger.go | 12 +- syft/pkg/cataloger/ruby/catalogers.go | 6 +- syft/pkg/cataloger/rust/cataloger.go | 2 +- syft/pkg/cataloger/sbom/cataloger.go | 29 +-- syft/pkg/cataloger/swift/cataloger.go | 2 +- syft/source/directory_resolver.go | 123 +++------- syft/source/directory_resolver_test.go | 8 +- syft/source/excluding_file_resolver.go | 15 -- syft/source/excluding_file_resolver_test.go | 9 - syft/source/file_metadata.go | 26 +- syft/source/file_resolver.go | 6 - syft/source/file_type.go | 70 ------ syft/source/image_all_layers_resolver.go | 105 ++------ syft/source/image_all_layers_resolver_test.go | 25 +- syft/source/image_squash_resolver.go | 84 ++----- syft/source/image_squash_resolver_test.go | 26 +- syft/source/location.go | 16 +- syft/source/mock_resolver.go | 6 +- 47 files changed, 244 insertions(+), 1093 deletions(-) delete mode 100644 syft/pkg/cataloger/generic/search.go delete mode 100644 syft/pkg/cataloger/generic/search_test.go delete mode 100644 syft/source/file_type.go diff --git a/syft/file/digest_cataloger.go b/syft/file/digest_cataloger.go index d24637f632a..5b3bf09c84f 100644 --- a/syft/file/digest_cataloger.go +++ b/syft/file/digest_cataloger.go @@ -4,7 +4,6 @@ import ( "crypto" "errors" "fmt" - "github.com/anchore/stereoscope/pkg/file" "hash" "io" "strings" @@ -12,6 +11,7 @@ import ( "github.com/wagoodman/go-partybus" "github.com/wagoodman/go-progress" + "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/log" diff --git a/syft/file/metadata_cataloger_test.go b/syft/file/metadata_cataloger_test.go index 7a04baf89d6..384ddedbbdc 100644 --- a/syft/file/metadata_cataloger_test.go +++ b/syft/file/metadata_cataloger_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" @@ -50,8 +51,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/file-1.txt", exists: true, expected: source.FileMetadata{ + Path: "/file-1.txt", Mode: 0644, - Type: "RegularFile", + Type: file.TypeReg, UserID: 1, GroupID: 2, Size: 7, @@ -62,8 +64,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/hardlink-1", exists: true, expected: source.FileMetadata{ + Path: "/hardlink-1", Mode: 0644, - Type: "HardLink", + Type: file.TypeHardLink, LinkDestination: "file-1.txt", UserID: 1, GroupID: 2, @@ -74,8 +77,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/symlink-1", exists: true, expected: source.FileMetadata{ + Path: "/symlink-1", Mode: 0777 | os.ModeSymlink, - Type: "SymbolicLink", + Type: file.TypeSymlink, LinkDestination: "file-1.txt", UserID: 0, GroupID: 0, @@ -86,8 +90,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/char-device-1", exists: true, expected: source.FileMetadata{ + Path: "/char-device-1", Mode: 0644 | os.ModeDevice | os.ModeCharDevice, - Type: "CharacterDevice", + Type: file.TypeCharacterDevice, UserID: 0, GroupID: 0, MIMEType: "", @@ -97,8 +102,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/block-device-1", exists: true, expected: source.FileMetadata{ + Path: "/block-device-1", Mode: 0644 | os.ModeDevice, - Type: "BlockDevice", + Type: file.TypeBlockDevice, UserID: 0, GroupID: 0, MIMEType: "", @@ -108,8 +114,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/fifo-1", exists: true, expected: source.FileMetadata{ + Path: "/fifo-1", Mode: 0644 | os.ModeNamedPipe, - Type: "FIFONode", + Type: file.TypeFifo, UserID: 0, GroupID: 0, MIMEType: "", @@ -119,8 +126,9 @@ func TestFileMetadataCataloger(t *testing.T) { path: "/bin", exists: true, expected: source.FileMetadata{ + Path: "/bin", Mode: 0755 | os.ModeDir, - Type: "Directory", + Type: file.TypeDir, UserID: 0, GroupID: 0, MIMEType: "", @@ -130,10 +138,8 @@ func TestFileMetadataCataloger(t *testing.T) { for _, test := range tests { t.Run(test.path, func(t *testing.T) { - _, ref, err := img.SquashedTree().File(file.Path(test.path)) - if err != nil { - t.Fatalf("unable to get file: %+v", err) - } + ref, err := img.SquashedSearchContext().SearchByPath(test.path) + require.NoError(t, err) l := source.NewLocationFromImage(test.path, *ref.Reference, img) diff --git a/syft/formats/syftjson/model/file.go b/syft/formats/syftjson/model/file.go index e230ef127b6..1ef827e2b96 100644 --- a/syft/formats/syftjson/model/file.go +++ b/syft/formats/syftjson/model/file.go @@ -1,6 +1,7 @@ package model import ( + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" ) @@ -14,10 +15,10 @@ type File struct { } type FileMetadataEntry struct { - Mode int `json:"mode"` - Type source.FileType `json:"type"` - LinkDestination string `json:"linkDestination,omitempty"` - UserID int `json:"userID"` - GroupID int `json:"groupID"` - MIMEType string `json:"mimeType"` + Mode int `json:"mode"` + Type stereoscopeFile.Type `json:"type"` + LinkDestination string `json:"linkDestination,omitempty"` + UserID int `json:"userID"` + GroupID int `json:"groupID"` + MIMEType string `json:"mimeType"` } diff --git a/syft/pkg/cataloger/alpm/cataloger.go b/syft/pkg/cataloger/alpm/cataloger.go index a82b6601300..39bc7d816c0 100644 --- a/syft/pkg/cataloger/alpm/cataloger.go +++ b/syft/pkg/cataloger/alpm/cataloger.go @@ -9,7 +9,5 @@ const catalogerName = "alpmdb-cataloger" func NewAlpmdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParser(parseAlpmDB, - generic.NewSearch().ByBasename("desc").MustMatchGlob(pkg.AlpmDBGlob), - ) + WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob) } diff --git a/syft/pkg/cataloger/apkdb/cataloger.go b/syft/pkg/cataloger/apkdb/cataloger.go index 91b2e8a400a..d3cea71263c 100644 --- a/syft/pkg/cataloger/apkdb/cataloger.go +++ b/syft/pkg/cataloger/apkdb/cataloger.go @@ -13,7 +13,5 @@ const catalogerName = "apkdb-cataloger" // NewApkdbCataloger returns a new Alpine DB cataloger object. func NewApkdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParser(parseApkDB, - generic.NewSearch().ByBasename("installed").MustMatchGlob(pkg.ApkDBGlob), - ) + WithParserByGlobs(parseApkDB, pkg.ApkDBGlob) } diff --git a/syft/pkg/cataloger/binary/cataloger.go b/syft/pkg/cataloger/binary/cataloger.go index bfbfa8bee95..a610de19d18 100644 --- a/syft/pkg/cataloger/binary/cataloger.go +++ b/syft/pkg/cataloger/binary/cataloger.go @@ -32,7 +32,7 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif var relationships []artifact.Relationship for _, cls := range defaultClassifiers { - log.WithFields("classifier", cls.Class, "search", cls.SearchRequest.String()).Trace("cataloging binaries") + log.WithFields("classifier", cls.Class).Trace("cataloging binaries") pkgs, err := catalog(resolver, cls) if err != nil { log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err) @@ -46,7 +46,7 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error) { var pkgs []pkg.Package - locations, err := cls.SearchRequest.Execute(resolver) + locations, err := resolver.FilesByGlob(cls.FileGlob) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 94aa4d7a5da..4e89483233d 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -12,7 +12,6 @@ import ( "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader" "github.com/anchore/syft/syft/source" ) @@ -24,8 +23,8 @@ var emptyPURL = packageurl.PackageURL{} type classifier struct { Class string - // SearchRequest specifies how to search for a file (and what full path globs must match for the file to be considered) - generic.SearchRequest + // FileGlob is a selector to narrow down file inspection using the **/glob* syntax + FileGlob string // EvidenceMatcher is what will be used to match against the file in the source // location. If the matcher returns a package, the file will be considered a candidate. diff --git a/syft/pkg/cataloger/binary/classifier_test.go b/syft/pkg/cataloger/binary/classifier_test.go index c21aa20bf5d..cc7d2b0d6aa 100644 --- a/syft/pkg/cataloger/binary/classifier_test.go +++ b/syft/pkg/cataloger/binary/classifier_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/source" ) @@ -22,7 +21,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), + FileGlob: "**/version.txt", EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{}, }, @@ -33,7 +32,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), + FileGlob: "**/version.txt", EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{ cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"), @@ -48,7 +47,7 @@ func Test_ClassifierCPEs(t *testing.T) { fixture: "test-fixtures/version.txt", classifier: classifier{ Package: "some-app", - SearchRequest: generic.NewSearch().ByBasename("version.txt").Request(), + FileGlob: "**/version.txt", EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P[0-9.]+)`), CPEs: []cpe.CPE{ cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"), diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index 5c18d47c5d6..114d9295b40 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -3,13 +3,12 @@ package binary import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/pkg/cataloger/generic" ) var defaultClassifiers = []classifier{ { - Class: "python-binary", - SearchRequest: generic.NewSearch().ByBasenameGlob("python*").Request(), + Class: "python-binary", + FileGlob: "**/python*", EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)python(?P[0-9]+\.[0-9]+)$`, `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), @@ -21,8 +20,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "python-binary-lib", - SearchRequest: generic.NewSearch().ByBasenameGlob("libpython*.so*").Request(), + Class: "python-binary-lib", + FileGlob: "**/libpython*.so*", EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)libpython(?P[0-9]+\.[0-9]+).so.*$`, `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), @@ -34,8 +33,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "cpython-source", - SearchRequest: generic.NewSearch().ByBasename("patchlevel.h").Request(), + Class: "cpython-source", + FileGlob: "**/patchlevel.h", EvidenceMatcher: fileContentsVersionMatcher( `(?m)#define\s+PY_VERSION\s+"?(?P[0-9\.\-_a-zA-Z]+)"?`), Package: "python", @@ -46,8 +45,8 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "go-binary", - SearchRequest: generic.NewSearch().ByBasename("go").Request(), + Class: "go-binary", + FileGlob: "**/go", EvidenceMatcher: fileContentsVersionMatcher( `(?m)go(?P[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)\x00`), Package: "go", @@ -55,8 +54,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"), }, { - Class: "redis-binary", - SearchRequest: generic.NewSearch().ByBasename("redis-server").Request(), + Class: "redis-binary", + FileGlob: "**/redis-server", EvidenceMatcher: fileContentsVersionMatcher( `(?s)payload %5.*(?P\d.\d\.\d\d*?)[a-z0-9]{12}-[0-9]{19}`), Package: "redis", @@ -64,8 +63,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-openjdk", - SearchRequest: generic.NewSearch().ByBasename("java").Request(), + Class: "java-binary-openjdk", + FileGlob: "**/java", EvidenceMatcher: fileContentsVersionMatcher( // [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL] // [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL] @@ -76,8 +75,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-ibm", - SearchRequest: generic.NewSearch().ByBasename("java").Request(), + Class: "java-binary-ibm", + FileGlob: "**/java", EvidenceMatcher: fileContentsVersionMatcher( // [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL] `(?m)\x00java\x00(?P[0-9]+[.0-9]+)\x00{4}(?P[0-9]+[-._a-zA-Z0-9]+)\x00`), @@ -86,8 +85,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"), }, { - Class: "java-binary-oracle", - SearchRequest: generic.NewSearch().ByBasename("java").Request(), + Class: "java-binary-oracle", + FileGlob: "**/java", EvidenceMatcher: fileContentsVersionMatcher( // [NUL]19.0.1+10-21[NUL] `(?m)\x00(?P[0-9]+[.0-9]+[+][-0-9]+)\x00`), @@ -96,8 +95,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"), }, { - Class: "nodejs-binary", - SearchRequest: generic.NewSearch().ByBasename("node").Request(), + Class: "nodejs-binary", + FileGlob: "**/node", EvidenceMatcher: fileContentsVersionMatcher( `(?m)node\.js\/v(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "node", @@ -106,24 +105,24 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*"), }, { - Class: "go-binary-hint", - SearchRequest: generic.NewSearch().ByBasename("VERSION").Request(), + Class: "go-binary-hint", + FileGlob: "**/VERSION", EvidenceMatcher: fileContentsVersionMatcher( `(?m)go(?P[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`), Package: "go", PURL: mustPURL("pkg:generic/go@version"), }, { - Class: "busybox-binary", - SearchRequest: generic.NewSearch().ByBasename("busybox").Request(), + Class: "busybox-binary", + FileGlob: "**/busybox", EvidenceMatcher: fileContentsVersionMatcher( `(?m)BusyBox\s+v(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "busybox", CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"), }, { - Class: "php-cli-binary", - SearchRequest: generic.NewSearch().ByBasenameGlob("php*").Request(), + Class: "php-cli-binary", + FileGlob: "**/php*", EvidenceMatcher: fileNameTemplateVersionMatcher( `(.*/|^)php[0-9]*$`, `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), @@ -132,8 +131,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "php-fpm-binary", - SearchRequest: generic.NewSearch().ByBasenameGlob("php-fpm*").Request(), + Class: "php-fpm-binary", + FileGlob: "**/php-fpm*", EvidenceMatcher: fileContentsVersionMatcher( `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), Package: "php-fpm", @@ -141,8 +140,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "php-apache-binary", - SearchRequest: generic.NewSearch().ByBasenameGlob("libphp*.so").Request(), + Class: "php-apache-binary", + FileGlob: "**/libphp*.so", EvidenceMatcher: fileContentsVersionMatcher( `(?m)X-Powered-By: PHP\/(?P[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`), Package: "libphp", @@ -150,8 +149,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"), }, { - Class: "httpd-binary", - SearchRequest: generic.NewSearch().ByBasename("httpd").Request(), + Class: "httpd-binary", + FileGlob: "**/httpd", EvidenceMatcher: fileContentsVersionMatcher( `(?m)Apache\/(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "httpd", @@ -159,8 +158,8 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*"), }, { - Class: "memcached-binary", - SearchRequest: generic.NewSearch().ByBasename("memcached").Request(), + Class: "memcached-binary", + FileGlob: "**/memcached", EvidenceMatcher: fileContentsVersionMatcher( `(?m)memcached\s(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "memcached", diff --git a/syft/pkg/cataloger/cpp/cataloger.go b/syft/pkg/cataloger/cpp/cataloger.go index 18d54d72fc3..80c6b5b11b2 100644 --- a/syft/pkg/cataloger/cpp/cataloger.go +++ b/syft/pkg/cataloger/cpp/cataloger.go @@ -9,6 +9,6 @@ const catalogerName = "conan-cataloger" // NewConanCataloger returns a new C++ conanfile.txt and conan.lock cataloger object. func NewConanCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByBasename(parseConanfile, "conanfile.txt"). - WithParserByBasename(parseConanlock, "conan.lock") + WithParserByGlobs(parseConanfile, "**/conanfile.txt"). + WithParserByGlobs(parseConanlock, "**/conan.lock") } diff --git a/syft/pkg/cataloger/dart/cataloger.go b/syft/pkg/cataloger/dart/cataloger.go index b077880c815..5fbff6f088a 100644 --- a/syft/pkg/cataloger/dart/cataloger.go +++ b/syft/pkg/cataloger/dart/cataloger.go @@ -9,5 +9,5 @@ const catalogerName = "dartlang-lock-cataloger" // NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files. func NewPubspecLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByBasename(parsePubspecLock, "pubspec.lock") + WithParserByGlobs(parsePubspecLock, "**/pubspec.lock") } diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index d0011b9f0e0..ff9ce84f7bc 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -14,6 +14,5 @@ const catalogerName = "dpkgdb-cataloger" func NewDpkgdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). // TODO: split up and re-write the glob search patterns - // WithParser(parseDpkgDB, generic.NewSearch().ByBasename("status").MustMatchGlob(pkg.DpkgDBGlob)) WithParserByGlobs(parseDpkgDB, pkg.DpkgDBGlob) } diff --git a/syft/pkg/cataloger/dotnet/cataloger.go b/syft/pkg/cataloger/dotnet/cataloger.go index 239bf6ca382..159edcb2743 100644 --- a/syft/pkg/cataloger/dotnet/cataloger.go +++ b/syft/pkg/cataloger/dotnet/cataloger.go @@ -9,5 +9,5 @@ const catalogerName = "dotnet-deps-cataloger" // NewDotnetDepsCataloger returns a new Dotnet cataloger object base on deps json files. func NewDotnetDepsCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByExtensions(parseDotnetDeps, ".deps.json") + WithParserByGlobs(parseDotnetDeps, "**/*.deps.json") } diff --git a/syft/pkg/cataloger/elixir/cataloger.go b/syft/pkg/cataloger/elixir/cataloger.go index 4a9beb98b4a..f4e62effd59 100644 --- a/syft/pkg/cataloger/elixir/cataloger.go +++ b/syft/pkg/cataloger/elixir/cataloger.go @@ -12,5 +12,5 @@ const catalogerName = "elixir-mix-lock-cataloger" // NewMixLockCataloger returns parses mix.lock files and returns packages func NewMixLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByBasename(parseMixLock, "mix.lock") + WithParserByGlobs(parseMixLock, "**/mix.lock") } diff --git a/syft/pkg/cataloger/erlang/cataloger.go b/syft/pkg/cataloger/erlang/cataloger.go index 740f4cce7f0..3e3b54f88e7 100644 --- a/syft/pkg/cataloger/erlang/cataloger.go +++ b/syft/pkg/cataloger/erlang/cataloger.go @@ -12,5 +12,5 @@ const catalogerName = "erlang-rebar-lock-cataloger" // NewRebarLockCataloger returns parses rebar.lock files and returns packages. func NewRebarLockCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - WithParserByBasename(parseRebarLock, "rebar.lock") + WithParserByGlobs(parseRebarLock, "**/rebar.lock") } diff --git a/syft/pkg/cataloger/generic/cataloger.go b/syft/pkg/cataloger/generic/cataloger.go index e40168d33a2..d2069ffff52 100644 --- a/syft/pkg/cataloger/generic/cataloger.go +++ b/syft/pkg/cataloger/generic/cataloger.go @@ -23,27 +23,6 @@ type Cataloger struct { upstreamCataloger string } -func (c *Cataloger) WithParser(parser Parser, searchRequests ...SearchRequest) *Cataloger { - c.processor = append(c.processor, - func(resolver source.FileResolver, env Environment) []request { - var requests []request - for _, req := range searchRequests { - log.Tracef("searching for: %s", req.String()) - - matches, err := req.Execute(resolver) - if err != nil { - log.WithFields("error", err).Warnf("unable to process search request %q", req.String()) - continue - } - - requests = append(requests, makeRequests(parser, matches)...) - } - return requests - }, - ) - return c -} - func (c *Cataloger) WithParserByGlobs(parser Parser, globs ...string) *Cataloger { c.processor = append(c.processor, func(resolver source.FileResolver, env Environment) []request { @@ -101,46 +80,6 @@ func (c *Cataloger) WithParserByPath(parser Parser, paths ...string) *Cataloger return c } -func (c *Cataloger) WithParserByExtensions(parser Parser, extensions ...string) *Cataloger { - c.processor = append(c.processor, - func(resolver source.FileResolver, env Environment) []request { - var requests []request - for _, e := range extensions { - log.WithFields("extension", e).Trace("searching for paths with a matching file extension") - - matches, err := resolver.FilesByExtension(e) - if err != nil { - log.Warnf("unable to process file extension=%q: %+v", e, err) - continue - } - requests = append(requests, makeRequests(parser, matches)...) - } - return requests - }, - ) - return c -} - -func (c *Cataloger) WithParserByBasename(parser Parser, filenames ...string) *Cataloger { - c.processor = append(c.processor, - func(resolver source.FileResolver, env Environment) []request { - var requests []request - for _, e := range filenames { - log.WithFields("basename", e).Trace("searching for paths with a matching basename") - - matches, err := resolver.FilesByBasename(e) - if err != nil { - log.Warnf("unable to process file basename=%q: %+v", e, err) - continue - } - requests = append(requests, makeRequests(parser, matches)...) - } - return requests - }, - ) - return c -} - func makeRequests(parser Parser, locations []source.Location) []request { var requests []request for _, l := range locations { diff --git a/syft/pkg/cataloger/generic/search.go b/syft/pkg/cataloger/generic/search.go deleted file mode 100644 index 8cdc0332ba4..00000000000 --- a/syft/pkg/cataloger/generic/search.go +++ /dev/null @@ -1,182 +0,0 @@ -package generic - -import ( - "fmt" - "strings" - - "github.com/bmatcuk/doublestar/v4" - - "github.com/anchore/syft/syft/source" -) - -type Search interface { - ByGlob(string) SearchRequest - ByPath(string) SearchRequest - ByBasename(string) SearchRequirement - ByBasenameGlob(string) SearchRequirement - ByExtension(string) SearchRequirement - ByMimeType(...string) SearchRequirement -} - -type SearchRequirement interface { - MustMatchGlob(string) SearchRequest - Request() SearchRequest -} - -type search struct { - SearchRequest -} - -type SearchRequest struct { - // search basis - glob string - path string - basename string - basenameGlob string - extension string - mimeTypes []string - // requirements - matchGlob string -} - -func NewSearch() Search { - return &search{} -} - -func (s *search) ByGlob(glob string) SearchRequest { - s.glob = glob - return s.SearchRequest -} - -func (s *search) ByPath(path string) SearchRequest { - s.path = path - return s.SearchRequest -} - -func (s *search) ByBasename(basename string) SearchRequirement { - s.basename = basename - return s -} - -func (s *search) ByBasenameGlob(basenameGlob string) SearchRequirement { - s.basenameGlob = basenameGlob - return s -} - -func (s *search) ByExtension(extension string) SearchRequirement { - s.extension = extension - return s -} - -func (s *search) ByMimeType(tys ...string) SearchRequirement { - s.mimeTypes = tys - return s -} - -func (s *search) MustMatchGlob(glob string) SearchRequest { - s.matchGlob = glob - return s.SearchRequest -} - -func (s *search) Request() SearchRequest { - return s.SearchRequest -} - -func (s SearchRequest) String() string { - var res string - switch { - case s.basename != "": - res += fmt.Sprintf("basename=%s", s.basename) - case s.basenameGlob != "": - res += fmt.Sprintf("basenameGlob=%s", s.basenameGlob) - case s.extension != "": - res += fmt.Sprintf("extension=%s", s.extension) - case len(s.mimeTypes) > 0: - res += fmt.Sprintf("mimeTypes=%s", s.mimeTypes) - case s.glob != "": - res += fmt.Sprintf("glob=%s", s.glob) - case s.path != "": - res += fmt.Sprintf("path=%s", s.path) - default: - res = "no search criteria" - return res - } - - if s.matchGlob != "" { - res += fmt.Sprintf(" and result must match glob=%s", s.matchGlob) - } - return res -} - -func (s SearchRequest) Execute(resolver source.FileResolver) ([]source.Location, error) { - var locations []source.Location - var err error - - switch { - case s.glob != "": - locations, err = resolver.FilesByGlob(s.glob) - if err != nil { - return nil, fmt.Errorf("unable to process search glob=%q: %w", s.glob, err) - } - case s.path != "": - locations, err = resolver.FilesByPath(s.path) - if err != nil { - return nil, fmt.Errorf("unable to process search path=%q: %w", s.path, err) - } - case s.basename != "": - locations, err = resolver.FilesByBasename(s.basename) - if err != nil { - return nil, fmt.Errorf("unable to process search basis basename=%q: %w", s.basename, err) - } - case s.basenameGlob != "": - locations, err = resolver.FilesByBasenameGlob(s.basenameGlob) - if err != nil { - return nil, fmt.Errorf("unable to process search basis basename=%q: %w", s.basename, err) - } - case s.extension != "": - if strings.HasPrefix(s.extension, "*") { - return nil, fmt.Errorf("extensions must not include a leading wildcard: %q", s.extension) - } - locations, err = resolver.FilesByExtension(s.extension) - if err != nil { - return nil, fmt.Errorf("unable to process search basis extension=%q: %w", s.extension, err) - } - case len(s.mimeTypes) > 0: - for _, t := range s.mimeTypes { - locations, err = resolver.FilesByMIMEType(t) - if err != nil { - return nil, fmt.Errorf("unable to process search basis mimetype=%q: %w", t, err) - } - } - } - - if s.matchGlob != "" { - // overwrite the locations with the filtered set - locations, err = s.matchRequirementGlob(locations) - if err != nil { - return nil, err - } - } - - return locations, nil -} - -func (s SearchRequest) matchRequirementGlob(locations []source.Location) ([]source.Location, error) { - var globMatches []source.Location - var err error -forMatches: - for _, m := range locations { - var matchesGlob bool - for _, path := range []string{m.RealPath, m.VirtualPath} { - matchesGlob, err = doublestar.Match(s.matchGlob, path) - if err != nil { - return nil, fmt.Errorf("unable to validate glob requirement=%q: %w", s.matchGlob, err) - } - if matchesGlob { - globMatches = append(globMatches, m) - continue forMatches - } - } - } - return globMatches, nil -} diff --git a/syft/pkg/cataloger/generic/search_test.go b/syft/pkg/cataloger/generic/search_test.go deleted file mode 100644 index 75fc822e684..00000000000 --- a/syft/pkg/cataloger/generic/search_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package generic - -import ( - "io" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/anchore/syft/syft/pkg/cataloger/internal/srctest" - "github.com/anchore/syft/syft/source" -) - -func TestSearchRequest_Execute(t *testing.T) { - - tests := []struct { - name string - request SearchRequest - responsePath string - wantPathQueries map[string][]string - wantLocations []source.Location - }{ - { - name: "search by glob", - request: NewSearch().ByGlob("**/*test?/*.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByGlob": {"**/*test?/*.txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by path", - request: NewSearch().ByPath("result/will/match/x-test2/file.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByPath": {"result/will/match/x-test2/file.txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by extension", - request: NewSearch().ByExtension(".txt").Request(), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByExtension": {".txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by extension, with matching requirement", - request: NewSearch().ByExtension(".txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByExtension": {".txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by extension, with unmatched requirement", - request: NewSearch().ByExtension(".txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "somewhere-else/file.txt", - wantPathQueries: map[string][]string{ - "FilesByExtension": {".txt"}, - }, - wantLocations: nil, - }, - { - name: "search by basename, with matching requirement", - request: NewSearch().ByBasename("file.txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByBasename": {"file.txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by basename, with unmatched requirement", - request: NewSearch().ByBasename("file.txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "somewhere-else/file.txt", - wantPathQueries: map[string][]string{ - "FilesByBasename": {"file.txt"}, - }, - wantLocations: nil, - }, - { - name: "search by basename glob, with matching requirement", - request: NewSearch().ByBasenameGlob("?i*.txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByBasenameGlob": {"?i*.txt"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by basename glob, with unmatched requirement", - request: NewSearch().ByBasenameGlob("?i*.txt").MustMatchGlob("**/*test?/*.txt"), - responsePath: "somewhere-else/file.txt", - wantPathQueries: map[string][]string{ - "FilesByBasenameGlob": {"?i*.txt"}, - }, - wantLocations: nil, - }, - { - name: "search by mimetype, with matching requirement", - request: NewSearch().ByMimeType("plain/text").MustMatchGlob("**/*test?/*.txt"), - responsePath: "result/will/match/x-test2/file.txt", - wantPathQueries: map[string][]string{ - "FilesByMIMEType": {"plain/text"}, - }, - wantLocations: []source.Location{ - source.NewLocation("result/will/match/x-test2/file.txt"), - }, - }, - { - name: "search by mimetype, with unmatched requirement", - request: NewSearch().ByMimeType("plain/text").MustMatchGlob("**/*test?/*.txt"), - responsePath: "somewhere-else/file.txt", - wantPathQueries: map[string][]string{ - "FilesByMIMEType": {"plain/text"}, - }, - wantLocations: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &nopResolver{} - if tt.responsePath != "" { - n.loc = []source.Location{source.NewLocation(tt.responsePath)} - } - resolver := srctest.NewObservingResolver(n) - - locations, err := tt.request.Execute(resolver) - require.NoError(t, err) - - assert.Equal(t, tt.wantLocations, locations) - - if d := cmp.Diff(tt.wantPathQueries, resolver.AllPathQueries()); d != "" { - t.Errorf("unexpected path queries (-want +got):\n%s", d) - } - }) - } -} - -func TestSearchRequest_Execute_ExceptionWithWildcard(t *testing.T) { - - tests := []struct { - name string - request SearchRequest - responsePath string - wantPathQueries map[string][]string - wantLocations []source.Location - }{ - { - name: "search by extension", - request: NewSearch().ByExtension("*.txt").Request(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resolver := srctest.NewObservingResolver(&nopResolver{}) - _, err := tt.request.Execute(resolver) - require.Error(t, err) - }) - } -} - -var _ source.FileResolver = (*nopResolver)(nil) - -type nopResolver struct { - loc []source.Location -} - -func (n nopResolver) FileContentsByLocation(_ source.Location) (io.ReadCloser, error) { - return nil, nil -} - -func (n nopResolver) HasPath(_ string) bool { - return false -} - -func (n nopResolver) FilesByPath(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) FilesByGlob(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) FilesByExtension(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) FilesByBasename(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) FilesByBasenameGlob(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) FilesByMIMEType(_ ...string) ([]source.Location, error) { - return n.loc, nil -} - -func (n nopResolver) RelativeFileByPath(_ source.Location, _ string) *source.Location { - return nil -} - -func (n nopResolver) AllLocations() <-chan source.Location { - return nil -} - -func (n nopResolver) FileMetadataByLocation(_ source.Location) (source.FileMetadata, error) { - return source.FileMetadata{}, nil -} diff --git a/syft/pkg/cataloger/golang/cataloger.go b/syft/pkg/cataloger/golang/cataloger.go index 255422670ef..f49b25e656a 100644 --- a/syft/pkg/cataloger/golang/cataloger.go +++ b/syft/pkg/cataloger/golang/cataloger.go @@ -11,7 +11,7 @@ import ( // NewGoModFileCataloger returns a new Go module cataloger object. func NewGoModFileCataloger() *generic.Cataloger { return generic.NewCataloger("go-mod-file-cataloger"). - WithParserByBasename(parseGoModFile, "go.mod") + WithParserByGlobs(parseGoModFile, "**/go.mod") } // NewGoModuleBinaryCataloger returns a new Golang cataloger object. diff --git a/syft/pkg/cataloger/haskell/cataloger.go b/syft/pkg/cataloger/haskell/cataloger.go index 4566def6ccf..a7638ee837c 100644 --- a/syft/pkg/cataloger/haskell/cataloger.go +++ b/syft/pkg/cataloger/haskell/cataloger.go @@ -11,7 +11,7 @@ import ( // NewHackageCataloger returns a new Haskell cataloger object. func NewHackageCataloger() *generic.Cataloger { return generic.NewCataloger("haskell-cataloger"). - WithParserByBasename(parseStackYaml, "stack.yaml"). - WithParserByBasename(parseStackLock, "stack.yaml.lock"). - WithParserByBasename(parseCabalFreeze, "cabal.project.freeze") + WithParserByGlobs(parseStackYaml, "**/stack.yaml"). + WithParserByGlobs(parseStackLock, "**/stack.yaml.lock"). + WithParserByGlobs(parseCabalFreeze, "**/cabal.project.freeze") } diff --git a/syft/pkg/cataloger/internal/srctest/observing_resolver.go b/syft/pkg/cataloger/internal/srctest/observing_resolver.go index 69c8cc5cbea..b045c5c3a5f 100644 --- a/syft/pkg/cataloger/internal/srctest/observing_resolver.go +++ b/syft/pkg/cataloger/internal/srctest/observing_resolver.go @@ -172,39 +172,6 @@ func (r *ObservingResolver) FilesByGlob(patterns ...string) ([]source.Location, return locs, err } -func (r *ObservingResolver) FilesByExtension(extensions ...string) ([]source.Location, error) { - name := "FilesByExtension" - r.addPathQuery(name, extensions...) - - locs, err := r.decorated.FilesByExtension(extensions...) - - r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, extensions...) - return locs, err -} - -func (r *ObservingResolver) FilesByBasename(filenames ...string) ([]source.Location, error) { - name := "FilesByBasename" - r.addPathQuery(name, filenames...) - - locs, err := r.decorated.FilesByBasename(filenames...) - - r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, filenames...) - return locs, err -} - -func (r *ObservingResolver) FilesByBasenameGlob(globs ...string) ([]source.Location, error) { - name := "FilesByBasenameGlob" - r.addPathQuery(name, globs...) - - locs, err := r.decorated.FilesByBasenameGlob(globs...) - - r.addPathResponse(locs...) - r.addEmptyPathResponse(name, locs, globs...) - return locs, err -} - func (r *ObservingResolver) FilesByMIMEType(types ...string) ([]source.Location, error) { name := "FilesByMIMEType" r.addPathQuery(name, types...) diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 50923e27572..3150351c8cc 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -18,15 +18,15 @@ import ( var _ generic.Parser = parseJavaArchive -var archiveFormatExtensions = []string{ - ".jar", - ".war", - ".ear", - ".par", - ".sar", - ".jpi", - ".hpi", - ".lpkg", // Zip-compressed package used to deploy applications +var archiveFormatGlobs = []string{ + "**/*.jar", + "**/*.war", + "**/*.ear", + "**/*.par", + "**/*.sar", + "**/*.jpi", + "**/*.hpi", + "**/*.lpkg", // Zip-compressed package used to deploy applications // (aka plugins) to Liferay Portal server. Those files contains .JAR(s) and a .PROPERTIES file, the latter // has information about the application and installation requirements. // NOTE(jonasagx): If you would like to test it with lpkg file, @@ -36,15 +36,6 @@ var archiveFormatExtensions = []string{ // project that we can build in CI feel free to include it } -// note: this mirrors 'archiveFormatExtensions' as a glob pattern for each element -var archiveFormatGlobs []string - -func init() { - for _, ext := range archiveFormatExtensions { - archiveFormatGlobs = append(archiveFormatGlobs, fmt.Sprintf("**/*%s", ext)) - } -} - // javaArchiveHashes are all the current hash algorithms used to calculate archive digests var javaArchiveHashes = []crypto.Hash{ crypto.SHA1, diff --git a/syft/pkg/cataloger/java/cataloger.go b/syft/pkg/cataloger/java/cataloger.go index b22c68ce44c..18e1aeefc6c 100644 --- a/syft/pkg/cataloger/java/cataloger.go +++ b/syft/pkg/cataloger/java/cataloger.go @@ -10,16 +10,16 @@ import ( // NewJavaCataloger returns a new Java archive cataloger object. func NewJavaCataloger(cfg Config) *generic.Cataloger { c := generic.NewCataloger("java-cataloger"). - WithParserByExtensions(parseJavaArchive, archiveFormatExtensions...) + WithParserByGlobs(parseJavaArchive, archiveFormatGlobs...) if cfg.SearchIndexedArchives { // java archives wrapped within zip files - c.WithParserByExtensions(parseZipWrappedJavaArchive, genericZipExtensions...) + c.WithParserByGlobs(parseZipWrappedJavaArchive, genericZipGlobs...) } if cfg.SearchUnindexedArchives { // java archives wrapped within tar files - c.WithParserByExtensions(parseTarWrappedJavaArchive, genericTarExtensions...) + c.WithParserByGlobs(parseTarWrappedJavaArchive, genericTarGlobs...) } return c } @@ -29,5 +29,5 @@ func NewJavaCataloger(cfg Config) *generic.Cataloger { // Pom files list dependencies that maybe not be locally installed yet. func NewJavaPomCataloger() *generic.Cataloger { return generic.NewCataloger("java-pom-cataloger"). - WithParserByBasename(parserPomXML, "pom.xml") + WithParserByGlobs(parserPomXML, "pom.xml") } diff --git a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go index 18b189dd5d0..99c723f44b5 100644 --- a/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go +++ b/syft/pkg/cataloger/java/tar_wrapped_archive_parser.go @@ -10,31 +10,33 @@ import ( "github.com/anchore/syft/syft/source" ) -var genericTarExtensions = []string{ - ".tar", +var genericTarGlobs = []string{ + "**/*.tar", // gzipped tar - ".tar.gz", - ".tgz", + "**/*.tar.gz", + "**/*.tgz", // bzip2 - ".tar.bz", - ".tar.bz2", - ".tbz", - ".tbz2", + "**/*.tar.bz", + "**/*.tar.bz2", + "**/*.tbz", + "**/*.tbz2", // brotli - ".tar.br", - ".tbr", + "**/*.tar.br", + "**/*.tbr", // lz4 - ".tar.lz4", - ".tlz4", + "**/*.tar.lz4", + "**/*.tlz4", // sz - ".tar.sz", - ".tsz", + "**/*.tar.sz", + "**/*.tsz", // xz - ".tar.xz", - ".txz", - // zstandard - ".tar.zst", - ".tzst", + "**/*.tar.xz", + "**/*.txz", + // zst + "**/*.tar.zst", + "**/*.tzst", + "**/*.tar.zstd", + "**/*.tzstd", } // TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246) diff --git a/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go b/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go index e2233735d78..dffe5df74a6 100644 --- a/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go +++ b/syft/pkg/cataloger/java/zip_wrapped_archive_parser.go @@ -10,8 +10,8 @@ import ( "github.com/anchore/syft/syft/source" ) -var genericZipExtensions = []string{ - ".zip", +var genericZipGlobs = []string{ + "**/*.zip", } // TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246) diff --git a/syft/pkg/cataloger/javascript/cataloger.go b/syft/pkg/cataloger/javascript/cataloger.go index cea0a7c7ffb..2109eb198b9 100644 --- a/syft/pkg/cataloger/javascript/cataloger.go +++ b/syft/pkg/cataloger/javascript/cataloger.go @@ -10,13 +10,13 @@ import ( // NewPackageCataloger returns a new JavaScript cataloger object based on detection of npm based packages. func NewPackageCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-package-cataloger"). - WithParserByBasename(parsePackageJSON, "package.json") + WithParserByGlobs(parsePackageJSON, "**/package.json") } // NewLockCataloger returns a new JavaScript cataloger object based on detection of lock files. func NewLockCataloger() *generic.Cataloger { return generic.NewCataloger("javascript-lock-cataloger"). - WithParserByBasename(parsePackageLock, "package-lock.json"). - WithParserByBasename(parseYarnLock, "yarn.lock"). - WithParserByBasename(parsePnpmLock, "pnpm-lock.yaml") + WithParserByGlobs(parsePackageLock, "**/package-lock.json"). + WithParserByGlobs(parseYarnLock, "**/yarn.lock"). + WithParserByGlobs(parsePnpmLock, "**/pnpm-lock.yaml") } diff --git a/syft/pkg/cataloger/php/cataloger.go b/syft/pkg/cataloger/php/cataloger.go index ff37719be07..5beba45acaf 100644 --- a/syft/pkg/cataloger/php/cataloger.go +++ b/syft/pkg/cataloger/php/cataloger.go @@ -10,11 +10,11 @@ import ( // NewComposerInstalledCataloger returns a new cataloger for PHP installed.json files. func NewComposerInstalledCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-installed-cataloger"). - WithParserByBasename(parseInstalledJSON, "installed.json") + WithParserByGlobs(parseInstalledJSON, "**/installed.json") } // NewComposerLockCataloger returns a new cataloger for PHP composer.lock files. func NewComposerLockCataloger() *generic.Cataloger { return generic.NewCataloger("php-composer-lock-cataloger"). - WithParserByBasename(parseComposerLock, "composer.lock") + WithParserByGlobs(parseComposerLock, "**/composer.lock") } diff --git a/syft/pkg/cataloger/portage/cataloger.go b/syft/pkg/cataloger/portage/cataloger.go index 9ee8328d724..b6be1a3ca23 100644 --- a/syft/pkg/cataloger/portage/cataloger.go +++ b/syft/pkg/cataloger/portage/cataloger.go @@ -9,5 +9,5 @@ import ( func NewPortageCataloger() *generic.Cataloger { return generic.NewCataloger("portage-cataloger"). - WithParser(parsePortageContents, generic.NewSearch().ByBasename("CONTENTS").MustMatchGlob("**/var/db/pkg/*/*/CONTENTS")) + WithParserByGlobs(parsePortageContents, "**/var/db/pkg/*/*/CONTENTS") } diff --git a/syft/pkg/cataloger/python/cataloger.go b/syft/pkg/cataloger/python/cataloger.go index 9c051262603..00536625780 100644 --- a/syft/pkg/cataloger/python/cataloger.go +++ b/syft/pkg/cataloger/python/cataloger.go @@ -9,20 +9,14 @@ const eggInfoExtension = ".egg-info" // NewPythonIndexCataloger returns a new cataloger for python packages referenced from poetry lock files, requirements.txt files, and setup.py files. func NewPythonIndexCataloger() *generic.Cataloger { return generic.NewCataloger("python-index-cataloger"). - WithParser(parseRequirementsTxt, generic.NewSearch().ByBasenameGlob("*requirements*.txt").Request()). - WithParserByBasename(parsePoetryLock, "poetry.lock"). - WithParserByBasename(parsePipfileLock, "Pipfile.lock"). - WithParserByBasename(parseSetup, "setup.py") + WithParserByGlobs(parseRequirementsTxt, "**/*requirements*.txt"). + WithParserByGlobs(parsePoetryLock, "**/poetry.lock"). + WithParserByGlobs(parsePipfileLock, "**/Pipfile.lock"). + WithParserByGlobs(parseSetup, "**/setup.py") } // NewPythonPackageCataloger returns a new cataloger for python packages within egg or wheel installation directories. func NewPythonPackageCataloger() *generic.Cataloger { return generic.NewCataloger("python-package-cataloger"). - WithParser(parseWheelOrEgg, - generic.NewSearch().ByBasename("METADATA").MustMatchGlob("**/*dist-info/METADATA"), - generic.NewSearch().ByBasename("PKG-INFO").MustMatchGlob("**/*egg-info/PKG-INFO"), - ). - WithParserByExtensions(parseWheelOrEgg, - eggInfoExtension, - ) + WithParserByGlobs(parseWheelOrEgg, eggInfoExtension, "**/*dist-info/METADATA", "**/*egg-info/PKG-INFO") } diff --git a/syft/pkg/cataloger/rpm/cataloger.go b/syft/pkg/cataloger/rpm/cataloger.go index 427d8ba7c70..eecebdc9771 100644 --- a/syft/pkg/cataloger/rpm/cataloger.go +++ b/syft/pkg/cataloger/rpm/cataloger.go @@ -11,18 +11,12 @@ import ( // NewRpmDBCataloger returns a new RPM DB cataloger object. func NewRpmDBCataloger() *generic.Cataloger { return generic.NewCataloger("rpm-db-cataloger"). - WithParser(parseRpmDB, - generic.NewSearch().ByBasename("Packages").MustMatchGlob(pkg.RpmDBGlob), - generic.NewSearch().ByBasename("Packages.db").MustMatchGlob(pkg.RpmDBGlob), - generic.NewSearch().ByBasename("rpmdb.sqlite").MustMatchGlob(pkg.RpmDBGlob), - ). - WithParser(parseRpmManifest, - generic.NewSearch().ByBasename("container-manifest-2").MustMatchGlob(pkg.RpmManifestGlob), - ) + WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob). + WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob) } // NewFileCataloger returns a new RPM file cataloger object. func NewFileCataloger() *generic.Cataloger { return generic.NewCataloger("rpm-file-cataloger"). - WithParserByExtensions(parseRpm, ".rpm") + WithParserByGlobs(parseRpm, "**/*.rpm") } diff --git a/syft/pkg/cataloger/ruby/catalogers.go b/syft/pkg/cataloger/ruby/catalogers.go index 7007e309b4e..e3e173a21d9 100644 --- a/syft/pkg/cataloger/ruby/catalogers.go +++ b/syft/pkg/cataloger/ruby/catalogers.go @@ -10,13 +10,11 @@ import ( // NewGemFileLockCataloger returns a new Bundler cataloger object tailored for parsing index-oriented files (e.g. Gemfile.lock). func NewGemFileLockCataloger() *generic.Cataloger { return generic.NewCataloger("ruby-gemfile-cataloger"). - WithParserByBasename(parseGemFileLockEntries, "Gemfile.lock") + WithParserByGlobs(parseGemFileLockEntries, "**/Gemfile.lock") } // NewGemSpecCataloger returns a new Bundler cataloger object tailored for detecting installations of gems (e.g. Gemspec). func NewGemSpecCataloger() *generic.Cataloger { return generic.NewCataloger("ruby-gemspec-cataloger"). - WithParser(parseGemSpecEntries, - generic.NewSearch().ByExtension(".gemspec").MustMatchGlob("**/specifications/**/*.gemspec"), - ) + WithParserByGlobs(parseGemSpecEntries, "**/specifications/**/*.gemspec") } diff --git a/syft/pkg/cataloger/rust/cataloger.go b/syft/pkg/cataloger/rust/cataloger.go index 4326ddfcd0b..a5128b24da4 100644 --- a/syft/pkg/cataloger/rust/cataloger.go +++ b/syft/pkg/cataloger/rust/cataloger.go @@ -11,7 +11,7 @@ import ( // NewCargoLockCataloger returns a new Rust Cargo lock file cataloger object. func NewCargoLockCataloger() *generic.Cataloger { return generic.NewCataloger("rust-cargo-lock-cataloger"). - WithParserByBasename(parseCargoLock, "Cargo.lock") + WithParserByGlobs(parseCargoLock, "**/Cargo.lock") } // NewAuditBinaryCataloger returns a new Rust auditable binary cataloger object that can detect dependencies diff --git a/syft/pkg/cataloger/sbom/cataloger.go b/syft/pkg/cataloger/sbom/cataloger.go index b80c197252c..0c82f452ebd 100644 --- a/syft/pkg/cataloger/sbom/cataloger.go +++ b/syft/pkg/cataloger/sbom/cataloger.go @@ -13,24 +13,19 @@ const catalogerName = "sbom-cataloger" // NewSBOMCataloger returns a new SBOM cataloger object loaded from saved SBOM JSON. func NewSBOMCataloger() *generic.Cataloger { - generic.NewSearch().ByExtension(".txt").MustMatchGlob("**/somewhere/here/*.txt") return generic.NewCataloger(catalogerName). - WithParserByBasename(parseSBOM, - "bom", - "sbom", - ). - WithParserByExtensions(parseSBOM, - ".syft.json", - ".bom", - ".sbom", - ".cdx", - ".spdx", - ). - WithParser(parseSBOM, - generic.NewSearch().ByBasenameGlob("*.bom.*").Request(), - generic.NewSearch().ByBasenameGlob("*.sbom.*").Request(), - generic.NewSearch().ByBasenameGlob("*.cdx.*").Request(), - generic.NewSearch().ByBasenameGlob("*.spdx.*").Request(), + WithParserByGlobs(parseSBOM, + "**/*.syft.json", + "**/*.bom.*", + "**/*.bom", + "**/bom", + "**/*.sbom.*", + "**/*.sbom", + "**/sbom", + "**/*.cdx.*", + "**/*.cdx", + "**/*.spdx.*", + "**/*.spdx", ) } diff --git a/syft/pkg/cataloger/swift/cataloger.go b/syft/pkg/cataloger/swift/cataloger.go index 50dcc7a288e..5ce504b749a 100644 --- a/syft/pkg/cataloger/swift/cataloger.go +++ b/syft/pkg/cataloger/swift/cataloger.go @@ -10,5 +10,5 @@ import ( // NewCocoapodsCataloger returns a new Swift Cocoapods lock file cataloger object. func NewCocoapodsCataloger() *generic.Cataloger { return generic.NewCataloger("cocoapods-cataloger"). - WithParserByBasename(parsePodfileLock, "Podfile.lock") + WithParserByGlobs(parsePodfileLock, "**/Podfile.lock") } diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index f6af81ab26a..0df2b80e668 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -41,6 +41,7 @@ type directoryResolver struct { currentWdRelativeToRoot string currentWd string fileTree *filetree.FileTree + fileTreeIndex filetree.Index metadata map[file.ID]FileMetadata // TODO: wire up to report these paths in the json report pathFilterFns []pathFilterFn @@ -93,6 +94,7 @@ func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) currentWd: cleanCWD, currentWdRelativeToRoot: currentWdRelRoot, fileTree: filetree.NewFileTree(), + fileTreeIndex: filetree.NewIndex(), metadata: make(map[file.ID]FileMetadata), pathFilterFns: append([]pathFilterFn{isUnallowableFileType, isUnixSystemRuntimePath}, pathFilters...), refsByMIMEType: make(map[string][]file.Reference), @@ -103,7 +105,7 @@ func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) } func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]string, error) { - log.Debugf("indexing filesystem path=%q", root) + log.WithFields("path", root).Trace("indexing filetree") var roots []string var err error @@ -195,12 +197,12 @@ func (r *directoryResolver) isFileAccessErr(path string, err error) bool { } func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, error) { - switch t := newFileTypeFromMode(info.Mode()); t { - case SymbolicLink: + switch t := file.TypeFromMode(info.Mode()); t { + case file.TypeSymlink: return r.addSymlinkToIndex(p, info) - case Directory: + case file.TypeDir: return "", r.addDirectoryToIndex(p, info) - case RegularFile: + case file.TypeReg: return "", r.addFileToIndex(p, info) default: return "", fmt.Errorf("unsupported file type: %s", t) @@ -220,8 +222,10 @@ func (r directoryResolver) hasBeenIndexed(p string) bool { // cases like "/" will be in the tree, but not been indexed yet (a special case). We want to capture // these cases as new paths to index. - _, exists = r.metadata[ref.ID()] - return exists + if !ref.HasReference() { + return false + } + return r.fileTreeIndex.Exists(*ref.Reference) } func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error { @@ -232,7 +236,7 @@ func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error location := NewLocationFromDirectory(p, *ref) metadata := fileMetadataFromPath(p, info, r.isInIndex(location)) - r.addFileMetadataToIndex(ref, metadata) + r.addFileToFileTreeIndex(ref, metadata) return nil } @@ -245,7 +249,8 @@ func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error { location := NewLocationFromDirectory(p, *ref) metadata := fileMetadataFromPath(p, info, r.isInIndex(location)) - r.addFileMetadataToIndex(ref, metadata) + r.addFileToFileTreeIndex(ref, metadata) + r.fileTreeIndex.Add(*ref, metadata) return nil } @@ -301,12 +306,12 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string location.VirtualPath = p metadata := fileMetadataFromPath(p, usedInfo, false) // note: to be consistent with other resolvers, don't record mime type for symlink destinations metadata.LinkDestination = linkTarget - r.addFileMetadataToIndex(ref, metadata) + r.addFileToFileTreeIndex(ref, metadata) return targetAbsPath, nil } -func (r directoryResolver) addFileMetadataToIndex(ref *file.Reference, metadata FileMetadata) { +func (r directoryResolver) addFileToFileTreeIndex(ref *file.Reference, metadata FileMetadata) { if ref != nil { if metadata.MIMEType != "" { r.refsByMIMEType[metadata.MIMEType] = append(r.refsByMIMEType[metadata.MIMEType], *ref) @@ -373,16 +378,19 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) } // we should be resolving symlinks and preserving this information as a VirtualPath to the real file - exists, ref, err := r.fileTree.File(file.Path(userStrPath), filetree.FollowBasenameLinks) + searchContext := filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) + ref, err := searchContext.SearchByPath(userStrPath, filetree.FollowBasenameLinks) if err != nil { log.Tracef("unable to evaluate symlink for path=%q : %+v", userPath, err) continue } - if !exists { + // TODO: alex: is this the same as "exists"? + if !ref.HasReference() { continue } // TODO: why not use stored metadata? + // TODO: today we don't store directory metadata while indexing, which would make the index much larger too fileMeta, err := os.Stat(string(ref.RealPath)) if errors.Is(err, os.ErrNotExist) { // note: there are other kinds of errors other than os.ErrNotExist that may be given that is platform @@ -427,83 +435,20 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) { result := make([]Location, 0) for _, pattern := range patterns { - globResults, err := r.fileTree.FilesByGlob(pattern, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) - } - } - - return result, nil -} - -func (r directoryResolver) FilesByExtension(extensions ...string) ([]Location, error) { - result := make([]Location, 0) - - for _, extension := range extensions { - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/*"+extension, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) - } - } - - return result, nil -} - -func (r directoryResolver) FilesByBasename(filenames ...string) ([]Location, error) { - result := make([]Location, 0) - - for _, filename := range filenames { - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/"+filename, filetree.FollowBasenameLinks) - if err != nil { - return nil, err - } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) - } - } - - return result, nil -} - -func (r directoryResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { - result := make([]Location, 0) - - for _, glob := range globs { - // TODO: is there a faster way to do this? - globResults, err := r.fileTree.FilesByGlob("**/"+glob, filetree.FollowBasenameLinks) + searchContext := filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) + refVias, err := searchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { return nil, err } - for _, globResult := range globResults { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(globResult.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(globResult.MatchPath)), // the path used to access this file, relative to the resolver root - globResult.Reference, - ) - result = append(result, loc) + for _, refVia := range refVias { + if refVia.HasReference() { + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(refVia.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(refVia.RequestPath)), // the path used to access this file, relative to the resolver root + *refVia.Reference, + ) + result = append(result, loc) + } } } @@ -622,8 +567,8 @@ func isUnallowableFileType(_ string, info os.FileInfo) bool { // we can't filter out by filetype for non-existent files return false } - switch newFileTypeFromMode(info.Mode()) { - case CharacterDevice, Socket, BlockDevice, FIFONode, IrregularFile: + switch file.TypeFromMode(info.Mode()) { + case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFifo, file.TypeIrregular: return true // note: symlinks that point to these files may still get by. // We handle this later in processing to help prevent against infinite links traversal. diff --git a/syft/source/directory_resolver_test.go b/syft/source/directory_resolver_test.go index 95ce43243b6..af11ed30365 100644 --- a/syft/source/directory_resolver_test.go +++ b/syft/source/directory_resolver_test.go @@ -1041,7 +1041,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { name: "by basename", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasename("file-2.txt") + actualLocations, err := resolver.FilesByGlob("**/file-2.txt") assert.NoError(t, err) return actualLocations }, @@ -1059,7 +1059,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { name: "by basename glob", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + actualLocations, err := resolver.FilesByGlob("**/file-?.txt") assert.NoError(t, err) return actualLocations }, @@ -1104,7 +1104,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { { name: "by basename glob to links", runner: func(resolver FileResolver) []Location { - actualLocations, err := resolver.FilesByBasenameGlob("link-*") + actualLocations, err := resolver.FilesByGlob("**/link-*") assert.NoError(t, err) return actualLocations }, @@ -1143,7 +1143,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { name: "by extension", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByExtension(".txt") + actualLocations, err := resolver.FilesByGlob("**/*.txt") assert.NoError(t, err) return actualLocations }, diff --git a/syft/source/excluding_file_resolver.go b/syft/source/excluding_file_resolver.go index 3207b5a9aec..50969116a81 100644 --- a/syft/source/excluding_file_resolver.go +++ b/syft/source/excluding_file_resolver.go @@ -59,21 +59,6 @@ func (r *excludingResolver) FilesByMIMEType(types ...string) ([]Location, error) return filterLocations(locations, err, r.excludeFn) } -func (r *excludingResolver) FilesByExtension(extensions ...string) ([]Location, error) { - locations, err := r.delegate.FilesByExtension(extensions...) - return filterLocations(locations, err, r.excludeFn) -} - -func (r *excludingResolver) FilesByBasename(filenames ...string) ([]Location, error) { - locations, err := r.delegate.FilesByBasename(filenames...) - return filterLocations(locations, err, r.excludeFn) -} - -func (r *excludingResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { - locations, err := r.delegate.FilesByBasenameGlob(globs...) - return filterLocations(locations, err, r.excludeFn) -} - func (r *excludingResolver) RelativeFileByPath(location Location, path string) *Location { l := r.delegate.RelativeFileByPath(location, path) if l != nil && locationMatches(l, r.excludeFn) { diff --git a/syft/source/excluding_file_resolver_test.go b/syft/source/excluding_file_resolver_test.go index 9b8b3325134..968d2b6a818 100644 --- a/syft/source/excluding_file_resolver_test.go +++ b/syft/source/excluding_file_resolver_test.go @@ -67,15 +67,6 @@ func TestExcludingResolver(t *testing.T) { locations, _ = er.FilesByMIMEType() assert.ElementsMatch(t, locationPaths(locations), test.expected) - locations, _ = er.FilesByExtension() - assert.ElementsMatch(t, locationPaths(locations), test.expected) - - locations, _ = er.FilesByBasename() - assert.ElementsMatch(t, locationPaths(locations), test.expected) - - locations, _ = er.FilesByBasenameGlob() - assert.ElementsMatch(t, locationPaths(locations), test.expected) - locations = []Location{} channel := er.AllLocations() diff --git a/syft/source/file_metadata.go b/syft/source/file_metadata.go index 8082c751269..8d541e51e7e 100644 --- a/syft/source/file_metadata.go +++ b/syft/source/file_metadata.go @@ -8,34 +8,18 @@ import ( "github.com/anchore/syft/internal/log" ) -type FileMetadata struct { - Mode os.FileMode - Type FileType - UserID int - GroupID int - LinkDestination string - Size int64 - MIMEType string -} +type FileMetadata = file.Metadata -func fileMetadataByLocation(img *image.Image, location Location) (FileMetadata, error) { +func fileMetadataByLocation(img *image.Image, location Location) (file.Metadata, error) { entry, err := img.FileCatalog.Get(location.ref) if err != nil { return FileMetadata{}, err } - return FileMetadata{ - Mode: entry.Metadata.Mode, - Type: newFileTypeFromTarHeaderTypeFlag(entry.Metadata.TypeFlag), - UserID: entry.Metadata.UserID, - GroupID: entry.Metadata.GroupID, - LinkDestination: entry.Metadata.Linkname, - Size: entry.Metadata.Size, - MIMEType: entry.Metadata.MIMEType, - }, nil + return entry.Metadata, nil } -func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) FileMetadata { +func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) file.Metadata { var mimeType string uid, gid := GetXid(info) @@ -57,7 +41,7 @@ func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) File return FileMetadata{ Mode: info.Mode(), - Type: newFileTypeFromMode(info.Mode()), + Type: file.TypeFromMode(info.Mode()), // unsupported across platforms UserID: uid, GroupID: gid, diff --git a/syft/source/file_resolver.go b/syft/source/file_resolver.go index a16daf30bcd..2f3bb188f8f 100644 --- a/syft/source/file_resolver.go +++ b/syft/source/file_resolver.go @@ -29,12 +29,6 @@ type FilePathResolver interface { FilesByPath(paths ...string) ([]Location, error) // FilesByGlob fetches a set of file references for the given glob matches FilesByGlob(patterns ...string) ([]Location, error) - // FilesByExtension fetches a set of file references for the given file extensions - FilesByExtension(extensions ...string) ([]Location, error) - // FilesByBasename fetches a set of file references for the given filenames - FilesByBasename(basenames ...string) ([]Location, error) - // FilesByBasenameGlob fetches a set of file references for the given filename glob patterns (e.g. *requirements*.txt) - FilesByBasenameGlob(patterns ...string) ([]Location, error) // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types FilesByMIMEType(types ...string) ([]Location, error) // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. diff --git a/syft/source/file_type.go b/syft/source/file_type.go deleted file mode 100644 index 370ea0f8df0..00000000000 --- a/syft/source/file_type.go +++ /dev/null @@ -1,70 +0,0 @@ -package source - -import ( - "archive/tar" - "os" -) - -const ( - RegularFile FileType = "RegularFile" - // IrregularFile is how syft defines files that are neither regular, symbolic or directory. - // For ref: the seven standard Unix file types are regular, directory, symbolic link, - // FIFO special, block special, character special, and socket as defined by POSIX. - IrregularFile FileType = "IrregularFile" - HardLink FileType = "HardLink" - SymbolicLink FileType = "SymbolicLink" - CharacterDevice FileType = "CharacterDevice" - BlockDevice FileType = "BlockDevice" - Directory FileType = "Directory" - FIFONode FileType = "FIFONode" - Socket FileType = "Socket" -) - -type FileType string - -func newFileTypeFromTarHeaderTypeFlag(flag byte) FileType { - switch flag { - case tar.TypeReg, tar.TypeRegA: - return RegularFile - case tar.TypeLink: - return HardLink - case tar.TypeSymlink: - return SymbolicLink - case tar.TypeChar: - return CharacterDevice - case tar.TypeBlock: - return BlockDevice - case tar.TypeDir: - return Directory - case tar.TypeFifo: - return FIFONode - } - return IrregularFile -} - -func newFileTypeFromMode(mode os.FileMode) FileType { - switch { - case isSet(mode, os.ModeSymlink): - return SymbolicLink - case isSet(mode, os.ModeIrregular): - return IrregularFile - case isSet(mode, os.ModeCharDevice): - return CharacterDevice - case isSet(mode, os.ModeDevice): - return BlockDevice - case isSet(mode, os.ModeNamedPipe): - return FIFONode - case isSet(mode, os.ModeSocket): - return Socket - case mode.IsDir(): - return Directory - case mode.IsRegular(): - return RegularFile - default: - return IrregularFile - } -} - -func isSet(mode, field os.FileMode) bool { - return mode&field != 0 -} diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index 2ec23045c9b..fa04b70c424 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -56,7 +56,7 @@ func (r *imageAllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs fil return nil, fmt.Errorf("unable to fetch metadata (ref=%+v): %w", ref, err) } - if entry.Metadata.TypeFlag == tar.TypeLink || entry.Metadata.TypeFlag == tar.TypeSymlink { + if entry.Metadata.Type == file.TypeHardLink || entry.Metadata.Type == tar.TypeSymlink { // a link may resolve in this layer or higher, assuming a squashed tree is used to search // we should search all possible resolutions within the valid source for _, subLayerIdx := range r.layers[layerIdx:] { @@ -84,8 +84,7 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error for _, path := range paths { for idx, layerIdx := range r.layers { - tree := r.img.Layers[layerIdx].Tree - _, ref, err := tree.File(file.Path(path), filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) + ref, err := r.img.Layers[layerIdx].SearchContext().SearchByPath(path, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) if err != nil { return nil, err } @@ -126,31 +125,34 @@ func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, er for _, pattern := range patterns { for idx, layerIdx := range r.layers { - results, err := r.img.Layers[layerIdx].SquashedTree.FilesByGlob(pattern, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) + results, err := r.img.Layers[layerIdx].SquashedSearchContext().SearchByGlob(pattern, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) if err != nil { return nil, fmt.Errorf("failed to resolve files by glob (%s): %w", pattern, err) } for _, result := range results { + if !result.HasReference() { + continue + } // don't consider directories (special case: there is no path information for /) if result.RealPath == "/" { continue - } else if r.img.FileCatalog.Exists(result.Reference) { - metadata, err := r.img.FileCatalog.Get(result.Reference) + } else if r.img.FileCatalog.Exists(*result.Reference) { + metadata, err := r.img.FileCatalog.Get(*result.Reference) if err != nil { - return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.MatchPath, err) + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err) } if metadata.Metadata.IsDir { continue } } - - refResults, err := r.fileByRef(result.Reference, uniqueFileIDs, idx) + // TODO: alex: can't we just use the result.Reference here instead? + refResults, err := r.fileByRef(*result.Reference, uniqueFileIDs, idx) if err != nil { return nil, err } for _, refResult := range refResults { - uniqueLocations = append(uniqueLocations, NewLocationFromImage(string(result.MatchPath), refResult, r.img)) + uniqueLocations = append(uniqueLocations, NewLocationFromImage(string(result.RequestPath), refResult, r.img)) } } } @@ -162,12 +164,9 @@ func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, er // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. // This is helpful when attempting to find a file that is in the same layer or lower as another file. func (r *imageAllLayersResolver) RelativeFileByPath(location Location, path string) *Location { - entry, err := r.img.FileCatalog.Get(location.ref) - if err != nil { - return nil - } + layer := r.img.FileCatalog.Layer(location.ref) - exists, relativeRef, err := entry.Layer.SquashedTree.File(file.Path(path), filetree.FollowBasenameLinks) + exists, relativeRef, err := layer.SquashedTree.File(file.Path(path), filetree.FollowBasenameLinks) if err != nil { log.Errorf("failed to find path=%q in squash: %+w", path, err) return nil @@ -189,8 +188,8 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err) } - switch entry.Metadata.TypeFlag { - case tar.TypeSymlink, tar.TypeLink: + switch entry.Metadata.Type { + case file.TypeSymlink, file.TypeHardLink: // the location we are searching may be a symlink, we should always work with the resolved file newLocation := r.RelativeFileByPath(location, location.VirtualPath) if newLocation == nil { @@ -206,9 +205,7 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) { var locations []Location for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] - - refs, err := layer.FilesByMIMEType(types...) + refs, err := r.img.Layers[layerIdx].SearchContext().SearchByMIMEType(types...) if err != nil { return nil, err } @@ -223,79 +220,13 @@ func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, e return locations, nil } -func (r *imageAllLayersResolver) FilesByExtension(extensions ...string) ([]Location, error) { - var locations []Location - for _, extension := range extensions { - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] - - refs, err := layer.FilesByExtension(extension) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) - } - } - } - } - - return locations, nil -} - -func (r *imageAllLayersResolver) FilesByBasename(filenames ...string) ([]Location, error) { - var locations []Location - for _, filename := range filenames { - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] - - refs, err := layer.FilesByBasename(filename) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) - } - } - } - } - - return locations, nil -} - -func (r *imageAllLayersResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { - var locations []Location - for _, glob := range globs { - for _, layerIdx := range r.layers { - layer := r.img.Layers[layerIdx] - - refs, err := layer.FilesByBasenameGlob(glob) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) - } - } - } - } - - return locations, nil -} - func (r *imageAllLayersResolver) AllLocations() <-chan Location { results := make(chan Location) go func() { defer close(results) for _, layerIdx := range r.layers { tree := r.img.Layers[layerIdx].Tree - for _, ref := range tree.AllFiles(file.AllTypes...) { + for _, ref := range tree.AllFiles(file.AllTypes()...) { results <- NewLocationFromImage(string(ref.RealPath), ref, r.img) } } diff --git a/syft/source/image_all_layers_resolver_test.go b/syft/source/image_all_layers_resolver_test.go index 50b690c4fe3..d7ef8ebcf83 100644 --- a/syft/source/image_all_layers_resolver_test.go +++ b/syft/source/image_all_layers_resolver_test.go @@ -124,13 +124,9 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) { t.Errorf("we should always prefer real paths over ones with links") } - entry, err := img.FileCatalog.Get(actual.ref) - if err != nil { - t.Fatalf("failed to get metadata: %+v", err) - } - - if entry.Layer.Metadata.Index != expected.layer { - t.Errorf("bad resolve layer: '%d'!='%d'", entry.Layer.Metadata.Index, expected.layer) + layer := img.FileCatalog.Layer(actual.ref) + if layer.Metadata.Index != expected.layer { + t.Errorf("bad resolve layer: '%d'!='%d'", layer.Metadata.Index, expected.layer) } } }) @@ -231,13 +227,10 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) { t.Errorf("we should always prefer real paths over ones with links") } - entry, err := img.FileCatalog.Get(actual.ref) - if err != nil { - t.Fatalf("failed to get metadata: %+v", err) - } + layer := img.FileCatalog.Layer(actual.ref) - if entry.Layer.Metadata.Index != expected.layer { - t.Errorf("bad resolve layer: '%d'!='%d'", entry.Layer.Metadata.Index, expected.layer) + if layer.Metadata.Index != expected.layer { + t.Errorf("bad resolve layer: '%d'!='%d'", layer.Metadata.Index, expected.layer) } } }) @@ -477,7 +470,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { name: "by basename", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasename("file-2.txt") + actualLocations, err := resolver.FilesByGlob("**/file-2.txt") assert.NoError(t, err) return actualLocations }, @@ -502,7 +495,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { name: "by basename glob", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + actualLocations, err := resolver.FilesByGlob("**/file-?.txt") assert.NoError(t, err) return actualLocations }, @@ -552,7 +545,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { name: "by extension", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByExtension(".txt") + actualLocations, err := resolver.FilesByGlob("**/*.txt") assert.NoError(t, err) return actualLocations }, diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index 863fe231c68..bd073bc22d0 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -1,7 +1,6 @@ package source import ( - "archive/tar" "fmt" "io" @@ -39,8 +38,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { uniqueLocations := make([]Location, 0) for _, path := range paths { - tree := r.img.SquashedTree() - _, ref, err := tree.File(file.Path(path), filetree.FollowBasenameLinks) + ref, err := r.img.SquashedSearchContext().SearchByPath(path, filetree.FollowBasenameLinks) if err != nil { return nil, err } @@ -81,28 +79,31 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) { var locations []Location for _, pattern := range patterns { - results, err := r.img.SquashedTree().FilesByGlob(pattern, filetree.FollowBasenameLinks) + results, err := r.img.SquashedSearchContext().SearchByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { return nil, fmt.Errorf("failed to resolve files by glob (%s): %w", pattern, err) } for _, result := range results { + if !result.HasReference() { + continue + } // don't consider directories (special case: there is no path information for /) - if result.MatchPath == "/" { + if result.RealPath == "/" { continue } - if r.img.FileCatalog.Exists(result.Reference) { - metadata, err := r.img.FileCatalog.Get(result.Reference) + if r.img.FileCatalog.Exists(*result.Reference) { + metadata, err := r.img.FileCatalog.Get(*result.Reference) if err != nil { - return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.MatchPath, err) + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err) } if metadata.Metadata.IsDir { continue } } - - resolvedLocations, err := r.FilesByPath(string(result.MatchPath)) + // TODO: alex: can't we just use the result.Reference here instead? + resolvedLocations, err := r.FilesByPath(string(result.RequestPath)) if err != nil { return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err) } @@ -136,8 +137,8 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err) } - switch entry.Metadata.TypeFlag { - case tar.TypeSymlink, tar.TypeLink: + switch entry.Metadata.Type { + case file.TypeSymlink, file.TypeHardLink: // the location we are searching may be a symlink, we should always work with the resolved file locations, err := r.FilesByPath(location.RealPath) if err != nil { @@ -161,7 +162,7 @@ func (r *imageSquashResolver) AllLocations() <-chan Location { results := make(chan Location) go func() { defer close(results) - for _, ref := range r.img.SquashedTree().AllFiles(file.AllTypes...) { + for _, ref := range r.img.SquashedTree().AllFiles(file.AllTypes()...) { results <- NewLocationFromImage(string(ref.RealPath), ref, r.img) } }() @@ -169,7 +170,7 @@ func (r *imageSquashResolver) AllLocations() <-chan Location { } func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, error) { - refs, err := r.img.FilesByMIMETypeFromSquash(types...) + refs, err := r.img.SquashedSearchContext().SearchByMIMEType(types...) if err != nil { return nil, err } @@ -184,61 +185,6 @@ func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, erro return locations, nil } -func (r *imageSquashResolver) FilesByExtension(extensions ...string) ([]Location, error) { - var locations []Location - for _, extension := range extensions { - refs, err := r.img.FilesByExtensionFromSquash(extension) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) - } - } - } - - return locations, nil -} - -func (r *imageSquashResolver) FilesByBasename(basenames ...string) ([]Location, error) { - var locations []Location - for _, basename := range basenames { - refs, err := r.img.FilesByBasenameFromSquash(basename) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) - } - } - } - - return locations, nil -} - -func (r *imageSquashResolver) FilesByBasenameGlob(globs ...string) ([]Location, error) { - var locations []Location - - for _, glob := range globs { - refs, err := r.img.FilesByBasenameGlobFromSquash(glob) - if err != nil { - return nil, err - } - - for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) - } - } - } - - return locations, nil -} - func (r *imageSquashResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { return fileMetadataByLocation(r.img, location) } diff --git a/syft/source/image_squash_resolver_test.go b/syft/source/image_squash_resolver_test.go index 1c82eac4d66..8083bd1c1c7 100644 --- a/syft/source/image_squash_resolver_test.go +++ b/syft/source/image_squash_resolver_test.go @@ -118,13 +118,10 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) { t.Errorf("we should always prefer real paths over ones with links") } - entry, err := img.FileCatalog.Get(actual.ref) - if err != nil { - t.Fatalf("failed to get metadata: %+v", err) - } + layer := img.FileCatalog.Layer(actual.ref) - if entry.Layer.Metadata.Index != c.resolveLayer { - t.Errorf("bad resolve layer: '%d'!='%d'", entry.Layer.Metadata.Index, c.resolveLayer) + if layer.Metadata.Index != c.resolveLayer { + t.Errorf("bad resolve layer: '%d'!='%d'", layer.Metadata.Index, c.resolveLayer) } }) } @@ -223,13 +220,10 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) { t.Errorf("we should always prefer real paths over ones with links") } - entry, err := img.FileCatalog.Get(actual.ref) - if err != nil { - t.Fatalf("failed to get metadata: %+v", err) - } + layer := img.FileCatalog.Layer(actual.ref) - if entry.Layer.Metadata.Index != c.resolveLayer { - t.Errorf("bad resolve layer: '%d'!='%d'", entry.Layer.Metadata.Index, c.resolveLayer) + if layer.Metadata.Index != c.resolveLayer { + t.Errorf("bad resolve layer: '%d'!='%d'", layer.Metadata.Index, c.resolveLayer) } }) } @@ -447,7 +441,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { name: "by basename", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasename("file-2.txt") + actualLocations, err := resolver.FilesByGlob("**/file-2.txt") assert.NoError(t, err) return actualLocations }, @@ -465,7 +459,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { name: "by basename glob", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByBasenameGlob("file-?.txt") + actualLocations, err := resolver.FilesByGlob("**/file-?.txt") assert.NoError(t, err) return actualLocations }, @@ -499,7 +493,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { { name: "by basename glob to links", runner: func(resolver FileResolver) []Location { - actualLocations, err := resolver.FilesByBasenameGlob("link-*") + actualLocations, err := resolver.FilesByGlob("**/link-*") assert.NoError(t, err) return actualLocations }, @@ -538,7 +532,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { name: "by extension", runner: func(resolver FileResolver) []Location { // links are searched, but resolve to the real files - actualLocations, err := resolver.FilesByExtension(".txt") + actualLocations, err := resolver.FilesByGlob("**/*.txt") assert.NoError(t, err) return actualLocations }, diff --git a/syft/source/location.go b/syft/source/location.go index f131057d192..a284d8dacce 100644 --- a/syft/source/location.go +++ b/syft/source/location.go @@ -5,7 +5,6 @@ import ( "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/internal/log" ) // Location represents a path relative to a particular filesystem resolved to a specific file.Reference. This struct is used as a key @@ -46,22 +45,11 @@ func NewLocationFromCoordinates(coordinates Coordinates) Location { // NewLocationFromImage creates a new Location representing the given path (extracted from the ref) relative to the given image. func NewLocationFromImage(virtualPath string, ref file.Reference, img *image.Image) Location { - entry, err := img.FileCatalog.Get(ref) - if err != nil { - log.Warnf("unable to find file catalog entry for ref=%+v", ref) - return Location{ - Coordinates: Coordinates{ - RealPath: string(ref.RealPath), - }, - VirtualPath: virtualPath, - ref: ref, - } - } - + layer := img.FileCatalog.Layer(ref) return Location{ Coordinates: Coordinates{ RealPath: string(ref.RealPath), - FileSystemID: entry.Layer.Metadata.Digest, + FileSystemID: layer.Metadata.Digest, }, VirtualPath: virtualPath, ref: ref, diff --git a/syft/source/mock_resolver.go b/syft/source/mock_resolver.go index f9af1ed2958..e0b624b8cc2 100644 --- a/syft/source/mock_resolver.go +++ b/syft/source/mock_resolver.go @@ -7,6 +7,8 @@ import ( "path" "github.com/bmatcuk/doublestar/v4" + + "github.com/anchore/stereoscope/pkg/file" ) var _ FileResolver = (*MockResolver)(nil) @@ -159,9 +161,9 @@ func (r MockResolver) FileMetadataByLocation(l Location) (FileMetadata, error) { } // other types not supported - ty := RegularFile + ty := file.TypeReg if info.IsDir() { - ty = Directory + ty = file.TypeDir } return FileMetadata{ From 8f45994721ebadbde2b4f59b860ddb6d5553f46a Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 1 Feb 2023 16:07:40 -0500 Subject: [PATCH 38/54] index files, links, and dirs within the directory resolver Signed-off-by: Alex Goodman --- syft/file/metadata_cataloger_test.go | 2 +- syft/formats/syftjson/encoder_test.go | 9 +++++---- syft/pkg/cataloger/deb/cataloger.go | 6 +++--- syft/source/directory_resolver.go | 2 ++ syft/source/image_all_layers_resolver.go | 6 +++--- syft/source/image_squash_resolver.go | 6 +++--- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/syft/file/metadata_cataloger_test.go b/syft/file/metadata_cataloger_test.go index 384ddedbbdc..e10e5c2c422 100644 --- a/syft/file/metadata_cataloger_test.go +++ b/syft/file/metadata_cataloger_test.go @@ -138,7 +138,7 @@ func TestFileMetadataCataloger(t *testing.T) { for _, test := range tests { t.Run(test.path, func(t *testing.T) { - ref, err := img.SquashedSearchContext().SearchByPath(test.path) + ref, err := img.SquashedSearchContext.SearchByPath(test.path) require.NoError(t, err) l := source.NewLocationFromImage(test.path, *ref.Reference, img) diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 216e60fc162..b2dc63e0ebf 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -5,6 +5,7 @@ import ( "regexp" "testing" + stereoFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" @@ -107,26 +108,26 @@ func TestEncodeFullJSONDocument(t *testing.T) { FileMetadata: map[source.Coordinates]source.FileMetadata{ source.NewLocation("/a/place").Coordinates: { Mode: 0775, - Type: "directory", + Type: stereoFile.TypeDir, UserID: 0, GroupID: 0, }, source.NewLocation("/a/place/a").Coordinates: { Mode: 0775, - Type: "regularFile", + Type: stereoFile.TypeReg, UserID: 0, GroupID: 0, }, source.NewLocation("/b").Coordinates: { Mode: 0775, - Type: "symbolicLink", + Type: stereoFile.TypeSymlink, LinkDestination: "/c", UserID: 0, GroupID: 0, }, source.NewLocation("/b/place/b").Coordinates: { Mode: 0644, - Type: "regularFile", + Type: stereoFile.TypeReg, UserID: 1, GroupID: 2, }, diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index ff9ce84f7bc..0e8f597e4fc 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -4,7 +4,6 @@ Package dpkg provides a concrete Cataloger implementation for Debian package DB package deb import ( - "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" ) @@ -13,6 +12,7 @@ const catalogerName = "dpkgdb-cataloger" // NewDpkgdbCataloger returns a new Deb package cataloger capable of parsing DPKG status DB files. func NewDpkgdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). - // TODO: split up and re-write the glob search patterns - WithParserByGlobs(parseDpkgDB, pkg.DpkgDBGlob) + // note: these globs have been intentionally split up in order to improve search performance, + // please do NOT combine into: "**/var/lib/dpkg/{status,status.d/**}" + WithParserByGlobs(parseDpkgDB, "**/var/lib/dpkg/status", "**/var/lib/dpkg/status.d/*") } diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index 0df2b80e668..9bc122e1a31 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -237,6 +237,7 @@ func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error location := NewLocationFromDirectory(p, *ref) metadata := fileMetadataFromPath(p, info, r.isInIndex(location)) r.addFileToFileTreeIndex(ref, metadata) + r.fileTreeIndex.Add(*ref, metadata) return nil } @@ -307,6 +308,7 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string metadata := fileMetadataFromPath(p, usedInfo, false) // note: to be consistent with other resolvers, don't record mime type for symlink destinations metadata.LinkDestination = linkTarget r.addFileToFileTreeIndex(ref, metadata) + r.fileTreeIndex.Add(*ref, metadata) return targetAbsPath, nil } diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index fa04b70c424..edce53b6921 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -84,7 +84,7 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error for _, path := range paths { for idx, layerIdx := range r.layers { - ref, err := r.img.Layers[layerIdx].SearchContext().SearchByPath(path, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) + ref, err := r.img.Layers[layerIdx].SearchContext.SearchByPath(path, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) if err != nil { return nil, err } @@ -125,7 +125,7 @@ func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, er for _, pattern := range patterns { for idx, layerIdx := range r.layers { - results, err := r.img.Layers[layerIdx].SquashedSearchContext().SearchByGlob(pattern, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) + results, err := r.img.Layers[layerIdx].SquashedSearchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks, filetree.DoNotFollowDeadBasenameLinks) if err != nil { return nil, fmt.Errorf("failed to resolve files by glob (%s): %w", pattern, err) } @@ -205,7 +205,7 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) { var locations []Location for _, layerIdx := range r.layers { - refs, err := r.img.Layers[layerIdx].SearchContext().SearchByMIMEType(types...) + refs, err := r.img.Layers[layerIdx].SearchContext.SearchByMIMEType(types...) if err != nil { return nil, err } diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index bd073bc22d0..832035f1f33 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -38,7 +38,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { uniqueLocations := make([]Location, 0) for _, path := range paths { - ref, err := r.img.SquashedSearchContext().SearchByPath(path, filetree.FollowBasenameLinks) + ref, err := r.img.SquashedSearchContext.SearchByPath(path, filetree.FollowBasenameLinks) if err != nil { return nil, err } @@ -79,7 +79,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) { var locations []Location for _, pattern := range patterns { - results, err := r.img.SquashedSearchContext().SearchByGlob(pattern, filetree.FollowBasenameLinks) + results, err := r.img.SquashedSearchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { return nil, fmt.Errorf("failed to resolve files by glob (%s): %w", pattern, err) } @@ -170,7 +170,7 @@ func (r *imageSquashResolver) AllLocations() <-chan Location { } func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, error) { - refs, err := r.img.SquashedSearchContext().SearchByMIMEType(types...) + refs, err := r.img.SquashedSearchContext.SearchByMIMEType(types...) if err != nil { return nil, err } From 30f51375e3a20408fc465ee0bd66f294c65477aa Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 3 Feb 2023 10:23:00 -0500 Subject: [PATCH 39/54] fix several resolver bugs and inconsistencies Signed-off-by: Alex Goodman --- syft/file/metadata_cataloger_test.go | 3 +- syft/formats/syftjson/model/file.go | 13 +- syft/formats/syftjson/to_format_model.go | 28 +- syft/formats/syftjson/to_format_model_test.go | 62 +++ syft/pkg/cataloger/java/cataloger.go | 2 +- syft/pkg/cataloger/java/cataloger_test.go | 2 + .../glob-paths/archives/example.tar.zstd | 1 + .../glob-paths/archives/example.tzstd | 1 + syft/pkg/cataloger/python/cataloger.go | 4 +- syft/pkg/cataloger/python/cataloger_test.go | 10 +- .../python/parse_wheel_egg_metadata.go | 2 +- syft/source/directory_resolver.go | 263 +++++---- syft/source/directory_resolver_test.go | 331 ++++++++--- syft/source/file_metadata.go | 34 -- syft/source/file_metadata_test.go | 57 -- syft/source/file_resolver.go | 22 +- syft/source/image_all_layers_resolver.go | 28 +- syft/source/image_all_layers_resolver_test.go | 486 ++++++++++++++++ syft/source/image_squash_resolver.go | 32 +- syft/source/image_squash_resolver_test.go | 525 +++++++++++++++++- syft/source/source.go | 15 +- syft/source/source_test.go | 275 ++++++--- .../symlinks-prune-indexing/before-path | 1 + .../symlinks-prune-indexing/c-file.txt | 1 + .../symlinks-prune-indexing/c-path | 1 + .../path/1/2/3/4/dont-index-me-twice.txt | 1 + .../5/6/7/8/dont-index-me-twice-either.txt | 1 + .../symlinks-prune-indexing/path/file.txt | 1 + 28 files changed, 1771 insertions(+), 431 deletions(-) create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zstd create mode 100644 syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzstd delete mode 100644 syft/source/file_metadata_test.go create mode 120000 syft/source/test-fixtures/symlinks-prune-indexing/before-path create mode 120000 syft/source/test-fixtures/symlinks-prune-indexing/c-file.txt create mode 120000 syft/source/test-fixtures/symlinks-prune-indexing/c-path create mode 100644 syft/source/test-fixtures/symlinks-prune-indexing/path/1/2/3/4/dont-index-me-twice.txt create mode 100644 syft/source/test-fixtures/symlinks-prune-indexing/path/5/6/7/8/dont-index-me-twice-either.txt create mode 100644 syft/source/test-fixtures/symlinks-prune-indexing/path/file.txt diff --git a/syft/file/metadata_cataloger_test.go b/syft/file/metadata_cataloger_test.go index e10e5c2c422..c29b80a2598 100644 --- a/syft/file/metadata_cataloger_test.go +++ b/syft/file/metadata_cataloger_test.go @@ -132,13 +132,14 @@ func TestFileMetadataCataloger(t *testing.T) { UserID: 0, GroupID: 0, MIMEType: "", + IsDir: true, }, }, } for _, test := range tests { t.Run(test.path, func(t *testing.T) { - ref, err := img.SquashedSearchContext.SearchByPath(test.path) + _, ref, err := img.SquashedTree().File(file.Path(test.path)) require.NoError(t, err) l := source.NewLocationFromImage(test.path, *ref.Reference, img) diff --git a/syft/formats/syftjson/model/file.go b/syft/formats/syftjson/model/file.go index 1ef827e2b96..1bed51e582e 100644 --- a/syft/formats/syftjson/model/file.go +++ b/syft/formats/syftjson/model/file.go @@ -1,7 +1,6 @@ package model import ( - stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" ) @@ -15,10 +14,10 @@ type File struct { } type FileMetadataEntry struct { - Mode int `json:"mode"` - Type stereoscopeFile.Type `json:"type"` - LinkDestination string `json:"linkDestination,omitempty"` - UserID int `json:"userID"` - GroupID int `json:"groupID"` - MIMEType string `json:"mimeType"` + Mode int `json:"mode"` + Type string `json:"type"` + LinkDestination string `json:"linkDestination,omitempty"` + UserID int `json:"userID"` + GroupID int `json:"groupID"` + MIMEType string `json:"mimeType"` } diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 00c62fc0db5..ba025272b46 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -5,6 +5,7 @@ import ( "sort" "strconv" + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" @@ -137,7 +138,7 @@ func toFileMetadataEntry(coordinates source.Coordinates, metadata *source.FileMe return &model.FileMetadataEntry{ Mode: mode, - Type: metadata.Type, + Type: toFileType(metadata.Type), LinkDestination: metadata.LinkDestination, UserID: metadata.UserID, GroupID: metadata.GroupID, @@ -145,6 +146,31 @@ func toFileMetadataEntry(coordinates source.Coordinates, metadata *source.FileMe } } +func toFileType(ty stereoscopeFile.Type) string { + switch ty { + case stereoscopeFile.TypeSymlink: + return "SymbolicLink" + case stereoscopeFile.TypeHardLink: + return "HardLink" + case stereoscopeFile.TypeDir: + return "Directory" + case stereoscopeFile.TypeSocket: + return "Socket" + case stereoscopeFile.TypeBlockDevice: + return "BlockDevice" + case stereoscopeFile.TypeCharacterDevice: + return "CharacterDevice" + case stereoscopeFile.TypeFifo: + return "FIFONode" + case stereoscopeFile.TypeReg: + return "RegularFile" + case stereoscopeFile.TypeIrregular: + return "IrregularFile" + default: + return "Unknown" + } +} + func toPackageModels(catalog *pkg.Catalog) []model.Package { artifacts := make([]model.Package, 0) if catalog == nil { diff --git a/syft/formats/syftjson/to_format_model_test.go b/syft/formats/syftjson/to_format_model_test.go index e44db8e779f..6811e6ac90f 100644 --- a/syft/formats/syftjson/to_format_model_test.go +++ b/syft/formats/syftjson/to_format_model_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/formats/syftjson/model" "github.com/anchore/syft/syft/source" @@ -98,3 +99,64 @@ func Test_toSourceModel(t *testing.T) { // assert all possible schemes were under test assert.ElementsMatch(t, allSchemes.List(), testedSchemes.List(), "not all source.Schemes are under test") } + +func Test_toFileType(t *testing.T) { + + badType := file.Type(0x1337) + var allTypesTested []file.Type + tests := []struct { + ty file.Type + name string + }{ + { + ty: file.TypeReg, + name: "RegularFile", + }, + { + ty: file.TypeDir, + name: "Directory", + }, + { + ty: file.TypeSymlink, + name: "SymbolicLink", + }, + { + ty: file.TypeHardLink, + name: "HardLink", + }, + { + ty: file.TypeSocket, + name: "Socket", + }, + { + ty: file.TypeCharacterDevice, + name: "CharacterDevice", + }, + { + ty: file.TypeBlockDevice, + name: "BlockDevice", + }, + { + ty: file.TypeFifo, + name: "FIFONode", + }, + { + ty: file.TypeIrregular, + name: "IrregularFile", + }, + { + ty: badType, + name: "Unknown", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.name, toFileType(tt.ty), "toFileType(%v)", tt.ty) + if tt.ty != badType { + allTypesTested = append(allTypesTested, tt.ty) + } + }) + } + + assert.ElementsMatch(t, allTypesTested, file.AllTypes(), "not all file.Types are under test") +} diff --git a/syft/pkg/cataloger/java/cataloger.go b/syft/pkg/cataloger/java/cataloger.go index 18e1aeefc6c..09ed0d1ab01 100644 --- a/syft/pkg/cataloger/java/cataloger.go +++ b/syft/pkg/cataloger/java/cataloger.go @@ -29,5 +29,5 @@ func NewJavaCataloger(cfg Config) *generic.Cataloger { // Pom files list dependencies that maybe not be locally installed yet. func NewJavaPomCataloger() *generic.Cataloger { return generic.NewCataloger("java-pom-cataloger"). - WithParserByGlobs(parserPomXML, "pom.xml") + WithParserByGlobs(parserPomXML, "**/pom.xml") } diff --git a/syft/pkg/cataloger/java/cataloger_test.go b/syft/pkg/cataloger/java/cataloger_test.go index 0bb0752926a..6ec834aff43 100644 --- a/syft/pkg/cataloger/java/cataloger_test.go +++ b/syft/pkg/cataloger/java/cataloger_test.go @@ -42,6 +42,8 @@ func Test_ArchiveCataloger_Globs(t *testing.T) { "archives/example.txz", "archives/example.tar.zst", "archives/example.tzst", + "archives/example.tar.zstd", + "archives/example.tzstd", }, }, } diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zstd b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zstd new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tar.zstd @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzstd b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzstd new file mode 100644 index 00000000000..8944cbcc070 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/glob-paths/archives/example.tzstd @@ -0,0 +1 @@ +example archive diff --git a/syft/pkg/cataloger/python/cataloger.go b/syft/pkg/cataloger/python/cataloger.go index 00536625780..1401c3a2f79 100644 --- a/syft/pkg/cataloger/python/cataloger.go +++ b/syft/pkg/cataloger/python/cataloger.go @@ -4,7 +4,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -const eggInfoExtension = ".egg-info" +const eggInfoGlob = "**/*.egg-info" // NewPythonIndexCataloger returns a new cataloger for python packages referenced from poetry lock files, requirements.txt files, and setup.py files. func NewPythonIndexCataloger() *generic.Cataloger { @@ -18,5 +18,5 @@ func NewPythonIndexCataloger() *generic.Cataloger { // NewPythonPackageCataloger returns a new cataloger for python packages within egg or wheel installation directories. func NewPythonPackageCataloger() *generic.Cataloger { return generic.NewCataloger("python-package-cataloger"). - WithParserByGlobs(parseWheelOrEgg, eggInfoExtension, "**/*dist-info/METADATA", "**/*egg-info/PKG-INFO") + WithParserByGlobs(parseWheelOrEgg, eggInfoGlob, "**/*dist-info/METADATA", "**/*egg-info/PKG-INFO") } diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 9d4bbeef7ab..8515daf4b41 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -3,6 +3,8 @@ package python import ( "testing" + "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -192,9 +194,7 @@ func Test_PackageCataloger(t *testing.T) { resolver := source.NewMockResolverForPaths(test.fixtures...) locations, err := resolver.FilesByPath(test.fixtures...) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) test.expectedPackage.Locations = source.NewLocationSet(locations...) @@ -223,9 +223,7 @@ func Test_PackageCataloger_IgnorePackage(t *testing.T) { resolver := source.NewMockResolverForPaths(test.MetadataFixture) actual, _, err := NewPythonPackageCataloger().Catalog(resolver) - if err != nil { - t.Fatalf("failed to catalog python package: %+v", err) - } + require.NoError(t, err) if len(actual) != 0 { t.Fatalf("Expected 0 packages but found: %d", len(actual)) diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index 44c6efe161d..ab97a06a94f 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -81,7 +81,7 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (pkg.PythonPackageMe // of egg metadata (as opposed to a directory that contains more metadata // files). func isEggRegularFile(path string) bool { - return file.GlobMatch("**/*"+eggInfoExtension, path) + return file.GlobMatch(eggInfoGlob, path) } // determineSitePackagesRootPath returns the path of the site packages root, diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index 9bc122e1a31..b3cded96734 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -30,9 +30,11 @@ var unixSystemRuntimePrefixes = []string{ "/sys", } +var errSkipPath = errors.New("skip path") + var _ FileResolver = (*directoryResolver)(nil) -type pathFilterFn func(string, os.FileInfo) bool +type pathIndexVisitor func(string, os.FileInfo, error) error // directoryResolver implements path and content access for the directory data source. type directoryResolver struct { @@ -42,14 +44,23 @@ type directoryResolver struct { currentWd string fileTree *filetree.FileTree fileTreeIndex filetree.Index + searchContext filetree.Searcher metadata map[file.ID]FileMetadata // TODO: wire up to report these paths in the json report - pathFilterFns []pathFilterFn - refsByMIMEType map[string][]file.Reference - errPaths map[string]error + pathIndexVisitors []pathIndexVisitor + errPaths map[string]error +} + +func newDirectoryResolver(root string, base string, pathFilters ...pathIndexVisitor) (*directoryResolver, error) { + resolver, err := newDirectoryResolverWithoutIndex(root, base, pathFilters...) + if err != nil { + return nil, err + } + + return resolver, resolver.index() } -func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) (*directoryResolver, error) { +func newDirectoryResolverWithoutIndex(root string, base string, pathFilters ...pathIndexVisitor) (*directoryResolver, error) { currentWD, err := os.Getwd() if err != nil { return nil, fmt.Errorf("could not get CWD: %w", err) @@ -88,7 +99,7 @@ func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) currentWdRelRoot = filepath.Clean(cleanRoot) } - resolver := directoryResolver{ + resolver := &directoryResolver{ path: cleanRoot, base: cleanBase, currentWd: cleanCWD, @@ -96,12 +107,39 @@ func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) fileTree: filetree.NewFileTree(), fileTreeIndex: filetree.NewIndex(), metadata: make(map[file.ID]FileMetadata), - pathFilterFns: append([]pathFilterFn{isUnallowableFileType, isUnixSystemRuntimePath}, pathFilters...), - refsByMIMEType: make(map[string][]file.Reference), + pathIndexVisitors: append([]pathIndexVisitor{requireFileInfo, disallowByFileType, disallowUnixSystemRuntimePath}, pathFilters...), errPaths: make(map[string]error), } - return &resolver, indexAllRoots(cleanRoot, resolver.indexTree) + // these additional stateful visitors should be the first thing considered when walking / indexing + resolver.pathIndexVisitors = append([]pathIndexVisitor{resolver.disallowRevisitingVisitor, resolver.disallowFileAccessErr}, resolver.pathIndexVisitors...) + + return resolver, nil +} + +func (r *directoryResolver) index() error { + return indexAllRoots(r.path, r.indexTree) +} + +func (r *directoryResolver) disallowRevisitingVisitor(path string, _ os.FileInfo, _ error) error { + // this prevents visiting: + // - link destinations twice, once for the real file and another through the virtual path + // - infinite link cycles + if indexed, metadata := r.hasBeenIndexed(path); indexed { + if metadata.IsDir { + // signal to walk() that we should skip this directory entirely + return fs.SkipDir + } + return errSkipPath + } + return nil +} + +func requireFileInfo(_ string, info os.FileInfo, _ error) error { + if info == nil { + return errSkipPath + } + return nil } func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]string, error) { @@ -129,7 +167,7 @@ func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]st return roots, nil } - return roots, filepath.Walk(root, + err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { stager.Current = path @@ -145,28 +183,33 @@ func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]st return nil }) -} -func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error) (string, error) { - // link cycles could cause a revisit --we should not allow this - if r.hasBeenIndexed(path) { - return "", nil + if err != nil { + return nil, fmt.Errorf("unable to index root=%q: %w", root, err) } + r.searchContext = filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) + + return roots, nil +} + +func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error) (string, error) { // ignore any path which a filter function returns true - for _, filterFn := range r.pathFilterFns { - if filterFn != nil && filterFn(path, info) { - if info != nil && info.IsDir() { - return "", fs.SkipDir + for _, filterFn := range r.pathIndexVisitors { + if filterFn == nil { + continue + } + + if filterErr := filterFn(path, info, err); filterErr != nil { + if errors.Is(filterErr, fs.SkipDir) { + // signal to walk() to skip this directory entirely (even if we're processing a file) + return "", filterErr } + // skip this path but don't affect walk() trajectory return "", nil } } - if r.isFileAccessErr(path, err) { - return "", nil - } - if info == nil { // walk may not be able to provide a FileInfo object, don't allow for this to stop indexing; keep track of the paths and continue. r.errPaths[path] = fmt.Errorf("no file info observable at path=%q", path) @@ -186,6 +229,13 @@ func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error) return newRoot, nil } +func (r *directoryResolver) disallowFileAccessErr(path string, _ os.FileInfo, err error) error { + if r.isFileAccessErr(path, err) { + return errSkipPath + } + return nil +} + func (r *directoryResolver) isFileAccessErr(path string, err error) bool { // don't allow for errors to stop indexing, keep track of the paths and continue. if err != nil { @@ -209,23 +259,29 @@ func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, e } } -func (r directoryResolver) hasBeenIndexed(p string) bool { +func (r directoryResolver) hasBeenIndexed(p string) (bool, *file.Metadata) { filePath := file.Path(p) if !r.fileTree.HasPath(filePath) { - return false + return false, nil } exists, ref, err := r.fileTree.File(filePath) if err != nil || !exists || !ref.HasReference() { - return false + return false, nil } // cases like "/" will be in the tree, but not been indexed yet (a special case). We want to capture // these cases as new paths to index. if !ref.HasReference() { - return false + return false, nil + } + + entry, err := r.fileTreeIndex.Get(*ref.Reference) + if err != nil { + return false, nil } - return r.fileTreeIndex.Exists(*ref.Reference) + + return true, &entry.Metadata } func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error { @@ -234,8 +290,7 @@ func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error return err } - location := NewLocationFromDirectory(p, *ref) - metadata := fileMetadataFromPath(p, info, r.isInIndex(location)) + metadata := file.NewMetadataFromPath(p, info) r.addFileToFileTreeIndex(ref, metadata) r.fileTreeIndex.Add(*ref, metadata) @@ -248,8 +303,7 @@ func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error { return err } - location := NewLocationFromDirectory(p, *ref) - metadata := fileMetadataFromPath(p, info, r.isInIndex(location)) + metadata := file.NewMetadataFromPath(p, info) r.addFileToFileTreeIndex(ref, metadata) r.fileTreeIndex.Add(*ref, metadata) @@ -257,8 +311,6 @@ func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error { } func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string, error) { - var usedInfo = info - linkTarget, err := os.Readlink(p) if err != nil { return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err) @@ -285,27 +337,17 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string } } - // previouslyIndexedDestination := r.hasBeenIndexed(linkTarget) - ref, err := r.fileTree.AddSymLink(file.Path(p), file.Path(linkTarget)) if err != nil { return "", err } - // if previouslyIndexedDestination { - // // this symlink is pointing to a spot on the filesystem that has already been indexed. We don't want to continue - // // searching this branch - // return "", nil - //} - targetAbsPath := linkTarget if !filepath.IsAbs(targetAbsPath) { targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) } - location := NewLocationFromDirectory(p, *ref) - location.VirtualPath = p - metadata := fileMetadataFromPath(p, usedInfo, false) // note: to be consistent with other resolvers, don't record mime type for symlink destinations + metadata := file.NewMetadataFromPath(p, info) metadata.LinkDestination = linkTarget r.addFileToFileTreeIndex(ref, metadata) r.fileTreeIndex.Add(*ref, metadata) @@ -315,9 +357,6 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string func (r directoryResolver) addFileToFileTreeIndex(ref *file.Reference, metadata FileMetadata) { if ref != nil { - if metadata.MIMEType != "" { - r.refsByMIMEType[metadata.MIMEType] = append(r.refsByMIMEType[metadata.MIMEType], *ref) - } r.metadata[ref.ID()] = metadata } } @@ -380,37 +419,24 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) } // we should be resolving symlinks and preserving this information as a VirtualPath to the real file - searchContext := filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) - ref, err := searchContext.SearchByPath(userStrPath, filetree.FollowBasenameLinks) + ref, err := r.searchContext.SearchByPath(userStrPath, filetree.FollowBasenameLinks) if err != nil { log.Tracef("unable to evaluate symlink for path=%q : %+v", userPath, err) continue } - // TODO: alex: is this the same as "exists"? + if !ref.HasReference() { continue } - // TODO: why not use stored metadata? - // TODO: today we don't store directory metadata while indexing, which would make the index much larger too - fileMeta, err := os.Stat(string(ref.RealPath)) - if errors.Is(err, os.ErrNotExist) { - // note: there are other kinds of errors other than os.ErrNotExist that may be given that is platform - // specific, but essentially hints at the same overall problem (that the path does not exist). Such an - // error could be syscall.ENOTDIR (see https://github.com/golang/go/issues/18974). - continue - } else if err != nil { - // we don't want to consider any other syscalls that may hint at non-existence of the file/dir as - // invalid paths. This logging statement is meant to raise IO or permissions related problems. - var pathErr *os.PathError - if !errors.As(err, &pathErr) { - log.Warnf("path is not valid (%s): %+v", ref.RealPath, err) - } + entry, err := r.fileTreeIndex.Get(*ref.Reference) + if err != nil { + log.Warnf("unable to get file by path=%q : %+v", userPath, err) continue } // don't consider directories - if fileMeta.IsDir() { + if entry.Metadata.IsDir { continue } @@ -434,27 +460,39 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) // FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image. func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) { - result := make([]Location, 0) + uniqueFileIDs := file.NewFileReferenceSet() + uniqueLocations := make([]Location, 0) for _, pattern := range patterns { - searchContext := filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) - refVias, err := searchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks) + refVias, err := r.searchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { return nil, err } for _, refVia := range refVias { - if refVia.HasReference() { - loc := NewVirtualLocationFromDirectory( - r.responsePath(string(refVia.Reference.RealPath)), // the actual path relative to the resolver root - r.responsePath(string(refVia.RequestPath)), // the path used to access this file, relative to the resolver root - *refVia.Reference, - ) - result = append(result, loc) + if !refVia.HasReference() || uniqueFileIDs.Contains(*refVia.Reference) { + continue } + entry, err := r.fileTreeIndex.Get(*refVia.Reference) + if err != nil { + return nil, fmt.Errorf("unable to get file metadata for reference %s: %w", refVia.Reference.RealPath, err) + } + + // don't consider directories + if entry.Metadata.IsDir { + continue + } + + loc := NewVirtualLocationFromDirectory( + r.responsePath(string(refVia.Reference.RealPath)), // the actual path relative to the resolver root + r.responsePath(string(refVia.RequestPath)), // the path used to access this file, relative to the resolver root + *refVia.Reference, + ) + uniqueFileIDs.Add(*refVia.Reference) + uniqueLocations = append(uniqueLocations, loc) } } - return result, nil + return uniqueLocations, nil } // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. @@ -478,34 +516,32 @@ func (r directoryResolver) FileContentsByLocation(location Location) (io.ReadClo if location.ref.RealPath == "" { return nil, errors.New("empty path given") } - if !r.isInIndex(location) { - // this is in cases where paths have been explicitly excluded from the tree index. In which case - // we should DENY all content requests. Why? These paths have been indicated to be inaccessible (either - // by preference or these files are not readable by the current user). - return nil, fmt.Errorf("file content is inaccessible path=%q", location.ref.RealPath) + + entry, err := r.fileTreeIndex.Get(location.ref) + if err != nil { + return nil, err } + + // don't consider directories + if entry.Type == file.TypeDir { + return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath) + } + // RealPath is posix so for windows directory resolver we need to translate // to its true on disk path. filePath := string(location.ref.RealPath) if runtime.GOOS == WindowsOS { filePath = posixToWindows(filePath) } - return file.NewLazyReadCloser(filePath), nil -} -func (r directoryResolver) isInIndex(location Location) bool { - if location.ref.RealPath == "" { - return false - } - return r.fileTree.HasPath(location.ref.RealPath, filetree.FollowBasenameLinks) + return file.NewLazyReadCloser(filePath), nil } func (r *directoryResolver) AllLocations() <-chan Location { results := make(chan Location) go func() { defer close(results) - // this should be all non-directory types - for _, ref := range r.fileTree.AllFiles(file.TypeReg, file.TypeSymlink, file.TypeHardLink, file.TypeBlockDevice, file.TypeCharacterDevice, file.TypeFifo) { + for _, ref := range r.fileTree.AllFiles(file.AllTypes()...) { results <- NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref) } }() @@ -522,15 +558,29 @@ func (r *directoryResolver) FileMetadataByLocation(location Location) (FileMetad } func (r *directoryResolver) FilesByMIMEType(types ...string) ([]Location, error) { - var locations []Location - for _, ty := range types { - if refs, ok := r.refsByMIMEType[ty]; ok { - for _, ref := range refs { - locations = append(locations, NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref)) - } + uniqueFileIDs := file.NewFileReferenceSet() + uniqueLocations := make([]Location, 0) + + refVias, err := r.searchContext.SearchByMIMEType(types...) + if err != nil { + return nil, err + } + for _, refVia := range refVias { + if !refVia.HasReference() { + continue + } + if uniqueFileIDs.Contains(*refVia.Reference) { + continue } + location := NewLocationFromDirectory( + r.responsePath(string(refVia.Reference.RealPath)), + *refVia.Reference, + ) + uniqueFileIDs.Add(*refVia.Reference) + uniqueLocations = append(uniqueLocations, location) } - return locations, nil + + return uniqueLocations, nil } func windowsToPosix(windowsPath string) (posixPath string) { @@ -560,23 +610,26 @@ func posixToWindows(posixPath string) (windowsPath string) { return filepath.Clean(volumeName + remainingTranslatedPath) } -func isUnixSystemRuntimePath(path string, _ os.FileInfo) bool { - return internal.HasAnyOfPrefixes(path, unixSystemRuntimePrefixes...) +func disallowUnixSystemRuntimePath(path string, _ os.FileInfo, _ error) error { + if internal.HasAnyOfPrefixes(path, unixSystemRuntimePrefixes...) { + return fs.SkipDir + } + return nil } -func isUnallowableFileType(_ string, info os.FileInfo) bool { +func disallowByFileType(_ string, info os.FileInfo, _ error) error { if info == nil { // we can't filter out by filetype for non-existent files - return false + return nil } switch file.TypeFromMode(info.Mode()) { case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFifo, file.TypeIrregular: - return true + return errSkipPath // note: symlinks that point to these files may still get by. // We handle this later in processing to help prevent against infinite links traversal. } - return false + return nil } func indexAllRoots(root string, indexer func(string, *progress.Stage) ([]string, error)) error { diff --git a/syft/source/directory_resolver_test.go b/syft/source/directory_resolver_test.go index af11ed30365..7602804bc3f 100644 --- a/syft/source/directory_resolver_test.go +++ b/syft/source/directory_resolver_test.go @@ -10,11 +10,12 @@ import ( "os" "path" "path/filepath" - "reflect" + "sort" "strings" "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -302,18 +303,14 @@ func TestDirectoryResolverDoesNotIgnoreRelativeSystemPaths(t *testing.T) { // let's make certain that "dev/place" is not ignored, since it is not "/dev/place" resolver, err := newDirectoryResolver("test-fixtures/system_paths/target", "") assert.NoError(t, err) - // ensure the correct filter function is wired up by default - expectedFn := reflect.ValueOf(isUnallowableFileType) - actualFn := reflect.ValueOf(resolver.pathFilterFns[0]) - assert.Equal(t, expectedFn.Pointer(), actualFn.Pointer()) // all paths should be found (non filtering matches a path) locations, err := resolver.FilesByGlob("**/place") assert.NoError(t, err) // 4: within target/ - // 1: target/link --> relative path to "place" + // 1: target/link --> relative path to "place" // NOTE: this is filtered out since it not unique relative to outside_root/link_target/place // 1: outside_root/link_target/place - assert.Len(t, locations, 6) + assert.Len(t, locations, 5) // ensure that symlink indexing outside of root worked testLocation := "test-fixtures/system_paths/outside_root/link_target/place" @@ -363,68 +360,65 @@ func Test_isUnallowableFileType(t *testing.T) { tests := []struct { name string info os.FileInfo - expected bool + expected error }{ { name: "regular file", info: testFileInfo{ mode: 0, }, - expected: false, }, { name: "dir", info: testFileInfo{ mode: os.ModeDir, }, - expected: false, }, { name: "symlink", info: testFileInfo{ mode: os.ModeSymlink, }, - expected: false, }, { name: "socket", info: testFileInfo{ mode: os.ModeSocket, }, - expected: true, + expected: errSkipPath, }, { name: "named pipe", info: testFileInfo{ mode: os.ModeNamedPipe, }, - expected: true, + expected: errSkipPath, }, { name: "char device", info: testFileInfo{ mode: os.ModeCharDevice, }, - expected: true, + expected: errSkipPath, }, { name: "block device", info: testFileInfo{ mode: os.ModeDevice, }, - expected: true, + expected: errSkipPath, }, { name: "irregular", info: testFileInfo{ mode: os.ModeIrregular, }, - expected: true, + expected: errSkipPath, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - assert.Equal(t, test.expected, isUnallowableFileType("dont/care", test.info)) + assert.Equal(t, test.expected, disallowByFileType("dont/care", test.info, nil)) }) } } @@ -646,12 +640,12 @@ func Test_IndexingNestedSymLinks(t *testing.T) { // check that we can access the same file via 2 symlinks locations, err = resolver.FilesByGlob("**/link_*") require.NoError(t, err) - require.Len(t, locations, 2) + require.Len(t, locations, 1) // you would think this is 2, however, they point to the same file, and glob only returns unique files // returned locations can be in any order expectedVirtualPaths := []string{ "link_to_link_to_new_readme", - "link_to_new_readme", + //"link_to_new_readme", // we filter out this one because the first symlink resolves to the same file } expectedRealPaths := []string{ @@ -670,8 +664,11 @@ func Test_IndexingNestedSymLinks(t *testing.T) { } func Test_IndexingNestedSymLinks_ignoredIndexes(t *testing.T) { - filterFn := func(path string, _ os.FileInfo) bool { - return strings.HasSuffix(path, string(filepath.Separator)+"readme") + filterFn := func(path string, _ os.FileInfo, _ error) error { + if strings.HasSuffix(path, string(filepath.Separator)+"readme") { + return errSkipPath + } + return nil } resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple", "", filterFn) @@ -732,6 +729,14 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) { cwd, err := os.Getwd() require.NoError(t, err) + r, err := newDirectoryResolver(".", "") + require.NoError(t, err) + + exists, existingPath, err := r.fileTree.File(file.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt"))) + require.True(t, exists) + require.NoError(t, err) + require.True(t, existingPath.HasReference()) + tests := []struct { name string location Location @@ -739,11 +744,9 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) { err bool }{ { - name: "use file reference for content requests", - location: NewLocationFromDirectory("some/place", file.Reference{ - RealPath: file.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt")), - }), - expects: "this file has contents", + name: "use file reference for content requests", + location: NewLocationFromDirectory("some/place", *existingPath.Reference), + expects: "this file has contents", }, { name: "error on empty file reference", @@ -753,8 +756,6 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - r, err := newDirectoryResolver(".", "") - require.NoError(t, err) actual, err := r.FileContentsByLocation(test.location) if test.err { @@ -775,44 +776,40 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) { func Test_isUnixSystemRuntimePath(t *testing.T) { tests := []struct { path string - expected bool + expected error }{ { - path: "proc/place", - expected: false, + path: "proc/place", }, { path: "/proc/place", - expected: true, + expected: fs.SkipDir, }, { path: "/proc", - expected: true, + expected: fs.SkipDir, }, { - path: "/pro/c", - expected: false, + path: "/pro/c", }, { - path: "/pro", - expected: false, + path: "/pro", }, { path: "/dev", - expected: true, + expected: fs.SkipDir, }, { path: "/sys", - expected: true, + expected: fs.SkipDir, }, { - path: "/something/sys", - expected: false, + path: "/something/sys", }, } for _, test := range tests { t.Run(test.path, func(t *testing.T) { - assert.Equal(t, test.expected, isUnixSystemRuntimePath(test.path, nil)) + assert.Equal(t, test.expected, disallowUnixSystemRuntimePath(test.path, nil, nil)) }) } } @@ -824,11 +821,9 @@ func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) { locations, err := resolver.FilesByGlob("**/file.target") require.NoError(t, err) - // Note: I'm not certain that this behavior is correct, but it is not an infinite loop (which is the point of the test) - // - block/loop0/file.target - // - devices/loop0/file.target - // - devices/loop0/subsystem/loop0/file.target - assert.Len(t, locations, 3) + + require.Len(t, locations, 1) + assert.Equal(t, "devices/loop0/file.target", locations[0].RealPath) } testWithTimeout(t, 5*time.Second, test) @@ -849,8 +844,11 @@ func testWithTimeout(t *testing.T, timeout time.Duration, test func(*testing.T)) } func Test_IncludeRootPathInIndex(t *testing.T) { - filterFn := func(path string, _ os.FileInfo) bool { - return path != "/" + filterFn := func(path string, _ os.FileInfo, _ error) error { + if path != "/" { + return fs.SkipDir + } + return nil } resolver, err := newDirectoryResolver("/", "", filterFn) @@ -865,23 +863,19 @@ func Test_IncludeRootPathInIndex(t *testing.T) { require.True(t, exists) } -func TestDirectoryResolver_indexPath(t *testing.T) { +func TestDirectoryResolver_indexPath_skipsNilFileInfo(t *testing.T) { // TODO: Ideally we can use an OS abstraction, which would obviate the need for real FS setup. tempFile, err := os.CreateTemp("", "") require.NoError(t, err) - resolver, err := newDirectoryResolver(tempFile.Name(), "") + resolver, err := newDirectoryResolverWithoutIndex(tempFile.Name(), "") require.NoError(t, err) t.Run("filtering path with nil os.FileInfo", func(t *testing.T) { - // We use one of these prefixes in order to trigger a pathFilterFn - filteredPath := unixSystemRuntimePrefixes[0] - - var fileInfo os.FileInfo = nil - assert.NotPanics(t, func() { - _, err := resolver.indexPath(filteredPath, fileInfo, nil) + _, err := resolver.indexPath("/dont-care", nil, nil) assert.NoError(t, err) + assert.False(t, resolver.fileTree.HasPath("/dont-care")) }) }) } @@ -1023,12 +1017,13 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { }, VirtualPath: "link-2", }, - { - Coordinates: Coordinates{ - RealPath: "file-2.txt", - }, - VirtualPath: "link-indirect", - }, + // we already have this real file path via another link, so only one is returned + //{ + // Coordinates: Coordinates{ + // RealPath: "file-2.txt", + // }, + // VirtualPath: "link-indirect", + //}, { Coordinates: Coordinates{ RealPath: "file-3.txt", @@ -1088,17 +1083,6 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { }, //VirtualPath: "parent/file-4.txt", }, - // note: this is a unique entry relative to the other resolvers. The other resolvers by nature - // do not walk full paths with symlinks, they treat symlinks as a single file. This resolver - // walks a real filsystem thus symlinks are followed and the same file can be found twice. - - // TODO: this is NOT good, since we should not be following symlinks in this case. This needs to be fixed. - { - Coordinates: Coordinates{ - RealPath: "parent/file-4.txt", - }, - VirtualPath: "parent-link/file-4.txt", - }, }, }, { @@ -1123,13 +1107,14 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { VirtualPath: "link-2", ref: file.Reference{RealPath: "file-2.txt"}, }, - { - Coordinates: Coordinates{ - RealPath: "file-2.txt", - }, - VirtualPath: "link-indirect", - ref: file.Reference{RealPath: "file-2.txt"}, - }, + // we already have this real file path via another link, so only one is returned + //{ + // Coordinates: Coordinates{ + // RealPath: "file-2.txt", + // }, + // VirtualPath: "link-indirect", + // ref: file.Reference{RealPath: "file-2.txt"}, + //}, { Coordinates: Coordinates{ RealPath: "file-3.txt", @@ -1172,17 +1157,6 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { }, //VirtualPath: "parent/file-4.txt", }, - // note: this is a unique entry relative to the other resolvers. The other resolvers by nature - // do not walk full paths with symlinks, they treat symlinks as a single file. This resolver - // walks a real filsystem thus symlinks are followed and the same file can be found twice. - - // TODO: this is NOT good, since we should not be following symlinks in this case. This needs to be fixed. - { - Coordinates: Coordinates{ - RealPath: "parent/file-4.txt", - }, - VirtualPath: "parent-link/file-4.txt", - }, }, }, { @@ -1235,3 +1209,176 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) { }) } } + +func TestDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) { + resolver, err := newDirectoryResolver("./test-fixtures/symlinks-prune-indexing", "") + require.NoError(t, err) + + allRealPaths := resolver.fileTree.AllRealPaths() + pathSet := file.NewPathSet(allRealPaths...) + + assert.False(t, + pathSet.Contains("/before-path/file.txt"), + "symlink destinations should only be indexed at their real path, not through their virtual (symlinked) path", + ) + + assert.False(t, + pathSet.Contains("/a-path/file.txt"), + "symlink destinations should only be indexed at their real path, not through their virtual (symlinked) path", + ) + +} + +func TestDirectoryResolver_SkipsAlreadyVisitedLinkDestinations(t *testing.T) { + var observedPaths []string + pathObserver := func(p string, _ os.FileInfo, _ error) error { + fields := strings.Split(p, "test-fixtures/symlinks-prune-indexing") + if len(fields) != 2 { + t.Fatalf("unable to parse path: %s", p) + } + clean := strings.TrimLeft(fields[1], "/") + if clean != "" { + observedPaths = append(observedPaths, clean) + } + return nil + } + resolver, err := newDirectoryResolverWithoutIndex("./test-fixtures/symlinks-prune-indexing", "") + require.NoError(t, err) + // we want to cut ahead of any possible filters to see what paths are considered for indexing (closest to walking) + resolver.pathIndexVisitors = append([]pathIndexVisitor{pathObserver}, resolver.pathIndexVisitors...) + + require.NoError(t, resolver.index()) + + expected := []string{ + "before-path", + "c-file.txt", + "c-path", + "path", + "path/1", + "path/1/2", + "path/1/2/3", + "path/1/2/3/4", + "path/1/2/3/4/dont-index-me-twice.txt", + "path/5", + "path/5/6", + "path/5/6/7", + "path/5/6/7/8", + "path/5/6/7/8/dont-index-me-twice-either.txt", + "path/file.txt", + // everything below is after the original tree is indexed, and we are now indexing additional roots from symlinks + "path", // considered from symlink before-path, but pruned + "before-path/file.txt", // considered from symlink c-file.txt, but pruned + "before-path", // considered from symlink c-path, but pruned + } + + assert.Equal(t, expected, observedPaths, "visited paths differ \n %s", cmp.Diff(expected, observedPaths)) + +} + +func TestDirectoryResolver_IndexesAllTypes(t *testing.T) { + resolver, err := newDirectoryResolver("./test-fixtures/symlinks-prune-indexing", "") + require.NoError(t, err) + + allRefs := resolver.fileTree.AllFiles(file.AllTypes()...) + var pathRefs []file.Reference + paths := strset.New() + for _, ref := range allRefs { + fields := strings.Split(string(ref.RealPath), "test-fixtures/symlinks-prune-indexing") + if len(fields) != 2 { + t.Fatalf("unable to parse path: %s", ref.RealPath) + } + clean := strings.TrimLeft(fields[1], "/") + if clean == "" { + continue + } + paths.Add(clean) + pathRefs = append(pathRefs, ref) + } + + pathsList := paths.List() + sort.Strings(pathsList) + + expected := []string{ + "before-path", // link + "c-file.txt", // link + "c-path", // link + "path", // dir + "path/1", // dir + "path/1/2", // dir + "path/1/2/3", // dir + "path/1/2/3/4", // dir + "path/1/2/3/4/dont-index-me-twice.txt", // file + "path/5", // dir + "path/5/6", // dir + "path/5/6/7", // dir + "path/5/6/7/8", // dir + "path/5/6/7/8/dont-index-me-twice-either.txt", // file + "path/file.txt", // file + } + expectedSet := strset.New(expected...) + + // make certain all expected paths are in the tree (and no extra ones are their either) + + assert.True(t, paths.IsEqual(expectedSet), "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, pathsList)) + + // make certain that the paths are also in the file index + + for _, ref := range pathRefs { + _, err := resolver.fileTreeIndex.Get(ref) + require.NoError(t, err) + } + +} + +func TestDirectoryResolver_FilesContents_errorOnDirRequest(t *testing.T) { + resolver, err := newDirectoryResolver("./test-fixtures/system_paths", "") + assert.NoError(t, err) + + var dirLoc *Location + for loc := range resolver.AllLocations() { + entry, err := resolver.fileTreeIndex.Get(loc.ref) + require.NoError(t, err) + if entry.Metadata.IsDir { + dirLoc = &loc + break + } + } + + require.NotNil(t, dirLoc) + + reader, err := resolver.FileContentsByLocation(*dirLoc) + require.Error(t, err) + require.Nil(t, reader) +} + +func TestDirectoryResolver_AllLocations(t *testing.T) { + resolver, err := newDirectoryResolver("./test-fixtures/symlinks-from-image-symlinks-fixture", "") + assert.NoError(t, err) + + paths := strset.New() + for loc := range resolver.AllLocations() { + if strings.HasPrefix(loc.RealPath, "/") { + // ignore outside of the fixture root for now + continue + } + paths.Add(loc.RealPath) + } + expected := []string{ + "file-1.txt", + "file-2.txt", + "file-3.txt", + "link-1", + "link-2", + "link-dead", + "link-indirect", + "link-within", + "parent", + "parent-link", + "parent/file-4.txt", + } + + pathsList := paths.List() + sort.Strings(pathsList) + + assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List())) +} diff --git a/syft/source/file_metadata.go b/syft/source/file_metadata.go index 8d541e51e7e..0763564d0fb 100644 --- a/syft/source/file_metadata.go +++ b/syft/source/file_metadata.go @@ -1,11 +1,8 @@ package source import ( - "os" - "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/internal/log" ) type FileMetadata = file.Metadata @@ -18,34 +15,3 @@ func fileMetadataByLocation(img *image.Image, location Location) (file.Metadata, return entry.Metadata, nil } - -func fileMetadataFromPath(path string, info os.FileInfo, withMIMEType bool) file.Metadata { - var mimeType string - uid, gid := GetXid(info) - - if withMIMEType { - f, err := os.Open(path) - if err != nil { - // TODO: it may be that the file is inaccessible, however, this is not an error or a warning. In the future we need to track these as known-unknowns - f = nil - } else { - defer func() { - if err := f.Close(); err != nil { - log.Warnf("unable to close file while obtaining metadata: %s", path) - } - }() - } - - mimeType = file.MIMEType(f) - } - - return FileMetadata{ - Mode: info.Mode(), - Type: file.TypeFromMode(info.Mode()), - // unsupported across platforms - UserID: uid, - GroupID: gid, - Size: info.Size(), - MIMEType: mimeType, - } -} diff --git a/syft/source/file_metadata_test.go b/syft/source/file_metadata_test.go deleted file mode 100644 index 3bdedb42ff1..00000000000 --- a/syft/source/file_metadata_test.go +++ /dev/null @@ -1,57 +0,0 @@ -//go:build !windows -// +build !windows - -package source - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_fileMetadataFromPath(t *testing.T) { - - tests := []struct { - path string - withMIMEType bool - expectedType string - expectedMIMEType string - }{ - { - path: "test-fixtures/symlinks-simple/readme", - withMIMEType: true, - expectedType: "RegularFile", - expectedMIMEType: "text/plain", - }, - { - path: "test-fixtures/symlinks-simple/link_to_new_readme", - withMIMEType: true, - expectedType: "SymbolicLink", - expectedMIMEType: "text/plain", - }, - { - path: "test-fixtures/symlinks-simple/readme", - withMIMEType: false, - expectedType: "RegularFile", - expectedMIMEType: "", - }, - { - path: "test-fixtures/symlinks-simple/link_to_new_readme", - withMIMEType: false, - expectedType: "SymbolicLink", - expectedMIMEType: "", - }, - } - for _, test := range tests { - t.Run(test.path, func(t *testing.T) { - info, err := os.Lstat(test.path) - require.NoError(t, err) - - actual := fileMetadataFromPath(test.path, info, test.withMIMEType) - assert.Equal(t, test.expectedMIMEType, actual.MIMEType) - assert.Equal(t, test.expectedType, string(actual.Type)) - }) - } -} diff --git a/syft/source/file_resolver.go b/syft/source/file_resolver.go index 2f3bb188f8f..63b5dc90b04 100644 --- a/syft/source/file_resolver.go +++ b/syft/source/file_resolver.go @@ -24,18 +24,36 @@ type FileMetadataResolver interface { // FilePathResolver knows how to get a Location for given string paths and globs type FilePathResolver interface { // HasPath indicates if the given path exists in the underlying source. + // The implementation for this may vary, however, generally the following considerations should be made: + // - full symlink resolution should be performed on all requests + // - returns locations for any file or directory HasPath(string) bool - // FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches) + + // FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches). + // The implementation for this may vary, however, generally the following considerations should be made: + // - full symlink resolution should be performed on all requests + // - only returns locations to files (NOT directories) FilesByPath(paths ...string) ([]Location, error) + // FilesByGlob fetches a set of file references for the given glob matches + // The implementation for this may vary, however, generally the following considerations should be made: + // - full symlink resolution should be performed on all requests + // - if multiple paths to the same file are found, the best single match should be returned + // - only returns locations to files (NOT directories) FilesByGlob(patterns ...string) ([]Location, error) - // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types + + // FilesByMIMEType fetches a set of file references which the contents have been classified as one of the given MIME Types. FilesByMIMEType(types ...string) ([]Location, error) + // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. // This is helpful when attempting to find a file that is in the same layer or lower as another file. RelativeFileByPath(_ Location, path string) *Location } type FileLocationResolver interface { + // AllLocations returns a channel of all file references from the underlying source. + // The implementation for this may vary, however, generally the following considerations should be made: + // - NO symlink resolution should be performed on results + // - returns locations for any file or directory AllLocations() <-chan Location } diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index edce53b6921..eeb09e3422b 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -1,7 +1,6 @@ package source import ( - "archive/tar" "fmt" "io" @@ -56,7 +55,7 @@ func (r *imageAllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs fil return nil, fmt.Errorf("unable to fetch metadata (ref=%+v): %w", ref, err) } - if entry.Metadata.Type == file.TypeHardLink || entry.Metadata.Type == tar.TypeSymlink { + if entry.Metadata.Type == file.TypeHardLink || entry.Metadata.Type == file.TypeSymlink { // a link may resolve in this layer or higher, assuming a squashed tree is used to search // we should search all possible resolutions within the valid source for _, subLayerIdx := range r.layers[layerIdx:] { @@ -142,11 +141,12 @@ func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, er if err != nil { return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err) } + // don't consider directories if metadata.Metadata.IsDir { continue } } - // TODO: alex: can't we just use the result.Reference here instead? + refResults, err := r.fileByRef(*result.Reference, uniqueFileIDs, idx) if err != nil { return nil, err @@ -197,27 +197,39 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R return nil, fmt.Errorf("no contents for location=%q", location.VirtualPath) } location = *newLocation + case file.TypeDir: + return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath) } return r.img.FileContentsByRef(location.ref) } func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) { - var locations []Location - for _, layerIdx := range r.layers { + uniqueFileIDs := file.NewFileReferenceSet() + uniqueLocations := make([]Location, 0) + + for idx, layerIdx := range r.layers { refs, err := r.img.Layers[layerIdx].SearchContext.SearchByMIMEType(types...) if err != nil { return nil, err } for _, ref := range refs { - if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RealPath), *ref.Reference, r.img)) + if !ref.HasReference() { + continue + } + + refResults, err := r.fileByRef(*ref.Reference, uniqueFileIDs, idx) + if err != nil { + return nil, err + } + for _, refResult := range refResults { + uniqueLocations = append(uniqueLocations, NewLocationFromImage(string(ref.RequestPath), refResult, r.img)) } } } - return locations, nil + return uniqueLocations, nil } func (r *imageAllLayersResolver) AllLocations() <-chan Location { diff --git a/syft/source/image_all_layers_resolver_test.go b/syft/source/image_all_layers_resolver_test.go index d7ef8ebcf83..a9932158c3c 100644 --- a/syft/source/image_all_layers_resolver_test.go +++ b/syft/source/image_all_layers_resolver_test.go @@ -2,8 +2,11 @@ package source import ( "io" + "sort" "testing" + "github.com/google/go-cmp/cmp" + "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -354,6 +357,30 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) { } } +func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) { + + img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + + resolver, err := newAllLayersResolver(img) + assert.NoError(t, err) + + var dirLoc *Location + for loc := range resolver.AllLocations() { + entry, err := resolver.img.FileCatalog.Get(loc.ref) + require.NoError(t, err) + if entry.Metadata.IsDir { + dirLoc = &loc + break + } + } + + require.NotNil(t, dirLoc) + + reader, err := resolver.FileContentsByLocation(*dirLoc) + require.Error(t, err) + require.Nil(t, reader) +} + func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { tests := []struct { name string @@ -400,6 +427,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { }, VirtualPath: "/file-2.txt", }, + // note: we're de-duping the redundant access to file-3.txt + // ... (there would usually be two copies) { Coordinates: Coordinates{ RealPath: "/file-3.txt", @@ -656,3 +685,460 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { } } + +func TestAllLayersResolver_AllLocations(t *testing.T) { + img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + + resolver, err := newAllLayersResolver(img) + assert.NoError(t, err) + + paths := strset.New() + for loc := range resolver.AllLocations() { + paths.Add(loc.RealPath) + } + expected := []string{ + "/file-1.txt", + "/file-2.txt", + "/file-3.txt", + "/link-1", + "/link-2", + "/link-dead", + "/link-indirect", + "/link-within", + "/parent", + "/parent-link", + "/parent/file-4.txt", + + // from base image... + "/bin", + "/bin/[", + "/bin/[[", + "/bin/acpid", + "/bin/add-shell", + "/bin/addgroup", + "/bin/adduser", + "/bin/adjtimex", + "/bin/ar", + "/bin/arch", + "/bin/arp", + "/bin/arping", + "/bin/ascii", + "/bin/ash", + "/bin/awk", + "/bin/base32", + "/bin/base64", + "/bin/basename", + "/bin/bc", + "/bin/beep", + "/bin/blkdiscard", + "/bin/blkid", + "/bin/blockdev", + "/bin/bootchartd", + "/bin/brctl", + "/bin/bunzip2", + "/bin/busybox", + "/bin/bzcat", + "/bin/bzip2", + "/bin/cal", + "/bin/cat", + "/bin/chat", + "/bin/chattr", + "/bin/chgrp", + "/bin/chmod", + "/bin/chown", + "/bin/chpasswd", + "/bin/chpst", + "/bin/chroot", + "/bin/chrt", + "/bin/chvt", + "/bin/cksum", + "/bin/clear", + "/bin/cmp", + "/bin/comm", + "/bin/conspy", + "/bin/cp", + "/bin/cpio", + "/bin/crc32", + "/bin/crond", + "/bin/crontab", + "/bin/cryptpw", + "/bin/cttyhack", + "/bin/cut", + "/bin/date", + "/bin/dc", + "/bin/dd", + "/bin/deallocvt", + "/bin/delgroup", + "/bin/deluser", + "/bin/depmod", + "/bin/devmem", + "/bin/df", + "/bin/dhcprelay", + "/bin/diff", + "/bin/dirname", + "/bin/dmesg", + "/bin/dnsd", + "/bin/dnsdomainname", + "/bin/dos2unix", + "/bin/dpkg", + "/bin/dpkg-deb", + "/bin/du", + "/bin/dumpkmap", + "/bin/dumpleases", + "/bin/echo", + "/bin/ed", + "/bin/egrep", + "/bin/eject", + "/bin/env", + "/bin/envdir", + "/bin/envuidgid", + "/bin/ether-wake", + "/bin/expand", + "/bin/expr", + "/bin/factor", + "/bin/fakeidentd", + "/bin/fallocate", + "/bin/false", + "/bin/fatattr", + "/bin/fbset", + "/bin/fbsplash", + "/bin/fdflush", + "/bin/fdformat", + "/bin/fdisk", + "/bin/fgconsole", + "/bin/fgrep", + "/bin/find", + "/bin/findfs", + "/bin/flock", + "/bin/fold", + "/bin/free", + "/bin/freeramdisk", + "/bin/fsck", + "/bin/fsck.minix", + "/bin/fsfreeze", + "/bin/fstrim", + "/bin/fsync", + "/bin/ftpd", + "/bin/ftpget", + "/bin/ftpput", + "/bin/fuser", + "/bin/getconf", + "/bin/getopt", + "/bin/getty", + "/bin/grep", + "/bin/groups", + "/bin/gunzip", + "/bin/gzip", + "/bin/halt", + "/bin/hd", + "/bin/hdparm", + "/bin/head", + "/bin/hexdump", + "/bin/hexedit", + "/bin/hostid", + "/bin/hostname", + "/bin/httpd", + "/bin/hush", + "/bin/hwclock", + "/bin/i2cdetect", + "/bin/i2cdump", + "/bin/i2cget", + "/bin/i2cset", + "/bin/i2ctransfer", + "/bin/id", + "/bin/ifconfig", + "/bin/ifdown", + "/bin/ifenslave", + "/bin/ifplugd", + "/bin/ifup", + "/bin/inetd", + "/bin/init", + "/bin/insmod", + "/bin/install", + "/bin/ionice", + "/bin/iostat", + "/bin/ip", + "/bin/ipaddr", + "/bin/ipcalc", + "/bin/ipcrm", + "/bin/ipcs", + "/bin/iplink", + "/bin/ipneigh", + "/bin/iproute", + "/bin/iprule", + "/bin/iptunnel", + "/bin/kbd_mode", + "/bin/kill", + "/bin/killall", + "/bin/killall5", + "/bin/klogd", + "/bin/last", + "/bin/less", + "/bin/link", + "/bin/linux32", + "/bin/linux64", + "/bin/linuxrc", + "/bin/ln", + "/bin/loadfont", + "/bin/loadkmap", + "/bin/logger", + "/bin/login", + "/bin/logname", + "/bin/logread", + "/bin/losetup", + "/bin/lpd", + "/bin/lpq", + "/bin/lpr", + "/bin/ls", + "/bin/lsattr", + "/bin/lsmod", + "/bin/lsof", + "/bin/lspci", + "/bin/lsscsi", + "/bin/lsusb", + "/bin/lzcat", + "/bin/lzma", + "/bin/lzop", + "/bin/makedevs", + "/bin/makemime", + "/bin/man", + "/bin/md5sum", + "/bin/mdev", + "/bin/mesg", + "/bin/microcom", + "/bin/mim", + "/bin/mkdir", + "/bin/mkdosfs", + "/bin/mke2fs", + "/bin/mkfifo", + "/bin/mkfs.ext2", + "/bin/mkfs.minix", + "/bin/mkfs.vfat", + "/bin/mknod", + "/bin/mkpasswd", + "/bin/mkswap", + "/bin/mktemp", + "/bin/modinfo", + "/bin/modprobe", + "/bin/more", + "/bin/mount", + "/bin/mountpoint", + "/bin/mpstat", + "/bin/mt", + "/bin/mv", + "/bin/nameif", + "/bin/nanddump", + "/bin/nandwrite", + "/bin/nbd-client", + "/bin/nc", + "/bin/netstat", + "/bin/nice", + "/bin/nl", + "/bin/nmeter", + "/bin/nohup", + "/bin/nologin", + "/bin/nproc", + "/bin/nsenter", + "/bin/nslookup", + "/bin/ntpd", + "/bin/od", + "/bin/openvt", + "/bin/partprobe", + "/bin/passwd", + "/bin/paste", + "/bin/patch", + "/bin/pgrep", + "/bin/pidof", + "/bin/ping", + "/bin/ping6", + "/bin/pipe_progress", + "/bin/pivot_root", + "/bin/pkill", + "/bin/pmap", + "/bin/popmaildir", + "/bin/poweroff", + "/bin/powertop", + "/bin/printenv", + "/bin/printf", + "/bin/ps", + "/bin/pscan", + "/bin/pstree", + "/bin/pwd", + "/bin/pwdx", + "/bin/raidautorun", + "/bin/rdate", + "/bin/rdev", + "/bin/readahead", + "/bin/readlink", + "/bin/readprofile", + "/bin/realpath", + "/bin/reboot", + "/bin/reformime", + "/bin/remove-shell", + "/bin/renice", + "/bin/reset", + "/bin/resize", + "/bin/resume", + "/bin/rev", + "/bin/rm", + "/bin/rmdir", + "/bin/rmmod", + "/bin/route", + "/bin/rpm", + "/bin/rpm2cpio", + "/bin/rtcwake", + "/bin/run-init", + "/bin/run-parts", + "/bin/runlevel", + "/bin/runsv", + "/bin/runsvdir", + "/bin/rx", + "/bin/script", + "/bin/scriptreplay", + "/bin/sed", + "/bin/sendmail", + "/bin/seq", + "/bin/setarch", + "/bin/setconsole", + "/bin/setfattr", + "/bin/setfont", + "/bin/setkeycodes", + "/bin/setlogcons", + "/bin/setpriv", + "/bin/setserial", + "/bin/setsid", + "/bin/setuidgid", + "/bin/sh", + "/bin/sha1sum", + "/bin/sha256sum", + "/bin/sha3sum", + "/bin/sha512sum", + "/bin/showkey", + "/bin/shred", + "/bin/shuf", + "/bin/slattach", + "/bin/sleep", + "/bin/smemcap", + "/bin/softlimit", + "/bin/sort", + "/bin/split", + "/bin/ssl_client", + "/bin/start-stop-daemon", + "/bin/stat", + "/bin/strings", + "/bin/stty", + "/bin/su", + "/bin/sulogin", + "/bin/sum", + "/bin/sv", + "/bin/svc", + "/bin/svlogd", + "/bin/svok", + "/bin/swapoff", + "/bin/swapon", + "/bin/switch_root", + "/bin/sync", + "/bin/sysctl", + "/bin/syslogd", + "/bin/tac", + "/bin/tail", + "/bin/tar", + "/bin/taskset", + "/bin/tc", + "/bin/tcpsvd", + "/bin/tee", + "/bin/telnet", + "/bin/telnetd", + "/bin/test", + "/bin/tftp", + "/bin/tftpd", + "/bin/time", + "/bin/timeout", + "/bin/top", + "/bin/touch", + "/bin/tr", + "/bin/traceroute", + "/bin/traceroute6", + "/bin/true", + "/bin/truncate", + "/bin/ts", + "/bin/tty", + "/bin/ttysize", + "/bin/tunctl", + "/bin/ubiattach", + "/bin/ubidetach", + "/bin/ubimkvol", + "/bin/ubirename", + "/bin/ubirmvol", + "/bin/ubirsvol", + "/bin/ubiupdatevol", + "/bin/udhcpc", + "/bin/udhcpc6", + "/bin/udhcpd", + "/bin/udpsvd", + "/bin/uevent", + "/bin/umount", + "/bin/uname", + "/bin/unexpand", + "/bin/uniq", + "/bin/unix2dos", + "/bin/unlink", + "/bin/unlzma", + "/bin/unshare", + "/bin/unxz", + "/bin/unzip", + "/bin/uptime", + "/bin/users", + "/bin/usleep", + "/bin/uudecode", + "/bin/uuencode", + "/bin/vconfig", + "/bin/vi", + "/bin/vlock", + "/bin/volname", + "/bin/w", + "/bin/wall", + "/bin/watch", + "/bin/watchdog", + "/bin/wc", + "/bin/wget", + "/bin/which", + "/bin/who", + "/bin/whoami", + "/bin/whois", + "/bin/xargs", + "/bin/xxd", + "/bin/xz", + "/bin/xzcat", + "/bin/yes", + "/bin/zcat", + "/bin/zcip", + "/dev", + "/etc", + "/etc/group", + "/etc/localtime", + "/etc/network", + "/etc/network/if-down.d", + "/etc/network/if-post-down.d", + "/etc/network/if-pre-up.d", + "/etc/network/if-up.d", + "/etc/passwd", + "/etc/shadow", + "/home", + "/proc", + "/root", + "/sys", + "/tmp", + "/usr", + "/usr/sbin", + "/var", + "/var/spool", + "/var/spool/mail", + "/var/www", + } + + pathsList := paths.List() + sort.Strings(pathsList) + + assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List())) +} diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index 832035f1f33..3c85d7a6e73 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -55,6 +55,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { if err != nil { return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err) } + // don't consider directories if metadata.Metadata.IsDir { continue } @@ -77,7 +78,9 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { // FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image. func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) { - var locations []Location + uniqueFileIDs := file.NewFileReferenceSet() + uniqueLocations := make([]Location, 0) + for _, pattern := range patterns { results, err := r.img.SquashedSearchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks) if err != nil { @@ -98,6 +101,7 @@ func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error if err != nil { return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err) } + // don't consider directories if metadata.Metadata.IsDir { continue } @@ -107,11 +111,17 @@ func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error if err != nil { return nil, fmt.Errorf("failed to find files by path (result=%+v): %w", result, err) } - locations = append(locations, resolvedLocations...) + for _, resolvedLocation := range resolvedLocations { + if uniqueFileIDs.Contains(resolvedLocation.ref) { + continue + } + uniqueFileIDs.Add(resolvedLocation.ref) + uniqueLocations = append(uniqueLocations, resolvedLocation) + } } } - return locations, nil + return uniqueLocations, nil } // RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference. @@ -153,6 +163,8 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read default: return nil, fmt.Errorf("link resolution resulted in multiple results while resolving content location: %+v", location) } + case file.TypeDir: + return nil, fmt.Errorf("unable to get file contents for directory: %+v", location) } return r.img.FileContentsByRef(location.ref) @@ -175,14 +187,22 @@ func (r *imageSquashResolver) FilesByMIMEType(types ...string) ([]Location, erro return nil, err } - var locations []Location + uniqueFileIDs := file.NewFileReferenceSet() + uniqueLocations := make([]Location, 0) + for _, ref := range refs { if ref.HasReference() { - locations = append(locations, NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img)) + if uniqueFileIDs.Contains(*ref.Reference) { + continue + } + location := NewLocationFromImage(string(ref.RequestPath), *ref.Reference, r.img) + + uniqueFileIDs.Add(*ref.Reference) + uniqueLocations = append(uniqueLocations, location) } } - return locations, nil + return uniqueLocations, nil } func (r *imageSquashResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { diff --git a/syft/source/image_squash_resolver_test.go b/syft/source/image_squash_resolver_test.go index 8083bd1c1c7..d614ce83b4b 100644 --- a/syft/source/image_squash_resolver_test.go +++ b/syft/source/image_squash_resolver_test.go @@ -294,26 +294,26 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { tests := []struct { name string - fixture string + path string contents []string }{ { - name: "one degree", - fixture: "link-2", + name: "one degree", + path: "link-2", contents: []string{ "NEW file override!", // always from the squashed perspective }, }, { - name: "two degrees", - fixture: "link-indirect", + name: "two degrees", + path: "link-indirect", contents: []string{ "NEW file override!", // always from the squashed perspective }, }, { name: "dead link", - fixture: "link-dead", + path: "link-dead", contents: []string{}, }, } @@ -325,7 +325,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { resolver, err := newImageSquashResolver(img) assert.NoError(t, err) - refs, err := resolver.FilesByPath(test.fixture) + refs, err := resolver.FilesByPath(test.path) require.NoError(t, err) assert.Len(t, refs, len(test.contents)) @@ -343,6 +343,30 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { } } +func TestSquashImageResolver_FilesContents_errorOnDirRequest(t *testing.T) { + + img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + + resolver, err := newImageSquashResolver(img) + assert.NoError(t, err) + + var dirLoc *Location + for loc := range resolver.AllLocations() { + entry, err := resolver.img.FileCatalog.Get(loc.ref) + require.NoError(t, err) + if entry.Metadata.IsDir { + dirLoc = &loc + break + } + } + + require.NotNil(t, dirLoc) + + reader, err := resolver.FileContentsByLocation(*dirLoc) + require.Error(t, err) + require.Nil(t, reader) +} + func Test_imageSquashResolver_resolvesLinks(t *testing.T) { tests := []struct { name string @@ -423,12 +447,14 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { }, VirtualPath: "/link-2", }, - { - Coordinates: Coordinates{ - RealPath: "/file-2.txt", - }, - VirtualPath: "/link-indirect", - }, + // though this is a link, and it matches to the file, the resolver de-duplicates files + // by the real path, so it is not included in the results + //{ + // Coordinates: Coordinates{ + // RealPath: "/file-2.txt", + // }, + // VirtualPath: "/link-indirect", + //}, { Coordinates: Coordinates{ RealPath: "/file-3.txt", @@ -512,13 +538,14 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { VirtualPath: "/link-2", ref: file.Reference{RealPath: "/file-2.txt"}, }, - { - Coordinates: Coordinates{ - RealPath: "/file-2.txt", - }, - VirtualPath: "/link-indirect", - ref: file.Reference{RealPath: "/file-2.txt"}, - }, + // we already have this real file path via another link, so only one is returned + //{ + // Coordinates: Coordinates{ + // RealPath: "/file-2.txt", + // }, + // VirtualPath: "/link-indirect", + // ref: file.Reference{RealPath: "/file-2.txt"}, + //}, { Coordinates: Coordinates{ RealPath: "/file-3.txt", @@ -618,6 +645,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { } func compareLocations(t *testing.T, expected, actual []Location) { + t.Helper() ignoreUnexported := cmpopts.IgnoreFields(Location{}, "ref") ignoreFS := cmpopts.IgnoreFields(Coordinates{}, "FileSystemID") @@ -633,3 +661,460 @@ func compareLocations(t *testing.T, expected, actual []Location) { } } + +func TestSquashResolver_AllLocations(t *testing.T) { + img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + + resolver, err := newImageSquashResolver(img) + assert.NoError(t, err) + + paths := strset.New() + for loc := range resolver.AllLocations() { + paths.Add(loc.RealPath) + } + expected := []string{ + "/file-1.txt", + "/file-2.txt", + "/file-3.txt", + "/link-1", + "/link-2", + "/link-dead", + "/link-indirect", + "/link-within", + "/parent", + "/parent-link", + "/parent/file-4.txt", + + // from base image... + "/bin", + "/bin/[", + "/bin/[[", + "/bin/acpid", + "/bin/add-shell", + "/bin/addgroup", + "/bin/adduser", + "/bin/adjtimex", + "/bin/ar", + "/bin/arch", + "/bin/arp", + "/bin/arping", + "/bin/ascii", + "/bin/ash", + "/bin/awk", + "/bin/base32", + "/bin/base64", + "/bin/basename", + "/bin/bc", + "/bin/beep", + "/bin/blkdiscard", + "/bin/blkid", + "/bin/blockdev", + "/bin/bootchartd", + "/bin/brctl", + "/bin/bunzip2", + "/bin/busybox", + "/bin/bzcat", + "/bin/bzip2", + "/bin/cal", + "/bin/cat", + "/bin/chat", + "/bin/chattr", + "/bin/chgrp", + "/bin/chmod", + "/bin/chown", + "/bin/chpasswd", + "/bin/chpst", + "/bin/chroot", + "/bin/chrt", + "/bin/chvt", + "/bin/cksum", + "/bin/clear", + "/bin/cmp", + "/bin/comm", + "/bin/conspy", + "/bin/cp", + "/bin/cpio", + "/bin/crc32", + "/bin/crond", + "/bin/crontab", + "/bin/cryptpw", + "/bin/cttyhack", + "/bin/cut", + "/bin/date", + "/bin/dc", + "/bin/dd", + "/bin/deallocvt", + "/bin/delgroup", + "/bin/deluser", + "/bin/depmod", + "/bin/devmem", + "/bin/df", + "/bin/dhcprelay", + "/bin/diff", + "/bin/dirname", + "/bin/dmesg", + "/bin/dnsd", + "/bin/dnsdomainname", + "/bin/dos2unix", + "/bin/dpkg", + "/bin/dpkg-deb", + "/bin/du", + "/bin/dumpkmap", + "/bin/dumpleases", + "/bin/echo", + "/bin/ed", + "/bin/egrep", + "/bin/eject", + "/bin/env", + "/bin/envdir", + "/bin/envuidgid", + "/bin/ether-wake", + "/bin/expand", + "/bin/expr", + "/bin/factor", + "/bin/fakeidentd", + "/bin/fallocate", + "/bin/false", + "/bin/fatattr", + "/bin/fbset", + "/bin/fbsplash", + "/bin/fdflush", + "/bin/fdformat", + "/bin/fdisk", + "/bin/fgconsole", + "/bin/fgrep", + "/bin/find", + "/bin/findfs", + "/bin/flock", + "/bin/fold", + "/bin/free", + "/bin/freeramdisk", + "/bin/fsck", + "/bin/fsck.minix", + "/bin/fsfreeze", + "/bin/fstrim", + "/bin/fsync", + "/bin/ftpd", + "/bin/ftpget", + "/bin/ftpput", + "/bin/fuser", + "/bin/getconf", + "/bin/getopt", + "/bin/getty", + "/bin/grep", + "/bin/groups", + "/bin/gunzip", + "/bin/gzip", + "/bin/halt", + "/bin/hd", + "/bin/hdparm", + "/bin/head", + "/bin/hexdump", + "/bin/hexedit", + "/bin/hostid", + "/bin/hostname", + "/bin/httpd", + "/bin/hush", + "/bin/hwclock", + "/bin/i2cdetect", + "/bin/i2cdump", + "/bin/i2cget", + "/bin/i2cset", + "/bin/i2ctransfer", + "/bin/id", + "/bin/ifconfig", + "/bin/ifdown", + "/bin/ifenslave", + "/bin/ifplugd", + "/bin/ifup", + "/bin/inetd", + "/bin/init", + "/bin/insmod", + "/bin/install", + "/bin/ionice", + "/bin/iostat", + "/bin/ip", + "/bin/ipaddr", + "/bin/ipcalc", + "/bin/ipcrm", + "/bin/ipcs", + "/bin/iplink", + "/bin/ipneigh", + "/bin/iproute", + "/bin/iprule", + "/bin/iptunnel", + "/bin/kbd_mode", + "/bin/kill", + "/bin/killall", + "/bin/killall5", + "/bin/klogd", + "/bin/last", + "/bin/less", + "/bin/link", + "/bin/linux32", + "/bin/linux64", + "/bin/linuxrc", + "/bin/ln", + "/bin/loadfont", + "/bin/loadkmap", + "/bin/logger", + "/bin/login", + "/bin/logname", + "/bin/logread", + "/bin/losetup", + "/bin/lpd", + "/bin/lpq", + "/bin/lpr", + "/bin/ls", + "/bin/lsattr", + "/bin/lsmod", + "/bin/lsof", + "/bin/lspci", + "/bin/lsscsi", + "/bin/lsusb", + "/bin/lzcat", + "/bin/lzma", + "/bin/lzop", + "/bin/makedevs", + "/bin/makemime", + "/bin/man", + "/bin/md5sum", + "/bin/mdev", + "/bin/mesg", + "/bin/microcom", + "/bin/mim", + "/bin/mkdir", + "/bin/mkdosfs", + "/bin/mke2fs", + "/bin/mkfifo", + "/bin/mkfs.ext2", + "/bin/mkfs.minix", + "/bin/mkfs.vfat", + "/bin/mknod", + "/bin/mkpasswd", + "/bin/mkswap", + "/bin/mktemp", + "/bin/modinfo", + "/bin/modprobe", + "/bin/more", + "/bin/mount", + "/bin/mountpoint", + "/bin/mpstat", + "/bin/mt", + "/bin/mv", + "/bin/nameif", + "/bin/nanddump", + "/bin/nandwrite", + "/bin/nbd-client", + "/bin/nc", + "/bin/netstat", + "/bin/nice", + "/bin/nl", + "/bin/nmeter", + "/bin/nohup", + "/bin/nologin", + "/bin/nproc", + "/bin/nsenter", + "/bin/nslookup", + "/bin/ntpd", + "/bin/od", + "/bin/openvt", + "/bin/partprobe", + "/bin/passwd", + "/bin/paste", + "/bin/patch", + "/bin/pgrep", + "/bin/pidof", + "/bin/ping", + "/bin/ping6", + "/bin/pipe_progress", + "/bin/pivot_root", + "/bin/pkill", + "/bin/pmap", + "/bin/popmaildir", + "/bin/poweroff", + "/bin/powertop", + "/bin/printenv", + "/bin/printf", + "/bin/ps", + "/bin/pscan", + "/bin/pstree", + "/bin/pwd", + "/bin/pwdx", + "/bin/raidautorun", + "/bin/rdate", + "/bin/rdev", + "/bin/readahead", + "/bin/readlink", + "/bin/readprofile", + "/bin/realpath", + "/bin/reboot", + "/bin/reformime", + "/bin/remove-shell", + "/bin/renice", + "/bin/reset", + "/bin/resize", + "/bin/resume", + "/bin/rev", + "/bin/rm", + "/bin/rmdir", + "/bin/rmmod", + "/bin/route", + "/bin/rpm", + "/bin/rpm2cpio", + "/bin/rtcwake", + "/bin/run-init", + "/bin/run-parts", + "/bin/runlevel", + "/bin/runsv", + "/bin/runsvdir", + "/bin/rx", + "/bin/script", + "/bin/scriptreplay", + "/bin/sed", + "/bin/sendmail", + "/bin/seq", + "/bin/setarch", + "/bin/setconsole", + "/bin/setfattr", + "/bin/setfont", + "/bin/setkeycodes", + "/bin/setlogcons", + "/bin/setpriv", + "/bin/setserial", + "/bin/setsid", + "/bin/setuidgid", + "/bin/sh", + "/bin/sha1sum", + "/bin/sha256sum", + "/bin/sha3sum", + "/bin/sha512sum", + "/bin/showkey", + "/bin/shred", + "/bin/shuf", + "/bin/slattach", + "/bin/sleep", + "/bin/smemcap", + "/bin/softlimit", + "/bin/sort", + "/bin/split", + "/bin/ssl_client", + "/bin/start-stop-daemon", + "/bin/stat", + "/bin/strings", + "/bin/stty", + "/bin/su", + "/bin/sulogin", + "/bin/sum", + "/bin/sv", + "/bin/svc", + "/bin/svlogd", + "/bin/svok", + "/bin/swapoff", + "/bin/swapon", + "/bin/switch_root", + "/bin/sync", + "/bin/sysctl", + "/bin/syslogd", + "/bin/tac", + "/bin/tail", + "/bin/tar", + "/bin/taskset", + "/bin/tc", + "/bin/tcpsvd", + "/bin/tee", + "/bin/telnet", + "/bin/telnetd", + "/bin/test", + "/bin/tftp", + "/bin/tftpd", + "/bin/time", + "/bin/timeout", + "/bin/top", + "/bin/touch", + "/bin/tr", + "/bin/traceroute", + "/bin/traceroute6", + "/bin/true", + "/bin/truncate", + "/bin/ts", + "/bin/tty", + "/bin/ttysize", + "/bin/tunctl", + "/bin/ubiattach", + "/bin/ubidetach", + "/bin/ubimkvol", + "/bin/ubirename", + "/bin/ubirmvol", + "/bin/ubirsvol", + "/bin/ubiupdatevol", + "/bin/udhcpc", + "/bin/udhcpc6", + "/bin/udhcpd", + "/bin/udpsvd", + "/bin/uevent", + "/bin/umount", + "/bin/uname", + "/bin/unexpand", + "/bin/uniq", + "/bin/unix2dos", + "/bin/unlink", + "/bin/unlzma", + "/bin/unshare", + "/bin/unxz", + "/bin/unzip", + "/bin/uptime", + "/bin/users", + "/bin/usleep", + "/bin/uudecode", + "/bin/uuencode", + "/bin/vconfig", + "/bin/vi", + "/bin/vlock", + "/bin/volname", + "/bin/w", + "/bin/wall", + "/bin/watch", + "/bin/watchdog", + "/bin/wc", + "/bin/wget", + "/bin/which", + "/bin/who", + "/bin/whoami", + "/bin/whois", + "/bin/xargs", + "/bin/xxd", + "/bin/xz", + "/bin/xzcat", + "/bin/yes", + "/bin/zcat", + "/bin/zcip", + "/dev", + "/etc", + "/etc/group", + "/etc/localtime", + "/etc/network", + "/etc/network/if-down.d", + "/etc/network/if-post-down.d", + "/etc/network/if-pre-up.d", + "/etc/network/if-up.d", + "/etc/passwd", + "/etc/shadow", + "/home", + "/proc", + "/root", + "/sys", + "/tmp", + "/usr", + "/usr/sbin", + "/var", + "/var/spool", + "/var/spool/mail", + "/var/www", + } + + pathsList := paths.List() + sort.Strings(pathsList) + + assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List())) +} diff --git a/syft/source/source.go b/syft/source/source.go index ed96dd11061..76b5470638e 100644 --- a/syft/source/source.go +++ b/syft/source/source.go @@ -518,7 +518,7 @@ func getImageExclusionFunction(exclusions []string) func(string) bool { } } -func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathFilterFn, error) { +func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathIndexVisitor, error) { if len(exclusions) == 0 { return nil, nil } @@ -551,20 +551,23 @@ func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathFil return nil, fmt.Errorf("invalid exclusion pattern(s): '%s' (must start with one of: './', '*/', or '**/')", strings.Join(errors, "', '")) } - return []pathFilterFn{ - func(path string, _ os.FileInfo) bool { + return []pathIndexVisitor{ + func(path string, info os.FileInfo, _ error) error { for _, exclusion := range exclusions { // this is required to handle Windows filepaths path = filepath.ToSlash(path) matches, err := doublestar.Match(exclusion, path) if err != nil { - return false + return nil } if matches { - return true + if info != nil && info.IsDir() { + return filepath.SkipDir + } + return errSkipPath } } - return false + return nil }, }, nil } diff --git a/syft/source/source_test.go b/syft/source/source_test.go index c172a3572c5..971dfde9854 100644 --- a/syft/source/source_test.go +++ b/syft/source/source_test.go @@ -5,15 +5,19 @@ package source import ( "io" + "io/fs" "io/ioutil" "os" "os/exec" "path" "path/filepath" + "sort" "strings" "syscall" "testing" + "time" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -455,7 +459,7 @@ func TestDirectoryExclusions(t *testing.T) { desc string input string glob string - expected int + expected []string exclusions []string err bool }{ @@ -463,86 +467,128 @@ func TestDirectoryExclusions(t *testing.T) { input: "test-fixtures/system_paths", desc: "exclude everything", glob: "**", - expected: 0, + expected: nil, exclusions: []string{"**/*"}, }, { - input: "test-fixtures/image-simple", - desc: "a single path excluded", - glob: "**", - expected: 3, + input: "test-fixtures/image-simple", + desc: "a single path excluded", + glob: "**", + expected: []string{ + "Dockerfile", + "file-1.txt", + "file-2.txt", + }, exclusions: []string{"**/target/**"}, }, { - input: "test-fixtures/image-simple", - desc: "exclude explicit directory relative to the root", - glob: "**", - expected: 3, + input: "test-fixtures/image-simple", + desc: "exclude explicit directory relative to the root", + glob: "**", + expected: []string{ + "Dockerfile", + "file-1.txt", + "file-2.txt", + //"target/really/nested/file-3.txt", // explicitly skipped + }, exclusions: []string{"./target"}, }, { - input: "test-fixtures/image-simple", - desc: "exclude explicit file relative to the root", - glob: "**", - expected: 3, + input: "test-fixtures/image-simple", + desc: "exclude explicit file relative to the root", + glob: "**", + expected: []string{ + "Dockerfile", + //"file-1.txt", // explicitly skipped + "file-2.txt", + "target/really/nested/file-3.txt", + }, exclusions: []string{"./file-1.txt"}, }, { - input: "test-fixtures/image-simple", - desc: "exclude wildcard relative to the root", - glob: "**", - expected: 2, + input: "test-fixtures/image-simple", + desc: "exclude wildcard relative to the root", + glob: "**", + expected: []string{ + "Dockerfile", + //"file-1.txt", // explicitly skipped + //"file-2.txt", // explicitly skipped + "target/really/nested/file-3.txt", + }, exclusions: []string{"./*.txt"}, }, { - input: "test-fixtures/image-simple", - desc: "exclude files deeper", - glob: "**", - expected: 3, + input: "test-fixtures/image-simple", + desc: "exclude files deeper", + glob: "**", + expected: []string{ + "Dockerfile", + "file-1.txt", + "file-2.txt", + //"target/really/nested/file-3.txt", // explicitly skipped + }, exclusions: []string{"**/really/**"}, }, { - input: "test-fixtures/image-simple", - desc: "files excluded with extension", - glob: "**", - expected: 1, + input: "test-fixtures/image-simple", + desc: "files excluded with extension", + glob: "**", + expected: []string{ + "Dockerfile", + //"file-1.txt", // explicitly skipped + //"file-2.txt", // explicitly skipped + //"target/really/nested/file-3.txt", // explicitly skipped + }, exclusions: []string{"**/*.txt"}, }, { - input: "test-fixtures/image-simple", - desc: "keep files with different extensions", - glob: "**", - expected: 4, + input: "test-fixtures/image-simple", + desc: "keep files with different extensions", + glob: "**", + expected: []string{ + "Dockerfile", + "file-1.txt", + "file-2.txt", + "target/really/nested/file-3.txt", + }, exclusions: []string{"**/target/**/*.jar"}, }, { - input: "test-fixtures/path-detected", - desc: "file directly excluded", - glob: "**", - expected: 1, + input: "test-fixtures/path-detected", + desc: "file directly excluded", + glob: "**", + expected: []string{ + ".vimrc", + }, exclusions: []string{"**/empty"}, }, { - input: "test-fixtures/path-detected", - desc: "pattern error containing **/", - glob: "**", - expected: 1, + input: "test-fixtures/path-detected", + desc: "pattern error containing **/", + glob: "**", + expected: []string{ + ".vimrc", + }, exclusions: []string{"/**/empty"}, err: true, }, { - input: "test-fixtures/path-detected", - desc: "pattern error incorrect start", - glob: "**", - expected: 1, + input: "test-fixtures/path-detected", + desc: "pattern error incorrect start", + glob: "**", + expected: []string{ + ".vimrc", + }, exclusions: []string{"empty"}, err: true, }, { - input: "test-fixtures/path-detected", - desc: "pattern error starting with /", - glob: "**", - expected: 1, + input: "test-fixtures/path-detected", + desc: "pattern error starting with /", + glob: "**", + expected: []string{ + ".vimrc", + }, exclusions: []string{"/empty"}, err: true, }, @@ -570,13 +616,19 @@ func TestDirectoryExclusions(t *testing.T) { if err != nil { t.Errorf("could not get resolver error: %+v", err) } - contents, err := resolver.FilesByGlob(test.glob) + locations, err := resolver.FilesByGlob(test.glob) if err != nil { t.Errorf("could not get files by glob: %s+v", err) } - if len(contents) != test.expected { - t.Errorf("wrong number of files after exclusions (%s): %d != %d", test.glob, len(contents), test.expected) + var actual []string + for _, l := range locations { + actual = append(actual, l.RealPath) } + + sort.Strings(test.expected) + sort.Strings(actual) + + assert.Equal(t, test.expected, actual, "diff \n"+cmp.Diff(test.expected, actual)) }) } } @@ -667,59 +719,118 @@ func TestImageExclusions(t *testing.T) { } } +type dummyInfo struct { + isDir bool +} + +func (d dummyInfo) Name() string { + //TODO implement me + panic("implement me") +} + +func (d dummyInfo) Size() int64 { + //TODO implement me + panic("implement me") +} + +func (d dummyInfo) Mode() fs.FileMode { + //TODO implement me + panic("implement me") +} + +func (d dummyInfo) ModTime() time.Time { + //TODO implement me + panic("implement me") +} + +func (d dummyInfo) IsDir() bool { + return d.isDir +} + +func (d dummyInfo) Sys() any { + //TODO implement me + panic("implement me") +} + func Test_crossPlatformExclusions(t *testing.T) { testCases := []struct { - desc string - root string - path string - exclude string - match bool + desc string + root string + path string + finfo os.FileInfo + exclude string + walkHint error }{ { - desc: "linux doublestar", - root: "/usr", - path: "/usr/var/lib/etc.txt", - exclude: "**/*.txt", - match: true, + desc: "directory exclusion", + root: "/", + path: "/usr/var/lib", + exclude: "**/var/lib", + finfo: dummyInfo{isDir: true}, + walkHint: fs.SkipDir, + }, + { + desc: "no file info", + root: "/", + path: "/usr/var/lib", + exclude: "**/var/lib", + walkHint: errSkipPath, + }, + // linux specific tests... + { + desc: "linux doublestar", + root: "/usr", + path: "/usr/var/lib/etc.txt", + exclude: "**/*.txt", + finfo: dummyInfo{isDir: false}, + walkHint: errSkipPath, }, { desc: "linux relative", root: "/usr/var/lib", path: "/usr/var/lib/etc.txt", exclude: "./*.txt", - match: true, + finfo: dummyInfo{isDir: false}, + + walkHint: errSkipPath, }, { - desc: "linux one level", - root: "/usr", - path: "/usr/var/lib/etc.txt", - exclude: "*/*.txt", - match: false, + desc: "linux one level", + root: "/usr", + path: "/usr/var/lib/etc.txt", + exclude: "*/*.txt", + finfo: dummyInfo{isDir: false}, + walkHint: nil, }, // NOTE: since these tests will run in linux and macOS, the windows paths will be // considered relative if they do not start with a forward slash and paths with backslashes // won't be modified by the filepath.ToSlash call, so these are emulating the result of // filepath.ToSlash usage + + // windows specific tests... { - desc: "windows doublestar", - root: "/C:/User/stuff", - path: "/C:/User/stuff/thing.txt", - exclude: "**/*.txt", - match: true, + desc: "windows doublestar", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "**/*.txt", + finfo: dummyInfo{isDir: false}, + walkHint: errSkipPath, }, { - desc: "windows relative", - root: "/C:/User/stuff", - path: "/C:/User/stuff/thing.txt", - exclude: "./*.txt", - match: true, + desc: "windows relative", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "./*.txt", + finfo: dummyInfo{isDir: false}, + walkHint: errSkipPath, }, { - desc: "windows one level", - root: "/C:/User/stuff", - path: "/C:/User/stuff/thing.txt", - exclude: "*/*.txt", - match: false, + desc: "windows one level", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "*/*.txt", + finfo: dummyInfo{isDir: false}, + walkHint: nil, }, } @@ -729,8 +840,8 @@ func Test_crossPlatformExclusions(t *testing.T) { require.NoError(t, err) for _, f := range fns { - result := f(test.path, nil) - require.Equal(t, test.match, result) + result := f(test.path, test.finfo, nil) + require.Equal(t, test.walkHint, result) } }) } diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/before-path b/syft/source/test-fixtures/symlinks-prune-indexing/before-path new file mode 120000 index 00000000000..b9dd30f5dc6 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/before-path @@ -0,0 +1 @@ +path \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/c-file.txt b/syft/source/test-fixtures/symlinks-prune-indexing/c-file.txt new file mode 120000 index 00000000000..d3e5e99ca68 --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/c-file.txt @@ -0,0 +1 @@ +before-path/file.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/c-path b/syft/source/test-fixtures/symlinks-prune-indexing/c-path new file mode 120000 index 00000000000..065736a4e5c --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/c-path @@ -0,0 +1 @@ +before-path \ No newline at end of file diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/path/1/2/3/4/dont-index-me-twice.txt b/syft/source/test-fixtures/symlinks-prune-indexing/path/1/2/3/4/dont-index-me-twice.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/path/1/2/3/4/dont-index-me-twice.txt @@ -0,0 +1 @@ +bogus diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/path/5/6/7/8/dont-index-me-twice-either.txt b/syft/source/test-fixtures/symlinks-prune-indexing/path/5/6/7/8/dont-index-me-twice-either.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/path/5/6/7/8/dont-index-me-twice-either.txt @@ -0,0 +1 @@ +bogus diff --git a/syft/source/test-fixtures/symlinks-prune-indexing/path/file.txt b/syft/source/test-fixtures/symlinks-prune-indexing/path/file.txt new file mode 100644 index 00000000000..5ffba7b57dc --- /dev/null +++ b/syft/source/test-fixtures/symlinks-prune-indexing/path/file.txt @@ -0,0 +1 @@ +bogus From caef6c64ce049aa4bc74e04d4868003dcfd00841 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 3 Feb 2023 12:41:14 -0500 Subject: [PATCH 40/54] move format testutils to internal package Signed-off-by: Alex Goodman --- go.mod | 4 +- go.sum | 823 +----------------- syft/formats/cyclonedxjson/encoder_test.go | 2 +- syft/formats/cyclonedxxml/encoder_test.go | 2 +- .../{common => internal}/testutils/utils.go | 0 syft/formats/spdxjson/encoder_test.go | 2 +- syft/formats/spdxtagvalue/encoder_test.go | 2 +- syft/formats/syftjson/decoder_test.go | 2 +- syft/formats/syftjson/encoder_test.go | 2 +- syft/formats/table/encoder_test.go | 2 +- syft/formats/template/encoder_test.go | 2 +- syft/formats/text/encoder_test.go | 2 +- 12 files changed, 17 insertions(+), 828 deletions(-) rename syft/formats/{common => internal}/testutils/utils.go (100%) diff --git a/go.mod b/go.mod index 80d743e3618..5ae41a5dd5b 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/sassoftware/go-rpmutils v0.2.0 github.com/vbatts/go-mtree v0.5.2 - golang.org/x/exp v0.0.0-20220823124025-807a23277127 + golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b gopkg.in/yaml.v3 v3.0.1 ) @@ -127,7 +127,7 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/tools v0.2.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/grpc v1.52.0 // indirect diff --git a/go.sum b/go.sum index 7858078d8c7..d85d3dc0370 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -39,7 +37,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -52,25 +49,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -79,48 +59,15 @@ github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce/go.mod h1 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim v0.9.5/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= @@ -131,9 +78,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 h1:imgMA0gN0TZx7PSa/pdWqXadBvrz8WsN6zySzCe4XX0= github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8/go.mod h1:+gPap4jha079qzRTUaehv+UZ6sSdaNwkH0D3b6zhTuk= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU= @@ -150,76 +94,30 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= -github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= -github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg= -github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= -github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04/go.mod h1:Z+bXnIbhKJYSvxNwsNnwde7pDKxuqlEZCbUBoTwAqf0= github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -230,199 +128,38 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= github.com/containerd/containerd v1.6.12 h1:kJ9b3mOFKf8yqo05Ob+tMoxvt1pbVWhnB0re9Y+k+8c= github.com/containerd/containerd v1.6.12/go.mod h1:K4Bw7gjgh4TnkmQY+py/PYQGp4e7xgnHAeg87VeWb3A= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUtk4cauMe34= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0= github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4= github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.23+incompatible h1:1ZQUUYAdh+oylOT85aA2ZcfRp22jmLhoaEcVEfK8dyA= github.com/docker/docker v20.10.23+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -432,11 +169,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk= github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ= github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4= @@ -445,81 +179,31 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -557,7 +241,6 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -573,13 +256,9 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k= github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -596,49 +275,25 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -650,35 +305,28 @@ github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39E github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= @@ -686,56 +334,31 @@ github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0 github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So= github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -744,34 +367,20 @@ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce h1:/w0hAcauo/FBVaBvNMQdPZgKjTu5Ip3jvGIM1+VUE7o= github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -780,7 +389,6 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -789,19 +397,12 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= @@ -811,42 +412,23 @@ github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5/go.mod h1:v github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -855,99 +437,27 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -957,48 +467,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= @@ -1006,22 +490,13 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE= github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= -github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -1029,70 +504,39 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.4.0 h1:jdhnW8zYelURCbYTphiviFKZkWu51in0E4A1KT2csP0= github.com/spdx/tools-golang v0.4.0/go.mod h1:VHzvNsKAfAGqs4ZvwRL+7a0dNsL20s7lGui4K9C0xQM= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1111,26 +555,13 @@ github.com/sylabs/sif/v2 v2.8.1 h1:whr4Vz12RXfLnYyVGHoD/rD/hbF2g9OW7BJHa+WIqW8= github.com/sylabs/sif/v2 v2.8.1/go.mod h1:LQOdYXC9a8i7BleTKRw9lohi0rTbXkJOeS9u0ebvgyM= github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ= github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/go-mtree v0.5.2 h1:d8SAbLJiR1cR3pe1J+FBaalRkCQw95gP12/P+a9PUcA= github.com/vbatts/go-mtree v0.5.2/go.mod h1:e0NDJ+bT3jG7ZINeB9HR5AxTvjskCsOR54+9KoaXyDc= @@ -1138,63 +569,32 @@ github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlI github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vifraa/gopom v0.2.1 h1:MYVMAMyiGzXPPy10EwojzKIL670kl5Zbae+o3fFvQEM= github.com/vifraa/gopom v0.2.1/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/wagoodman/go-partybus v0.0.0-20200526224238-eb215533f07d/go.mod h1:JPirS5jde/CF5qIjcK4WX+eQmKXdPc6vcZkJ/P0hfPw= github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5 h1:phTLPgMRDYTizrBSKsNSOa2zthoC2KsJsaY/8sg3rD8= github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5/go.mod h1:JPirS5jde/CF5qIjcK4WX+eQmKXdPc6vcZkJ/P0hfPw= -github.com/wagoodman/go-progress v0.0.0-20200621122631-1a2120f0695a/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240 h1:r6BlIP7CVZtMlxUQhT40h1IE1TzEgKVqwmsVGuscvdk= github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLAHYlJzTw7JKnWxdV/WXpug2X0quEzRnY= github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1202,61 +602,27 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1269,8 +635,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220823124025-807a23277127 h1:S4NrSKDfihhl3+4jSTgwoIevKxX9p7Iv9x++OEIptDo= -golang.org/x/exp v0.0.0-20220823124025-807a23277127/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE= +golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1284,6 +650,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1302,29 +669,20 @@ golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.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= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1335,16 +693,13 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1352,17 +707,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= @@ -1378,7 +725,6 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1402,9 +748,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1413,41 +757,22 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1455,66 +780,39 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1525,7 +823,6 @@ golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1543,40 +840,28 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1596,22 +881,18 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1621,15 +902,15 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1651,7 +932,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1670,13 +950,11 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1685,7 +963,6 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1694,21 +971,17 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1737,23 +1010,17 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1777,9 +1044,6 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1796,35 +1060,17 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1835,15 +1081,11 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1851,50 +1093,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/cri-api v0.25.0/go.mod h1:J1rAyQkSJ2Q6I+aBMOVgg2/cbbebso6FNa0UagiR0kc= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo= @@ -1933,13 +1131,4 @@ modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/syft/formats/cyclonedxjson/encoder_test.go b/syft/formats/cyclonedxjson/encoder_test.go index 005d44b9591..4649ad31701 100644 --- a/syft/formats/cyclonedxjson/encoder_test.go +++ b/syft/formats/cyclonedxjson/encoder_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden files for cyclone-dx encoders") diff --git a/syft/formats/cyclonedxxml/encoder_test.go b/syft/formats/cyclonedxxml/encoder_test.go index c7ecd80650d..1070f44cad5 100644 --- a/syft/formats/cyclonedxxml/encoder_test.go +++ b/syft/formats/cyclonedxxml/encoder_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateCycloneDx = flag.Bool("update-cyclonedx", false, "update the *.golden files for cyclone-dx encoders") diff --git a/syft/formats/common/testutils/utils.go b/syft/formats/internal/testutils/utils.go similarity index 100% rename from syft/formats/common/testutils/utils.go rename to syft/formats/internal/testutils/utils.go diff --git a/syft/formats/spdxjson/encoder_test.go b/syft/formats/spdxjson/encoder_test.go index 90f1342841b..f33a87708b3 100644 --- a/syft/formats/spdxjson/encoder_test.go +++ b/syft/formats/spdxjson/encoder_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateSpdxJson = flag.Bool("update-spdx-json", false, "update the *.golden files for spdx-json encoders") diff --git a/syft/formats/spdxtagvalue/encoder_test.go b/syft/formats/spdxtagvalue/encoder_test.go index c2b8e4e8563..116f9ae8fbe 100644 --- a/syft/formats/spdxtagvalue/encoder_test.go +++ b/syft/formats/spdxtagvalue/encoder_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" diff --git a/syft/formats/syftjson/decoder_test.go b/syft/formats/syftjson/decoder_test.go index ed9043b8b75..06d41711dad 100644 --- a/syft/formats/syftjson/decoder_test.go +++ b/syft/formats/syftjson/decoder_test.go @@ -8,7 +8,7 @@ import ( "github.com/go-test/deep" "github.com/stretchr/testify/assert" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) func TestEncodeDecodeCycle(t *testing.T) { diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index b2dc63e0ebf..433590e2420 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -9,7 +9,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" diff --git a/syft/formats/table/encoder_test.go b/syft/formats/table/encoder_test.go index 830f28bb3b5..44e9f473069 100644 --- a/syft/formats/table/encoder_test.go +++ b/syft/formats/table/encoder_test.go @@ -6,7 +6,7 @@ import ( "github.com/go-test/deep" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateTableGoldenFiles = flag.Bool("update-table", false, "update the *.golden files for table format") diff --git a/syft/formats/template/encoder_test.go b/syft/formats/template/encoder_test.go index e41d3109c92..34eb6c79a5a 100644 --- a/syft/formats/template/encoder_test.go +++ b/syft/formats/template/encoder_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateTmpl = flag.Bool("update-tmpl", false, "update the *.golden files for json encoders") diff --git a/syft/formats/text/encoder_test.go b/syft/formats/text/encoder_test.go index a50d90a1e85..7b5e9f4727f 100644 --- a/syft/formats/text/encoder_test.go +++ b/syft/formats/text/encoder_test.go @@ -4,7 +4,7 @@ import ( "flag" "testing" - "github.com/anchore/syft/syft/formats/common/testutils" + "github.com/anchore/syft/syft/formats/internal/testutils" ) var updateTextEncoderGoldenFiles = flag.Bool("update-text", false, "update the *.golden files for text encoder") From 868d89b35b90eea663c87c8deaa65e2db1d7f358 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 3 Feb 2023 13:16:39 -0500 Subject: [PATCH 41/54] update syft json to account for file type string normalization Signed-off-by: Alex Goodman --- .../snapshot/TestDirectoryEncoder.golden | 4 ++-- .../TestEncodeFullJSONDocument.golden | 8 +++---- .../snapshot/TestImageEncoder.golden | 20 +++++++++--------- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index e8526917755..6a051331a0e 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -89,7 +89,7 @@ } }, "schema": { - "version": "6.1.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.1.0.json" + "version": "6.2.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 0b9a4036ac0..3b638f94106 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -78,7 +78,7 @@ }, "metadata": { "mode": 775, - "type": "directory", + "type": "Directory", "userID": 0, "groupID": 0, "mimeType": "" @@ -91,7 +91,7 @@ }, "metadata": { "mode": 775, - "type": "regularFile", + "type": "RegularFile", "userID": 0, "groupID": 0, "mimeType": "" @@ -111,7 +111,7 @@ }, "metadata": { "mode": 775, - "type": "symbolicLink", + "type": "SymbolicLink", "linkDestination": "/c", "userID": 0, "groupID": 0, @@ -125,7 +125,7 @@ }, "metadata": { "mode": 644, - "type": "regularFile", + "type": "RegularFile", "userID": 1, "groupID": 2, "mimeType": "" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 362f9886fad..a72adb10c60 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:6afd1cb55939d87ba4c298907d0a53059bb3742c2d65314643e2464071cf0a2d" + "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" } ], "licenses": [ @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:657997cff9a836139186239bdfe77250239a700d0ed97d57e101c295e8244319" + "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" } ], "licenses": [], @@ -64,11 +64,11 @@ ], "artifactRelationships": [], "source": { - "id": "c85f7ae1b0ac38342c1cf1a6f7ea2b4b1ddc49cd1b24219ebd05dc10b3303491", + "id": "1a678f111c8ddc66fd82687bb024e0dd6af61314404937a80e810c0cf317b796", "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:b5c0bfa8bcf70c75d92ebebbf76af667906d56e6fad50c37e7f93df824a64b79", + "imageID": "sha256:3c51b06feb0cda8ee62d0e3755ef2a8496a6b71f8a55b245f07f31c4bb813d31", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -78,17 +78,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:6afd1cb55939d87ba4c298907d0a53059bb3742c2d65314643e2464071cf0a2d", + "digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:657997cff9a836139186239bdfe77250239a700d0ed97d57e101c295e8244319", + "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjpiNWMwYmZhOGJjZjcwYzc1ZDkyZWJlYmJmNzZhZjY2NzkwNmQ1NmU2ZmFkNTBjMzdlN2Y5M2RmODI0YTY0Yjc5In0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo2YWZkMWNiNTU5MzlkODdiYTRjMjk4OTA3ZDBhNTMwNTliYjM3NDJjMmQ2NTMxNDY0M2UyNDY0MDcxY2YwYTJkIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjY1Nzk5N2NmZjlhODM2MTM5MTg2MjM5YmRmZTc3MjUwMjM5YTcwMGQwZWQ5N2Q1N2UxMDFjMjk1ZTgyNDQzMTkifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMjVUMTY6MjI6MDguODkxMzY0Mjc4WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTI1VDE2OjIyOjA4Ljc2MzMzMDMyM1oiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMjVUMTY6MjI6MDguODkxMzY0Mjc4WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6NmFmZDFjYjU1OTM5ZDg3YmE0YzI5ODkwN2QwYTUzMDU5YmIzNzQyYzJkNjUzMTQ2NDNlMjQ2NDA3MWNmMGEyZCIsInNoYTI1Njo2NTc5OTdjZmY5YTgzNjEzOTE4NjIzOWJkZmU3NzI1MDIzOWE3MDBkMGVkOTdkNTdlMTAxYzI5NWU4MjQ0MzE5Il19fQ==", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjozYzUxYjA2ZmViMGNkYThlZTYyZDBlMzc1NWVmMmE4NDk2YTZiNzFmOGE1NWIyNDVmMDdmMzFjNGJiODEzZDMxIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTAxVDIwOjA5OjIyLjQ4Nzg5NTUxOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmI2YmVlY2I3NWIzOWY0YmI4MTNkYmYxNzdlNTAxZWRkNWRkYjNlNjliYjQ1Y2VkZWI3OGM2NzZlZTFiN2E1OSIsInNoYTI1NjozMTliNTg4Y2U2NDI1M2E4N2I1MzNjOGVkMDFjZjAwMjVlMGVhYzk4ZTdiNTE2ZTEyNTMyOTU3ZTEyNDRmZGVjIl19fQ==", "repoDigests": [], "architecture": "", "os": "" @@ -112,7 +112,7 @@ } }, "schema": { - "version": "6.1.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.1.0.json" + "version": "6.2.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 0a4b4d25667754eaccaf5fe1dabb4e5a5b16005e..11a1958c8935029370bb6a48ad18899403776949 100644 GIT binary patch literal 15360 zcmeHOZExE)5ccQ&3Qzl**nFpCU>~}sKnoO0(Pka6qA2)sOtjjPA<0F9Apdm%%?PJlu^wTR3f}9JyT$INc2+m0LY2Bo z+kCjL@($GhDlwJ2$Qy9Ytb$~en2A>+IZ9kvg#0`iT_ul-cgg5g^6bIcA**Z}Z~(B(HF&Ar5m|{4vYNJ{c9W@%S2tt-v&Z z<%AK&j!Ak<=oup;l8qP}NRkOgHD&)`*NePv%IXqzyV}xCq&m%{q*>c-Il>!@J(C9~ zC;gC*;g}AZ_f3CTmlIei|JJvyc5m}$Zjs3ZiUxaqxH<5pdka}WC)5Q(m;?gGWhr0tP6t%jdrI6?%$4P|rP|4?pM&h_=5YAJNDFF!+e z=O#b-`rq0AFCRUB@#OT+yZo?a|3~-pb^kM>h>ZLHT{hiS9UXLze0HTul=BrV(0Tsv zApVzZWBq4Da24nOAzp{8ByWT5uCD>cd{a{`qhswgO%cpt?3XzyKzi#9rAlcoiC`%g zUKpNQNsP1tFy!MF(O;Tr$E;^4>mDcoaY|$I0;)H+h~;rvH-(wBfi4O=DJR(Aw)|HO zE&9Nk2Tfz##f!!xI2i>qu3_a~Im1wY^Cnc-b$RowK?Je9KAl3bE@xgkEgUMbY%|AE zEzCJe9!{2s=O}qHG3QG~UzX$91TYj|G+M(-sO_VosV=8w-a?Q?i#70clsq*S#>?(4 zl)6E1->^M)ge&iF&=!2&*P@?*$}3RoyMrrTC;o7bBnK^r^2y(p_)mNk|Fby_6#1V| zT6A|6H-yKWXV{(GRY_a?k3>Mb{}ZMtG5}Qf2&I}M{yRuy`jg|oIoZYf?JkPDcm3 z?))!NY%~7@`QP~c=OFLQlK<`c8X%c=%h}RQ6HMD^GdBokZnS^%#EZz(ejsi)nA)#M zAa-^9-uOQf`JY2905|;KJOOGyBW~ya)JOaurVB>?Z$Iu>liFYw-8=)Xe_`qy8gQ*i zEJO=L3q%V<3;ci&PqIn?_abld!Ax4Gf-TyUKK3XcKB_mIfGV=884#+q6MM_z9S3#2P@x!egFUf literal 15360 zcmeHOZExE)5YFfQ3QzkQ+kB^JU>~xkKnoO0(Pka6peQK5BwB3AkmRC4kpI4u?Krl( zL`@_c$wtGl`FcD)r<3k@Cy5P`Tr=aC@SgZ2HYUbI3u`Q6PFw4}v;qpyELBXolU90< zluLN5(D0!7gAju?_yJmcw*Twa1I#GGk{}__0U}5udC=p{q}z{}m6a}lrM8r|H`kWl zp61K8^8fVu!^gKLzdyt-!Uze*w&i|X2ju9M{IO*Gpx0U^4>5Jy_dl0E9_$6>TeQRf zGee-Y{{LO(S;L+h^dEDwegDT?ilF}uuy)dY9i=uJMQtyG=rEe@DZywiePNf;F(A;#H+Vaqhpv4-b9^N2V{jR#t$aJq6eV+0Y>M{%F0SQ3 zvxNfTfIvVXAP^9ERtO~Apv3DqFd;S+`S>*-z z;Ahtj)1;u3B1-7|z+kb$mjq@;N5{v#xe0`8^S3u#nZt|vFTJF8urJdJST*@%vN++m zZ5-I#T)LtrF!+k1o5LnXHcu}8m65p}zQ)L+{zyi^BVKb#52GT_D-T}kbyQtVYu9z| zwUw|bn|l8xwa|AKl^2?DF#^T6*d&}Qs%$J12o4bip&n_G<`i+2BvdkDh!vb-CKz=D zei68m9%*8M>MbQMm6FDLrDG~ERX7%es>FIHCE*Cla5jW2a#l&prNaorTHL|yg;A`c z&o?j5q27zucDG2|!@3>C|9Bn$3Gu(~y}&pMdl(bh!!xZ4C<5fIvVX zAP^Av&ml0;S?XO`{YV_=she+l{&P;(a={`0AM!tN$p3jN+>e?Ev6ENJyveAOSRiH?>0~-~qnJbPr{NgIabhiz%u5|Br<8-& zMihb=q%cf5NxV0Tb9gbt!0RYD_Q(-pcv5Hoy{mK3=F4^G&cHsp%Z-CT#^;|NC6|2- zE Date: Sat, 4 Feb 2023 20:40:24 -0500 Subject: [PATCH 42/54] split up directory resolver from indexing Signed-off-by: Alex Goodman --- syft/source/directory_indexer.go | 361 +++++++++++++++++++++++ syft/source/directory_indexer_test.go | 328 +++++++++++++++++++++ syft/source/directory_resolver.go | 377 ++----------------------- syft/source/directory_resolver_test.go | 311 +------------------- 4 files changed, 717 insertions(+), 660 deletions(-) create mode 100644 syft/source/directory_indexer.go create mode 100644 syft/source/directory_indexer_test.go diff --git a/syft/source/directory_indexer.go b/syft/source/directory_indexer.go new file mode 100644 index 00000000000..d9c728c1cca --- /dev/null +++ b/syft/source/directory_indexer.go @@ -0,0 +1,361 @@ +package source + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path" + "path/filepath" + "runtime" + + "github.com/wagoodman/go-partybus" + "github.com/wagoodman/go-progress" + + "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/stereoscope/pkg/filetree" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/event" +) + +type pathIndexVisitor func(string, os.FileInfo, error) error + +type directoryIndexer struct { + path string + base string + pathIndexVisitors []pathIndexVisitor + errPaths map[string]error + tree filetree.ReadWriter + index filetree.Index +} + +func newDirectoryIndexer(path, base string, visitors ...pathIndexVisitor) *directoryIndexer { + i := &directoryIndexer{ + path: path, + base: base, + tree: filetree.New(), + index: filetree.NewIndex(), + pathIndexVisitors: append([]pathIndexVisitor{requireFileInfo, disallowByFileType, disallowUnixSystemRuntimePath}, visitors...), + errPaths: make(map[string]error), + } + + // these additional stateful visitors should be the first thing considered when walking / indexing + i.pathIndexVisitors = append([]pathIndexVisitor{i.disallowRevisitingVisitor, i.disallowFileAccessErr}, i.pathIndexVisitors...) + + return i +} + +func (r *directoryIndexer) build() (filetree.Reader, filetree.IndexReader, error) { + return r.tree, r.index, indexAllRoots(r.path, r.indexTree) +} + +func indexAllRoots(root string, indexer func(string, *progress.Stage) ([]string, error)) error { + // why account for multiple roots? To cover cases when there is a symlink that references above the root path, + // in which case we need to additionally index where the link resolves to. it's for this reason why the filetree + // must be relative to the root of the filesystem (and not just relative to the given path). + pathsToIndex := []string{root} + fullPathsMap := map[string]struct{}{} + + stager, prog := indexingProgress(root) + defer prog.SetCompleted() +loop: + for { + var currentPath string + switch len(pathsToIndex) { + case 0: + break loop + case 1: + currentPath, pathsToIndex = pathsToIndex[0], nil + default: + currentPath, pathsToIndex = pathsToIndex[0], pathsToIndex[1:] + } + + additionalRoots, err := indexer(currentPath, stager) + if err != nil { + return fmt.Errorf("unable to index filesystem path=%q: %w", currentPath, err) + } + + for _, newRoot := range additionalRoots { + if _, ok := fullPathsMap[newRoot]; !ok { + fullPathsMap[newRoot] = struct{}{} + pathsToIndex = append(pathsToIndex, newRoot) + } + } + } + + return nil +} + +func (r *directoryIndexer) indexTree(root string, stager *progress.Stage) ([]string, error) { + log.WithFields("path", root).Trace("indexing filetree") + + var roots []string + var err error + + root, err = filepath.Abs(root) + if err != nil { + return nil, err + } + + // we want to be able to index single files with the directory resolver. However, we should also allow for attempting + // to index paths that do not exist (that is, a root that does not exist is not an error case that should stop indexing). + // For this reason we look for an opportunity to discover if the given root is a file, and if so add a single root, + // but continue forth with index regardless if the given root path exists or not. + fi, err := os.Stat(root) + if err != nil && fi != nil && !fi.IsDir() { + // note: we want to index the path regardless of an error stat-ing the path + newRoot, _ := r.indexPath(root, fi, nil) + if newRoot != "" { + roots = append(roots, newRoot) + } + return roots, nil + } + + err = filepath.Walk(root, + func(path string, info os.FileInfo, err error) error { + stager.Current = path + + newRoot, err := r.indexPath(path, info, err) + + if err != nil { + return err + } + + if newRoot != "" { + roots = append(roots, newRoot) + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("unable to index root=%q: %w", root, err) + } + + return roots, nil +} + +func (r *directoryIndexer) indexPath(path string, info os.FileInfo, err error) (string, error) { + // ignore any path which a filter function returns true + for _, filterFn := range r.pathIndexVisitors { + if filterFn == nil { + continue + } + + if filterErr := filterFn(path, info, err); filterErr != nil { + if errors.Is(filterErr, fs.SkipDir) { + // signal to walk() to skip this directory entirely (even if we're processing a file) + return "", filterErr + } + // skip this path but don't affect walk() trajectory + return "", nil + } + } + + if info == nil { + // walk may not be able to provide a FileInfo object, don't allow for this to stop indexing; keep track of the paths and continue. + r.errPaths[path] = fmt.Errorf("no file info observable at path=%q", path) + return "", nil + } + + // here we check to see if we need to normalize paths to posix on the way in coming from windows + if runtime.GOOS == WindowsOS { + path = windowsToPosix(path) + } + + newRoot, err := r.addPathToIndex(path, info) + if r.isFileAccessErr(path, err) { + return "", nil + } + + return newRoot, nil +} + +func (r *directoryIndexer) disallowFileAccessErr(path string, _ os.FileInfo, err error) error { + if r.isFileAccessErr(path, err) { + return errSkipPath + } + return nil +} + +func (r *directoryIndexer) isFileAccessErr(path string, err error) bool { + // don't allow for errors to stop indexing, keep track of the paths and continue. + if err != nil { + log.Warnf("unable to access path=%q: %+v", path, err) + r.errPaths[path] = err + return true + } + return false +} + +func (r directoryIndexer) addPathToIndex(p string, info os.FileInfo) (string, error) { + switch t := file.TypeFromMode(info.Mode()); t { + case file.TypeSymlink: + return r.addSymlinkToIndex(p, info) + case file.TypeDir: + return "", r.addDirectoryToIndex(p, info) + case file.TypeReg: + return "", r.addFileToIndex(p, info) + default: + return "", fmt.Errorf("unsupported file type: %s", t) + } +} + +func (r directoryIndexer) addDirectoryToIndex(p string, info os.FileInfo) error { + ref, err := r.tree.AddDir(file.Path(p)) + if err != nil { + return err + } + + metadata := file.NewMetadataFromPath(p, info) + r.index.Add(*ref, metadata) + + return nil +} + +func (r directoryIndexer) addFileToIndex(p string, info os.FileInfo) error { + ref, err := r.tree.AddFile(file.Path(p)) + if err != nil { + return err + } + + metadata := file.NewMetadataFromPath(p, info) + r.index.Add(*ref, metadata) + + return nil +} + +func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, error) { + linkTarget, err := os.Readlink(p) + if err != nil { + return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err) + } + + if filepath.IsAbs(linkTarget) { + // if the link is absolute (e.g, /bin/ls -> /bin/busybox) we need to + // resolve relative to the root of the base directory + linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget)) + } else { + // if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to + // resolve it relative to the directory in question (e.g. resolve to + // /dev/fd/2) + if r.base == "" { + linkTarget = filepath.Join(filepath.Dir(p), linkTarget) + } else { + // if the base is set, then we first need to resolve the link, + // before finding it's location in the base + dir, err := filepath.Rel(r.base, filepath.Dir(p)) + if err != nil { + return "", fmt.Errorf("unable to resolve relative path for path=%q: %w", p, err) + } + linkTarget = filepath.Join(r.base, filepath.Clean(filepath.Join("/", dir, linkTarget))) + } + } + + ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget)) + if err != nil { + return "", err + } + + targetAbsPath := linkTarget + if !filepath.IsAbs(targetAbsPath) { + targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) + } + + metadata := file.NewMetadataFromPath(p, info) + metadata.LinkDestination = linkTarget + r.index.Add(*ref, metadata) + + return targetAbsPath, nil +} + +func (r directoryIndexer) hasBeenIndexed(p string) (bool, *file.Metadata) { + filePath := file.Path(p) + if !r.tree.HasPath(filePath) { + return false, nil + } + + exists, ref, err := r.tree.File(filePath) + if err != nil || !exists || !ref.HasReference() { + return false, nil + } + + // cases like "/" will be in the tree, but not been indexed yet (a special case). We want to capture + // these cases as new paths to index. + if !ref.HasReference() { + return false, nil + } + + entry, err := r.index.Get(*ref.Reference) + if err != nil { + return false, nil + } + + return true, &entry.Metadata +} + +func (r *directoryIndexer) disallowRevisitingVisitor(path string, _ os.FileInfo, _ error) error { + // this prevents visiting: + // - link destinations twice, once for the real file and another through the virtual path + // - infinite link cycles + if indexed, metadata := r.hasBeenIndexed(path); indexed { + if metadata.IsDir { + // signal to walk() that we should skip this directory entirely + return fs.SkipDir + } + return errSkipPath + } + return nil +} + +func disallowUnixSystemRuntimePath(path string, _ os.FileInfo, _ error) error { + if internal.HasAnyOfPrefixes(path, unixSystemRuntimePrefixes...) { + return fs.SkipDir + } + return nil +} + +func disallowByFileType(_ string, info os.FileInfo, _ error) error { + if info == nil { + // we can't filter out by filetype for non-existent files + return nil + } + switch file.TypeFromMode(info.Mode()) { + case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFifo, file.TypeIrregular: + return errSkipPath + // note: symlinks that point to these files may still get by. + // We handle this later in processing to help prevent against infinite links traversal. + } + + return nil +} + +func requireFileInfo(_ string, info os.FileInfo, _ error) error { + if info == nil { + return errSkipPath + } + return nil +} + +func indexingProgress(path string) (*progress.Stage, *progress.Manual) { + stage := &progress.Stage{} + prog := &progress.Manual{ + Total: -1, + } + + bus.Publish(partybus.Event{ + Type: event.FileIndexingStarted, + Source: path, + Value: struct { + progress.Stager + progress.Progressable + }{ + Stager: progress.Stager(stage), + Progressable: prog, + }, + }) + + return stage, prog +} diff --git a/syft/source/directory_indexer_test.go b/syft/source/directory_indexer_test.go new file mode 100644 index 00000000000..b6403559d16 --- /dev/null +++ b/syft/source/directory_indexer_test.go @@ -0,0 +1,328 @@ +package source + +import ( + "io/fs" + "os" + "path" + "sort" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/wagoodman/go-progress" + + "github.com/anchore/stereoscope/pkg/file" +) + +type indexerMock struct { + observedRoots []string + additionalRoots map[string][]string +} + +func (m *indexerMock) indexer(s string, _ *progress.Stage) ([]string, error) { + m.observedRoots = append(m.observedRoots, s) + return m.additionalRoots[s], nil +} + +func Test_indexAllRoots(t *testing.T) { + tests := []struct { + name string + root string + mock indexerMock + expectedRoots []string + }{ + { + name: "no additional roots", + root: "a/place", + mock: indexerMock{ + additionalRoots: make(map[string][]string), + }, + expectedRoots: []string{ + "a/place", + }, + }, + { + name: "additional roots from a single call", + root: "a/place", + mock: indexerMock{ + additionalRoots: map[string][]string{ + "a/place": { + "another/place", + "yet-another/place", + }, + }, + }, + expectedRoots: []string{ + "a/place", + "another/place", + "yet-another/place", + }, + }, + { + name: "additional roots from a multiple calls", + root: "a/place", + mock: indexerMock{ + additionalRoots: map[string][]string{ + "a/place": { + "another/place", + "yet-another/place", + }, + "yet-another/place": { + "a-quiet-place-2", + "a-final/place", + }, + }, + }, + expectedRoots: []string{ + "a/place", + "another/place", + "yet-another/place", + "a-quiet-place-2", + "a-final/place", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.NoError(t, indexAllRoots(test.root, test.mock.indexer)) + }) + } +} + +func TestDirectoryIndexer_handleFileAccessErr(t *testing.T) { + tests := []struct { + name string + input error + expectedPathTracked bool + }{ + { + name: "permission error does not propagate", + input: os.ErrPermission, + expectedPathTracked: true, + }, + { + name: "file does not exist error does not propagate", + input: os.ErrNotExist, + expectedPathTracked: true, + }, + { + name: "non-permission errors are tracked", + input: os.ErrInvalid, + expectedPathTracked: true, + }, + { + name: "non-errors ignored", + input: nil, + expectedPathTracked: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + r := directoryIndexer{ + errPaths: make(map[string]error), + } + p := "a/place" + assert.Equal(t, r.isFileAccessErr(p, test.input), test.expectedPathTracked) + _, exists := r.errPaths[p] + assert.Equal(t, test.expectedPathTracked, exists) + }) + } +} + +func TestDirectoryIndexer_IncludeRootPathInIndex(t *testing.T) { + filterFn := func(path string, _ os.FileInfo, _ error) error { + if path != "/" { + return fs.SkipDir + } + return nil + } + + indexer := newDirectoryIndexer("/", "", filterFn) + tree, index, err := indexer.build() + require.NoError(t, err) + + exists, ref, err := tree.File(file.Path("/")) + require.NoError(t, err) + require.NotNil(t, ref) + assert.True(t, exists) + + _, err = index.Get(*ref.Reference) + require.NoError(t, err) +} + +func TestDirectoryIndexer_indexPath_skipsNilFileInfo(t *testing.T) { + // TODO: Ideally we can use an OS abstraction, which would obviate the need for real FS setup. + tempFile, err := os.CreateTemp("", "") + require.NoError(t, err) + + indexer := newDirectoryIndexer(tempFile.Name(), "") + + t.Run("filtering path with nil os.FileInfo", func(t *testing.T) { + assert.NotPanics(t, func() { + _, err := indexer.indexPath("/dont-care", nil, nil) + assert.NoError(t, err) + assert.False(t, indexer.tree.HasPath("/dont-care")) + }) + }) +} + +func TestDirectoryIndexer_index(t *testing.T) { + // note: this test is testing the effects from newDirectoryResolver, indexTree, and addPathToIndex + indexer := newDirectoryIndexer("test-fixtures/system_paths/target", "") + tree, index, err := indexer.build() + require.NoError(t, err) + + tests := []struct { + name string + path string + }{ + { + name: "has dir", + path: "test-fixtures/system_paths/target/home", + }, + { + name: "has path", + path: "test-fixtures/system_paths/target/home/place", + }, + { + name: "has symlink", + path: "test-fixtures/system_paths/target/link/a-symlink", + }, + { + name: "has symlink target", + path: "test-fixtures/system_paths/outside_root/link_target/place", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + info, err := os.Stat(test.path) + assert.NoError(t, err) + + // note: the index uses absolute paths, so assertions MUST keep this in mind + cwd, err := os.Getwd() + require.NoError(t, err) + + p := file.Path(path.Join(cwd, test.path)) + assert.Equal(t, true, tree.HasPath(p)) + exists, ref, err := tree.File(p) + assert.Equal(t, true, exists) + if assert.NoError(t, err) { + return + } + + entry, err := index.Get(*ref.Reference) + require.NoError(t, err) + assert.Equal(t, info.Mode(), entry.Mode) + }) + } +} + +func TestDirectoryIndexer_SkipsAlreadyVisitedLinkDestinations(t *testing.T) { + var observedPaths []string + pathObserver := func(p string, _ os.FileInfo, _ error) error { + fields := strings.Split(p, "test-fixtures/symlinks-prune-indexing") + if len(fields) != 2 { + t.Fatalf("unable to parse path: %s", p) + } + clean := strings.TrimLeft(fields[1], "/") + if clean != "" { + observedPaths = append(observedPaths, clean) + } + return nil + } + resolver := newDirectoryIndexer("./test-fixtures/symlinks-prune-indexing", "") + // we want to cut ahead of any possible filters to see what paths are considered for indexing (closest to walking) + resolver.pathIndexVisitors = append([]pathIndexVisitor{pathObserver}, resolver.pathIndexVisitors...) + + // note: this test is NOT about the effects left on the tree or the index, but rather the WHICH paths that are + // considered for indexing and HOW traversal prunes paths that have already been visited + _, _, err := resolver.build() + require.NoError(t, err) + + expected := []string{ + "before-path", + "c-file.txt", + "c-path", + "path", + "path/1", + "path/1/2", + "path/1/2/3", + "path/1/2/3/4", + "path/1/2/3/4/dont-index-me-twice.txt", + "path/5", + "path/5/6", + "path/5/6/7", + "path/5/6/7/8", + "path/5/6/7/8/dont-index-me-twice-either.txt", + "path/file.txt", + // everything below is after the original tree is indexed, and we are now indexing additional roots from symlinks + "path", // considered from symlink before-path, but pruned + "before-path/file.txt", // considered from symlink c-file.txt, but pruned + "before-path", // considered from symlink c-path, but pruned + } + + assert.Equal(t, expected, observedPaths, "visited paths differ \n %s", cmp.Diff(expected, observedPaths)) + +} + +func TestDirectoryIndexer_IndexesAllTypes(t *testing.T) { + indexer := newDirectoryIndexer("./test-fixtures/symlinks-prune-indexing", "") + + tree, index, err := indexer.build() + require.NoError(t, err) + + allRefs := tree.AllFiles(file.AllTypes()...) + var pathRefs []file.Reference + paths := strset.New() + for _, ref := range allRefs { + fields := strings.Split(string(ref.RealPath), "test-fixtures/symlinks-prune-indexing") + if len(fields) != 2 { + t.Fatalf("unable to parse path: %s", ref.RealPath) + } + clean := strings.TrimLeft(fields[1], "/") + if clean == "" { + continue + } + paths.Add(clean) + pathRefs = append(pathRefs, ref) + } + + pathsList := paths.List() + sort.Strings(pathsList) + + expected := []string{ + "before-path", // link + "c-file.txt", // link + "c-path", // link + "path", // dir + "path/1", // dir + "path/1/2", // dir + "path/1/2/3", // dir + "path/1/2/3/4", // dir + "path/1/2/3/4/dont-index-me-twice.txt", // file + "path/5", // dir + "path/5/6", // dir + "path/5/6/7", // dir + "path/5/6/7/8", // dir + "path/5/6/7/8/dont-index-me-twice-either.txt", // file + "path/file.txt", // file + } + expectedSet := strset.New(expected...) + + // make certain all expected paths are in the tree (and no extra ones are their either) + + assert.True(t, paths.IsEqual(expectedSet), "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, pathsList)) + + // make certain that the paths are also in the file index + + for _, ref := range pathRefs { + _, err := index.Get(ref) + require.NoError(t, err) + } + +} diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index b3cded96734..c72795b7c9b 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -4,22 +4,15 @@ import ( "errors" "fmt" "io" - "io/fs" "os" "path" "path/filepath" "runtime" "strings" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/filetree" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/event" ) const WindowsOS = "windows" @@ -34,30 +27,25 @@ var errSkipPath = errors.New("skip path") var _ FileResolver = (*directoryResolver)(nil) -type pathIndexVisitor func(string, os.FileInfo, error) error - // directoryResolver implements path and content access for the directory data source. type directoryResolver struct { path string base string currentWdRelativeToRoot string currentWd string - fileTree *filetree.FileTree - fileTreeIndex filetree.Index + tree filetree.Reader + index filetree.IndexReader searchContext filetree.Searcher - metadata map[file.ID]FileMetadata - // TODO: wire up to report these paths in the json report - pathIndexVisitors []pathIndexVisitor - errPaths map[string]error + indexer *directoryIndexer } func newDirectoryResolver(root string, base string, pathFilters ...pathIndexVisitor) (*directoryResolver, error) { - resolver, err := newDirectoryResolverWithoutIndex(root, base, pathFilters...) + r, err := newDirectoryResolverWithoutIndex(root, base, pathFilters...) if err != nil { return nil, err } - return resolver, resolver.index() + return r, r.buildIndex() } func newDirectoryResolverWithoutIndex(root string, base string, pathFilters ...pathIndexVisitor) (*directoryResolver, error) { @@ -99,268 +87,33 @@ func newDirectoryResolverWithoutIndex(root string, base string, pathFilters ...p currentWdRelRoot = filepath.Clean(cleanRoot) } - resolver := &directoryResolver{ + return &directoryResolver{ path: cleanRoot, base: cleanBase, currentWd: cleanCWD, currentWdRelativeToRoot: currentWdRelRoot, - fileTree: filetree.NewFileTree(), - fileTreeIndex: filetree.NewIndex(), - metadata: make(map[file.ID]FileMetadata), - pathIndexVisitors: append([]pathIndexVisitor{requireFileInfo, disallowByFileType, disallowUnixSystemRuntimePath}, pathFilters...), - errPaths: make(map[string]error), - } - - // these additional stateful visitors should be the first thing considered when walking / indexing - resolver.pathIndexVisitors = append([]pathIndexVisitor{resolver.disallowRevisitingVisitor, resolver.disallowFileAccessErr}, resolver.pathIndexVisitors...) - - return resolver, nil -} - -func (r *directoryResolver) index() error { - return indexAllRoots(r.path, r.indexTree) -} - -func (r *directoryResolver) disallowRevisitingVisitor(path string, _ os.FileInfo, _ error) error { - // this prevents visiting: - // - link destinations twice, once for the real file and another through the virtual path - // - infinite link cycles - if indexed, metadata := r.hasBeenIndexed(path); indexed { - if metadata.IsDir { - // signal to walk() that we should skip this directory entirely - return fs.SkipDir - } - return errSkipPath - } - return nil -} - -func requireFileInfo(_ string, info os.FileInfo, _ error) error { - if info == nil { - return errSkipPath - } - return nil -} - -func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]string, error) { - log.WithFields("path", root).Trace("indexing filetree") - - var roots []string - var err error - - root, err = filepath.Abs(root) - if err != nil { - return nil, err - } - - // we want to be able to index single files with the directory resolver. However, we should also allow for attempting - // to index paths that do not exist (that is, a root that does not exist is not an error case that should stop indexing). - // For this reason we look for an opportunity to discover if the given root is a file, and if so add a single root, - // but continue forth with index regardless if the given root path exists or not. - fi, err := os.Stat(root) - if err != nil && fi != nil && !fi.IsDir() { - // note: we want to index the path regardless of an error stat-ing the path - newRoot, _ := r.indexPath(root, fi, nil) - if newRoot != "" { - roots = append(roots, newRoot) - } - return roots, nil - } - - err = filepath.Walk(root, - func(path string, info os.FileInfo, err error) error { - stager.Current = path - - newRoot, err := r.indexPath(path, info, err) - - if err != nil { - return err - } - - if newRoot != "" { - roots = append(roots, newRoot) - } - - return nil - }) - - if err != nil { - return nil, fmt.Errorf("unable to index root=%q: %w", root, err) - } - - r.searchContext = filetree.NewSearchContext(r.fileTree, r.fileTreeIndex) - - return roots, nil -} - -func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error) (string, error) { - // ignore any path which a filter function returns true - for _, filterFn := range r.pathIndexVisitors { - if filterFn == nil { - continue - } - - if filterErr := filterFn(path, info, err); filterErr != nil { - if errors.Is(filterErr, fs.SkipDir) { - // signal to walk() to skip this directory entirely (even if we're processing a file) - return "", filterErr - } - // skip this path but don't affect walk() trajectory - return "", nil - } - } - - if info == nil { - // walk may not be able to provide a FileInfo object, don't allow for this to stop indexing; keep track of the paths and continue. - r.errPaths[path] = fmt.Errorf("no file info observable at path=%q", path) - return "", nil - } - - // here we check to see if we need to normalize paths to posix on the way in coming from windows - if runtime.GOOS == WindowsOS { - path = windowsToPosix(path) - } - - newRoot, err := r.addPathToIndex(path, info) - if r.isFileAccessErr(path, err) { - return "", nil - } - - return newRoot, nil -} - -func (r *directoryResolver) disallowFileAccessErr(path string, _ os.FileInfo, err error) error { - if r.isFileAccessErr(path, err) { - return errSkipPath - } - return nil -} - -func (r *directoryResolver) isFileAccessErr(path string, err error) bool { - // don't allow for errors to stop indexing, keep track of the paths and continue. - if err != nil { - log.Warnf("unable to access path=%q: %+v", path, err) - r.errPaths[path] = err - return true - } - return false -} - -func (r directoryResolver) addPathToIndex(p string, info os.FileInfo) (string, error) { - switch t := file.TypeFromMode(info.Mode()); t { - case file.TypeSymlink: - return r.addSymlinkToIndex(p, info) - case file.TypeDir: - return "", r.addDirectoryToIndex(p, info) - case file.TypeReg: - return "", r.addFileToIndex(p, info) - default: - return "", fmt.Errorf("unsupported file type: %s", t) - } + tree: filetree.New(), + index: filetree.NewIndex(), + indexer: newDirectoryIndexer(cleanRoot, cleanBase, pathFilters...), + }, nil } -func (r directoryResolver) hasBeenIndexed(p string) (bool, *file.Metadata) { - filePath := file.Path(p) - if !r.fileTree.HasPath(filePath) { - return false, nil +func (r *directoryResolver) buildIndex() error { + if r.indexer == nil { + return fmt.Errorf("no directory indexer configured") } - - exists, ref, err := r.fileTree.File(filePath) - if err != nil || !exists || !ref.HasReference() { - return false, nil - } - - // cases like "/" will be in the tree, but not been indexed yet (a special case). We want to capture - // these cases as new paths to index. - if !ref.HasReference() { - return false, nil - } - - entry, err := r.fileTreeIndex.Get(*ref.Reference) - if err != nil { - return false, nil - } - - return true, &entry.Metadata -} - -func (r directoryResolver) addDirectoryToIndex(p string, info os.FileInfo) error { - ref, err := r.fileTree.AddDir(file.Path(p)) - if err != nil { - return err - } - - metadata := file.NewMetadataFromPath(p, info) - r.addFileToFileTreeIndex(ref, metadata) - r.fileTreeIndex.Add(*ref, metadata) - - return nil -} - -func (r directoryResolver) addFileToIndex(p string, info os.FileInfo) error { - ref, err := r.fileTree.AddFile(file.Path(p)) + tree, index, err := r.indexer.build() if err != nil { return err } - metadata := file.NewMetadataFromPath(p, info) - r.addFileToFileTreeIndex(ref, metadata) - r.fileTreeIndex.Add(*ref, metadata) + r.tree = tree + r.index = index + r.searchContext = filetree.NewSearchContext(tree, index) return nil } -func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string, error) { - linkTarget, err := os.Readlink(p) - if err != nil { - return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err) - } - - if filepath.IsAbs(linkTarget) { - // if the link is absolute (e.g, /bin/ls -> /bin/busybox) we need to - // resolve relative to the root of the base directory - linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget)) - } else { - // if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to - // resolve it relative to the directory in question (e.g. resolve to - // /dev/fd/2) - if r.base == "" { - linkTarget = filepath.Join(filepath.Dir(p), linkTarget) - } else { - // if the base is set, then we first need to resolve the link, - // before finding it's location in the base - dir, err := filepath.Rel(r.base, filepath.Dir(p)) - if err != nil { - return "", fmt.Errorf("unable to resolve relative path for path=%q: %w", p, err) - } - linkTarget = filepath.Join(r.base, filepath.Clean(filepath.Join("/", dir, linkTarget))) - } - } - - ref, err := r.fileTree.AddSymLink(file.Path(p), file.Path(linkTarget)) - if err != nil { - return "", err - } - - targetAbsPath := linkTarget - if !filepath.IsAbs(targetAbsPath) { - targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) - } - - metadata := file.NewMetadataFromPath(p, info) - metadata.LinkDestination = linkTarget - r.addFileToFileTreeIndex(ref, metadata) - r.fileTreeIndex.Add(*ref, metadata) - - return targetAbsPath, nil -} - -func (r directoryResolver) addFileToFileTreeIndex(ref *file.Reference, metadata FileMetadata) { - if ref != nil { - r.metadata[ref.ID()] = metadata - } -} - func (r directoryResolver) requestPath(userPath string) (string, error) { if filepath.IsAbs(userPath) { // don't allow input to potentially hop above root path @@ -399,7 +152,7 @@ func (r *directoryResolver) HasPath(userPath string) bool { if err != nil { return false } - return r.fileTree.HasPath(file.Path(requestPath)) + return r.tree.HasPath(file.Path(requestPath)) } // Stringer to represent a directory path data source @@ -429,7 +182,7 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error) continue } - entry, err := r.fileTreeIndex.Get(*ref.Reference) + entry, err := r.index.Get(*ref.Reference) if err != nil { log.Warnf("unable to get file by path=%q : %+v", userPath, err) continue @@ -472,7 +225,7 @@ func (r directoryResolver) FilesByGlob(patterns ...string) ([]Location, error) { if !refVia.HasReference() || uniqueFileIDs.Contains(*refVia.Reference) { continue } - entry, err := r.fileTreeIndex.Get(*refVia.Reference) + entry, err := r.index.Get(*refVia.Reference) if err != nil { return nil, fmt.Errorf("unable to get file metadata for reference %s: %w", refVia.Reference.RealPath, err) } @@ -517,7 +270,7 @@ func (r directoryResolver) FileContentsByLocation(location Location) (io.ReadClo return nil, errors.New("empty path given") } - entry, err := r.fileTreeIndex.Get(location.ref) + entry, err := r.index.Get(location.ref) if err != nil { return nil, err } @@ -541,7 +294,7 @@ func (r *directoryResolver) AllLocations() <-chan Location { results := make(chan Location) go func() { defer close(results) - for _, ref := range r.fileTree.AllFiles(file.AllTypes()...) { + for _, ref := range r.tree.AllFiles(file.AllTypes()...) { results <- NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref) } }() @@ -549,12 +302,12 @@ func (r *directoryResolver) AllLocations() <-chan Location { } func (r *directoryResolver) FileMetadataByLocation(location Location) (FileMetadata, error) { - metadata, exists := r.metadata[location.ref.ID()] - if !exists { + entry, err := r.index.Get(location.ref) + if err != nil { return FileMetadata{}, fmt.Errorf("location: %+v : %w", location, os.ErrNotExist) } - return metadata, nil + return entry.Metadata, nil } func (r *directoryResolver) FilesByMIMEType(types ...string) ([]Location, error) { @@ -609,83 +362,3 @@ func posixToWindows(posixPath string) (windowsPath string) { // combine volume name and backslash components return filepath.Clean(volumeName + remainingTranslatedPath) } - -func disallowUnixSystemRuntimePath(path string, _ os.FileInfo, _ error) error { - if internal.HasAnyOfPrefixes(path, unixSystemRuntimePrefixes...) { - return fs.SkipDir - } - return nil -} - -func disallowByFileType(_ string, info os.FileInfo, _ error) error { - if info == nil { - // we can't filter out by filetype for non-existent files - return nil - } - switch file.TypeFromMode(info.Mode()) { - case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFifo, file.TypeIrregular: - return errSkipPath - // note: symlinks that point to these files may still get by. - // We handle this later in processing to help prevent against infinite links traversal. - } - - return nil -} - -func indexAllRoots(root string, indexer func(string, *progress.Stage) ([]string, error)) error { - // why account for multiple roots? To cover cases when there is a symlink that references above the root path, - // in which case we need to additionally index where the link resolves to. it's for this reason why the filetree - // must be relative to the root of the filesystem (and not just relative to the given path). - pathsToIndex := []string{root} - fullPathsMap := map[string]struct{}{} - - stager, prog := indexingProgress(root) - defer prog.SetCompleted() -loop: - for { - var currentPath string - switch len(pathsToIndex) { - case 0: - break loop - case 1: - currentPath, pathsToIndex = pathsToIndex[0], nil - default: - currentPath, pathsToIndex = pathsToIndex[0], pathsToIndex[1:] - } - - additionalRoots, err := indexer(currentPath, stager) - if err != nil { - return fmt.Errorf("unable to index filesystem path=%q: %w", currentPath, err) - } - - for _, newRoot := range additionalRoots { - if _, ok := fullPathsMap[newRoot]; !ok { - fullPathsMap[newRoot] = struct{}{} - pathsToIndex = append(pathsToIndex, newRoot) - } - } - } - - return nil -} - -func indexingProgress(path string) (*progress.Stage, *progress.Manual) { - stage := &progress.Stage{} - prog := &progress.Manual{ - Total: -1, - } - - bus.Publish(partybus.Event{ - Type: event.FileIndexingStarted, - Source: path, - Value: struct { - progress.Stager - progress.Progressable - }{ - Stager: progress.Stager(stage), - Progressable: prog, - }, - }) - - return stage, prog -} diff --git a/syft/source/directory_resolver_test.go b/syft/source/directory_resolver_test.go index 7602804bc3f..2b4b505e425 100644 --- a/syft/source/directory_resolver_test.go +++ b/syft/source/directory_resolver_test.go @@ -8,7 +8,6 @@ import ( "io/fs" "io/ioutil" "os" - "path" "path/filepath" "sort" "strings" @@ -19,7 +18,6 @@ import ( "github.com/scylladb/go-set/strset" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/wagoodman/go-progress" "github.com/anchore/stereoscope/pkg/file" ) @@ -423,171 +421,6 @@ func Test_isUnallowableFileType(t *testing.T) { } } -func Test_directoryResolver_index(t *testing.T) { - // note: this test is testing the effects from newDirectoryResolver, indexTree, and addPathToIndex - r, err := newDirectoryResolver("test-fixtures/system_paths/target", "") - if err != nil { - t.Fatalf("unable to get indexed dir resolver: %+v", err) - } - tests := []struct { - name string - path string - }{ - { - name: "has dir", - path: "test-fixtures/system_paths/target/home", - }, - { - name: "has path", - path: "test-fixtures/system_paths/target/home/place", - }, - { - name: "has symlink", - path: "test-fixtures/system_paths/target/link/a-symlink", - }, - { - name: "has symlink target", - path: "test-fixtures/system_paths/outside_root/link_target/place", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - info, err := os.Stat(test.path) - assert.NoError(t, err) - - // note: the index uses absolute paths, so assertions MUST keep this in mind - cwd, err := os.Getwd() - require.NoError(t, err) - - p := file.Path(path.Join(cwd, test.path)) - assert.Equal(t, true, r.fileTree.HasPath(p)) - exists, ref, err := r.fileTree.File(p) - assert.Equal(t, true, exists) - if assert.NoError(t, err) { - return - } - assert.Equal(t, info, r.metadata[ref.ID()]) - }) - } -} - -func Test_handleFileAccessErr(t *testing.T) { - tests := []struct { - name string - input error - expectedPathTracked bool - }{ - { - name: "permission error does not propagate", - input: os.ErrPermission, - expectedPathTracked: true, - }, - { - name: "file does not exist error does not propagate", - input: os.ErrNotExist, - expectedPathTracked: true, - }, - { - name: "non-permission errors are tracked", - input: os.ErrInvalid, - expectedPathTracked: true, - }, - { - name: "non-errors ignored", - input: nil, - expectedPathTracked: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - r := directoryResolver{ - errPaths: make(map[string]error), - } - p := "a/place" - assert.Equal(t, r.isFileAccessErr(p, test.input), test.expectedPathTracked) - _, exists := r.errPaths[p] - assert.Equal(t, test.expectedPathTracked, exists) - }) - } -} - -type indexerMock struct { - observedRoots []string - additionalRoots map[string][]string -} - -func (m *indexerMock) indexer(s string, _ *progress.Stage) ([]string, error) { - m.observedRoots = append(m.observedRoots, s) - return m.additionalRoots[s], nil -} - -func Test_indexAllRoots(t *testing.T) { - tests := []struct { - name string - root string - mock indexerMock - expectedRoots []string - }{ - { - name: "no additional roots", - root: "a/place", - mock: indexerMock{ - additionalRoots: make(map[string][]string), - }, - expectedRoots: []string{ - "a/place", - }, - }, - { - name: "additional roots from a single call", - root: "a/place", - mock: indexerMock{ - additionalRoots: map[string][]string{ - "a/place": { - "another/place", - "yet-another/place", - }, - }, - }, - expectedRoots: []string{ - "a/place", - "another/place", - "yet-another/place", - }, - }, - { - name: "additional roots from a multiple calls", - root: "a/place", - mock: indexerMock{ - additionalRoots: map[string][]string{ - "a/place": { - "another/place", - "yet-another/place", - }, - "yet-another/place": { - "a-quiet-place-2", - "a-final/place", - }, - }, - }, - expectedRoots: []string{ - "a/place", - "another/place", - "yet-another/place", - "a-quiet-place-2", - "a-final/place", - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert.NoError(t, indexAllRoots(test.root, test.mock.indexer)) - }) - } -} - func Test_directoryResolver_FilesByMIMEType(t *testing.T) { tests := []struct { fixturePath string @@ -732,7 +565,7 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) { r, err := newDirectoryResolver(".", "") require.NoError(t, err) - exists, existingPath, err := r.fileTree.File(file.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt"))) + exists, existingPath, err := r.tree.File(file.Path(filepath.Join(cwd, "test-fixtures/image-simple/file-1.txt"))) require.True(t, exists) require.NoError(t, err) require.True(t, existingPath.HasReference()) @@ -843,43 +676,6 @@ func testWithTimeout(t *testing.T, timeout time.Duration, test func(*testing.T)) } } -func Test_IncludeRootPathInIndex(t *testing.T) { - filterFn := func(path string, _ os.FileInfo, _ error) error { - if path != "/" { - return fs.SkipDir - } - return nil - } - - resolver, err := newDirectoryResolver("/", "", filterFn) - require.NoError(t, err) - - exists, ref, err := resolver.fileTree.File(file.Path("/")) - require.NoError(t, err) - require.NotNil(t, ref) - assert.True(t, exists) - - _, exists = resolver.metadata[ref.ID()] - require.True(t, exists) -} - -func TestDirectoryResolver_indexPath_skipsNilFileInfo(t *testing.T) { - // TODO: Ideally we can use an OS abstraction, which would obviate the need for real FS setup. - tempFile, err := os.CreateTemp("", "") - require.NoError(t, err) - - resolver, err := newDirectoryResolverWithoutIndex(tempFile.Name(), "") - require.NoError(t, err) - - t.Run("filtering path with nil os.FileInfo", func(t *testing.T) { - assert.NotPanics(t, func() { - _, err := resolver.indexPath("/dont-care", nil, nil) - assert.NoError(t, err) - assert.False(t, resolver.fileTree.HasPath("/dont-care")) - }) - }) -} - func TestDirectoryResolver_FilesByPath_baseRoot(t *testing.T) { cases := []struct { name string @@ -1214,7 +1010,7 @@ func TestDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) { resolver, err := newDirectoryResolver("./test-fixtures/symlinks-prune-indexing", "") require.NoError(t, err) - allRealPaths := resolver.fileTree.AllRealPaths() + allRealPaths := resolver.tree.AllRealPaths() pathSet := file.NewPathSet(allRealPaths...) assert.False(t, @@ -1229,114 +1025,13 @@ func TestDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) { } -func TestDirectoryResolver_SkipsAlreadyVisitedLinkDestinations(t *testing.T) { - var observedPaths []string - pathObserver := func(p string, _ os.FileInfo, _ error) error { - fields := strings.Split(p, "test-fixtures/symlinks-prune-indexing") - if len(fields) != 2 { - t.Fatalf("unable to parse path: %s", p) - } - clean := strings.TrimLeft(fields[1], "/") - if clean != "" { - observedPaths = append(observedPaths, clean) - } - return nil - } - resolver, err := newDirectoryResolverWithoutIndex("./test-fixtures/symlinks-prune-indexing", "") - require.NoError(t, err) - // we want to cut ahead of any possible filters to see what paths are considered for indexing (closest to walking) - resolver.pathIndexVisitors = append([]pathIndexVisitor{pathObserver}, resolver.pathIndexVisitors...) - - require.NoError(t, resolver.index()) - - expected := []string{ - "before-path", - "c-file.txt", - "c-path", - "path", - "path/1", - "path/1/2", - "path/1/2/3", - "path/1/2/3/4", - "path/1/2/3/4/dont-index-me-twice.txt", - "path/5", - "path/5/6", - "path/5/6/7", - "path/5/6/7/8", - "path/5/6/7/8/dont-index-me-twice-either.txt", - "path/file.txt", - // everything below is after the original tree is indexed, and we are now indexing additional roots from symlinks - "path", // considered from symlink before-path, but pruned - "before-path/file.txt", // considered from symlink c-file.txt, but pruned - "before-path", // considered from symlink c-path, but pruned - } - - assert.Equal(t, expected, observedPaths, "visited paths differ \n %s", cmp.Diff(expected, observedPaths)) - -} - -func TestDirectoryResolver_IndexesAllTypes(t *testing.T) { - resolver, err := newDirectoryResolver("./test-fixtures/symlinks-prune-indexing", "") - require.NoError(t, err) - - allRefs := resolver.fileTree.AllFiles(file.AllTypes()...) - var pathRefs []file.Reference - paths := strset.New() - for _, ref := range allRefs { - fields := strings.Split(string(ref.RealPath), "test-fixtures/symlinks-prune-indexing") - if len(fields) != 2 { - t.Fatalf("unable to parse path: %s", ref.RealPath) - } - clean := strings.TrimLeft(fields[1], "/") - if clean == "" { - continue - } - paths.Add(clean) - pathRefs = append(pathRefs, ref) - } - - pathsList := paths.List() - sort.Strings(pathsList) - - expected := []string{ - "before-path", // link - "c-file.txt", // link - "c-path", // link - "path", // dir - "path/1", // dir - "path/1/2", // dir - "path/1/2/3", // dir - "path/1/2/3/4", // dir - "path/1/2/3/4/dont-index-me-twice.txt", // file - "path/5", // dir - "path/5/6", // dir - "path/5/6/7", // dir - "path/5/6/7/8", // dir - "path/5/6/7/8/dont-index-me-twice-either.txt", // file - "path/file.txt", // file - } - expectedSet := strset.New(expected...) - - // make certain all expected paths are in the tree (and no extra ones are their either) - - assert.True(t, paths.IsEqual(expectedSet), "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, pathsList)) - - // make certain that the paths are also in the file index - - for _, ref := range pathRefs { - _, err := resolver.fileTreeIndex.Get(ref) - require.NoError(t, err) - } - -} - func TestDirectoryResolver_FilesContents_errorOnDirRequest(t *testing.T) { resolver, err := newDirectoryResolver("./test-fixtures/system_paths", "") assert.NoError(t, err) var dirLoc *Location for loc := range resolver.AllLocations() { - entry, err := resolver.fileTreeIndex.Get(loc.ref) + entry, err := resolver.index.Get(loc.ref) require.NoError(t, err) if entry.Metadata.IsDir { dirLoc = &loc From 47963ba698ab04b202b3e0f63fbba26cd2797114 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sat, 4 Feb 2023 20:40:46 -0500 Subject: [PATCH 43/54] update docs to include details about searching Signed-off-by: Alex Goodman --- DEVELOPING.md | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index e5242647dde..cddb5070035 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -118,45 +118,55 @@ sequenceDiagram Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata. For example, Syft can locate and process `package-lock.json` files when performing filesystem scans. -See: [how to specify file globs](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) -and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) fora quick review. +See: [how to specify file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) +and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) fora quick review. #### Building a new Cataloger -Catalogers must fulfill the interface [found here](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger.go). +Catalogers must fulfill the interface [found here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger.go). This means that when building a new cataloger, the new struct must implement both method signatures of `Catalog` and `Name`. -A top level view of the functions that construct all the catalogers can be found [here](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/cataloger.go). +A top level view of the functions that construct all the catalogers can be found [here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/cataloger.go). When an author has finished writing a new cataloger this is the spot to plug in the new catalog constructor. -For a top level view of how the catalogers are used see [this function](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/catalog.go#L41-L100) as a reference. It ranges over all catalogers passed as an argument and invokes the `Catalog` method: +For a top level view of how the catalogers are used see [this function](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/catalog.go#L41-L100) as a reference. It ranges over all catalogers passed as an argument and invokes the `Catalog` method: Each cataloger has its own `Catalog` method, but this does not mean that they are all vastly different. -Take a look at the `apkdb` cataloger for alpine to see how it [constructs a generic.NewCataloger](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/apkdb/cataloger.go). +Take a look at the `apkdb` cataloger for alpine to see how it [constructs a generic.NewCataloger](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/cataloger.go). `generic.NewCataloger` is an abstraction syft uses to make writing common components easier. First, it takes the `catalogerName` to identify the cataloger. On the other side of the call it uses two key pieces which inform the cataloger how to identify and return packages, the `globPatterns` and the `parseFunction`: - The first piece is a `parseByGlob` matching pattern used to identify the files that contain the package metadata. -See [here for the APK example](https://github.com/anchore/syft/blob/main/syft/pkg/apk_metadata.go#L16-L41). +See [here for the APK example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/apk_metadata.go#L16-L41). - The other is a `parseFunction` which informs the cataloger what to do when it has found one of the above matches files. -See this [link for an example](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102). +See this [link for an example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102). If you're unsure about using the `Generic Cataloger` and think the use case being filled requires something more custom just file an issue or ask in our slack, and we'd be more than happy to help on the design. -Identified packages share a common struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/blob/main/syft/pkg/package.go#L16-L31). +Identified packages share a common struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/package.go#L16-L31). Metadata Note: Identified packages are also assigned specific metadata that can be unique to their environment. -See [this folder](https://github.com/anchore/syft/tree/main/syft/pkg) for examples of the different metadata types. +See [this folder](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg) for examples of the different metadata types. These are plugged into the `MetadataType` and `Metadata` fields in the above struct. `MetadataType` informs which type is being used. `Metadata` is an interface converted to that type. Finally, here is an example of where the package construction is done in the apk cataloger. The first link is where `newPackage` is called in the `parseFunction`. The second link shows the package construction: -- [Call for new package](https://github.com/anchore/syft/blob/6a7d6e6071829c7ce2943266c0e187b27c0b325c/syft/pkg/cataloger/apkdb/parse_apk_db.go#L96-L99) -- [APK Package Constructor](https://github.com/anchore/syft/blob/6a7d6e6071829c7ce2943266c0e187b27c0b325c/syft/pkg/cataloger/apkdb/package.go#L12-L27) +- [Call for new package](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L106) +- [APK Package Constructor](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/package.go#L12-L27) If you have more questions about implementing a cataloger or questions about one you might be currently working always feel free to file an issue or reach out to us [on slack](https://anchore.com/slack). +#### Searching for files + +All catalogers are provided an instance of the [`source.FileResolver`](https://github.com/anchore/syft/blob/v0.70.0/syft/source/file_resolver.go#L8) to interface with the image and search for files. The implementations for these +abstractions leverage [`stereoscope`](https://github.com/anchore/stereoscope) in order to perform searching. Here is a +rough outline how that works: + +1. a stereoscope `file.Index` is searched based on the input given (a path, glob, or MIME type). The index is relatively fast to search, but requires results to be filtered down to the files that exist in the specific layer(s) of interest. This is done automatically by the `filetree.Searcher` abstraction. This abstraction will fallback to searching directly against the raw `filetree.FileTree` if the index does not contain the file(s) of interest. Note: the `filetree.Searcher` is used by the `source.FileResolver` abstraction. +2. Once the set of files are returned from the `filetree.Searcher` the results are filtered down further to return the most unique file results. For example, you may have requested for files by a glob that returns multiple results. These results are filtered down to deduplicate by real files, so if a result contains two references to the same file, say one accessed via symlink and one accessed via the real path, then the real path reference is returned and the symlink reference is filtered out. If both were accessed by symlink then the first (by lexical order) is returned. This is done automatically by the `source.FileResolver` abstraction. +3. By the time results reach the `pkg.Cataloger` you are guaranteed to have a set of unique files that exist in the layer(s) of interest (relative to what the resolver supports). + ## Testing ### Levels of testing From 8eed4f765c7266ee061f34370f7bb9ab7893c504 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sat, 4 Feb 2023 20:41:20 -0500 Subject: [PATCH 44/54] [wip] bump stereoscope to development version Signed-off-by: Alex Goodman --- go.mod | 4 +--- go.sum | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5ae41a5dd5b..cc9e60df3d4 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230127222921-bb20df0cf085 + github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b github.com/docker/docker v20.10.23+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 @@ -157,5 +157,3 @@ retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) - -replace github.com/anchore/stereoscope => ../stereoscope diff --git a/go.sum b/go.sum index d85d3dc0370..f71c83f13aa 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= +github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b h1:06HhGWGGqoP+t9XTFxfte/4IeJT7IKGaL9/+joPJcUA= +github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= 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= From a46433d5e58883f71286ce3b3a8d5aeb01790472 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sun, 5 Feb 2023 07:37:34 -0500 Subject: [PATCH 45/54] fix linting Signed-off-by: Alex Goodman --- .golangci.yaml | 23 +++++++++++++---------- syft/source/image_all_layers_resolver.go | 1 + syft/source/image_squash_resolver.go | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 927d1d47ab8..8e447bfc366 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -6,7 +6,6 @@ issues: # include: # - EXC0002 # disable excluding of issues about comments from golint - linters: # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint disable-all: true @@ -14,25 +13,24 @@ linters: - asciicheck - bodyclose - depguard + - dogsled - dupl - errcheck - - errorlint - exportloopref - - forcetypeassert - funlen - gocognit - goconst - gocritic - gocyclo - gofmt - - tparallel - - importas + - goimports + - goprintffuncname - gosec - gosimple - govet - ineffassign - misspell - - nolintlint + - nakedret - revive - staticcheck - stylecheck @@ -41,6 +39,7 @@ linters: - unparam - unused - whitespace + linters-settings: funlen: # Checks the number of lines in a function. @@ -57,7 +56,7 @@ run: timeout: 10m # do not enable... -# - dogsled # found to be to niche and ineffective +# - deadcode # The owner seems to have abandoned the linter. Replaced by "unused". # - goprintffuncname # does not catch all cases and there are exceptions # - nakedret # does not catch all cases and should not fail a build # - gochecknoglobals @@ -73,7 +72,11 @@ run: # - lll # without a way to specify per-line exception cases, this is not usable # - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations # - nestif -# - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code -# - scopelint # deprecated +# - nolintlint # as of go1.19 this conflicts with the behavior of gofmt, which is a deal-breaker (lint-fix will still fail when running lint) +# - prealloc # following this rule isn't consistently a good idea, as it sometimes forces unnecessary allocations that result in less idiomatic code +# - rowserrcheck # not in a repo with sql, so this is not useful +# - scopelint # deprecated +# - structcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - testpackage -# - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90) +# - varcheck # The owner seems to have abandoned the linter. Replaced by "unused". +# - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90) diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index eeb09e3422b..3d5bc0e80a6 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -118,6 +118,7 @@ func (r *imageAllLayersResolver) FilesByPath(paths ...string) ([]Location, error } // FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image. +// nolint:gocognit func (r *imageAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) { uniqueFileIDs := file.NewFileReferenceSet() uniqueLocations := make([]Location, 0) diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index 3c85d7a6e73..c2cc9408947 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -77,6 +77,7 @@ func (r *imageSquashResolver) FilesByPath(paths ...string) ([]Location, error) { } // FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image. +// nolint:gocognit func (r *imageSquashResolver) FilesByGlob(patterns ...string) ([]Location, error) { uniqueFileIDs := file.NewFileReferenceSet() uniqueLocations := make([]Location, 0) From f76c12e48ee85661efc8c821de52c32d602121ad Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sun, 5 Feb 2023 07:49:31 -0500 Subject: [PATCH 46/54] adjust symlinks fixture to be fixed to digest Signed-off-by: Alex Goodman --- syft/source/test-fixtures/image-symlinks/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/source/test-fixtures/image-symlinks/Dockerfile b/syft/source/test-fixtures/image-symlinks/Dockerfile index 3b4e5125f56..f05f29a1380 100644 --- a/syft/source/test-fixtures/image-symlinks/Dockerfile +++ b/syft/source/test-fixtures/image-symlinks/Dockerfile @@ -1,5 +1,5 @@ # LAYER 0: -FROM busybox:1.34.0 +FROM busybox:1.34.0@sha256:e8e5cca392e3cf056fcdb3093e7ac2bf83fcf28b3bcf5818fe8ae71cf360c231 # LAYER 1: ADD file-1.txt . From ddcb97a74b235488163b914caa79578e943878ff Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sun, 5 Feb 2023 10:48:31 -0500 Subject: [PATCH 47/54] fix all-locations resolver tests Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 +- syft/source/image_all_layers_resolver_test.go | 385 ++------------- syft/source/image_squash_resolver_test.go | 446 +----------------- .../image-files-deleted/Dockerfile | 6 + .../image-files-deleted/file-1.txt | 1 + .../image-files-deleted/file-3.txt | 1 + .../image-files-deleted/target/file-2.txt | 1 + 8 files changed, 52 insertions(+), 794 deletions(-) create mode 100644 syft/source/test-fixtures/image-files-deleted/Dockerfile create mode 100644 syft/source/test-fixtures/image-files-deleted/file-1.txt create mode 120000 syft/source/test-fixtures/image-files-deleted/file-3.txt create mode 100644 syft/source/test-fixtures/image-files-deleted/target/file-2.txt diff --git a/go.mod b/go.mod index 817582b8b72..8f398175989 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b + github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5 github.com/docker/docker v23.0.0+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 diff --git a/go.sum b/go.sum index 7548efb815e..3a2e30d57cb 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b h1:06HhGWGGqoP+t9XTFxfte/4IeJT7IKGaL9/+joPJcUA= -github.com/anchore/stereoscope v0.0.0-20230204152652-a2eecb60297b/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= +github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5 h1:UaK2lgDh5u2wVQcDNIQMVLI+5z2ONwY/5tAs2L3+Wr8= +github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= 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= diff --git a/syft/source/image_all_layers_resolver_test.go b/syft/source/image_all_layers_resolver_test.go index a9932158c3c..1ba11304ddb 100644 --- a/syft/source/image_all_layers_resolver_test.go +++ b/syft/source/image_all_layers_resolver_test.go @@ -687,7 +687,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { } func TestAllLayersResolver_AllLocations(t *testing.T) { - img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") resolver, err := newAllLayersResolver(img) assert.NoError(t, err) @@ -697,446 +697,125 @@ func TestAllLayersResolver_AllLocations(t *testing.T) { paths.Add(loc.RealPath) } expected := []string{ + "/Dockerfile", "/file-1.txt", - "/file-2.txt", "/file-3.txt", - "/link-1", - "/link-2", - "/link-dead", - "/link-indirect", - "/link-within", - "/parent", - "/parent-link", - "/parent/file-4.txt", + "/target", + "/target/file-2.txt", - // from base image... + "/.wh.bin", + "/.wh.file-1.txt", + "/.wh.lib", "/bin", - "/bin/[", - "/bin/[[", - "/bin/acpid", - "/bin/add-shell", - "/bin/addgroup", - "/bin/adduser", - "/bin/adjtimex", - "/bin/ar", "/bin/arch", - "/bin/arp", - "/bin/arping", - "/bin/ascii", "/bin/ash", - "/bin/awk", - "/bin/base32", "/bin/base64", - "/bin/basename", - "/bin/bc", - "/bin/beep", - "/bin/blkdiscard", - "/bin/blkid", - "/bin/blockdev", - "/bin/bootchartd", - "/bin/brctl", - "/bin/bunzip2", + "/bin/bbconfig", "/bin/busybox", - "/bin/bzcat", - "/bin/bzip2", - "/bin/cal", "/bin/cat", - "/bin/chat", "/bin/chattr", "/bin/chgrp", "/bin/chmod", "/bin/chown", - "/bin/chpasswd", - "/bin/chpst", - "/bin/chroot", - "/bin/chrt", - "/bin/chvt", - "/bin/cksum", - "/bin/clear", - "/bin/cmp", - "/bin/comm", - "/bin/conspy", "/bin/cp", - "/bin/cpio", - "/bin/crc32", - "/bin/crond", - "/bin/crontab", - "/bin/cryptpw", - "/bin/cttyhack", - "/bin/cut", "/bin/date", - "/bin/dc", "/bin/dd", - "/bin/deallocvt", - "/bin/delgroup", - "/bin/deluser", - "/bin/depmod", - "/bin/devmem", "/bin/df", - "/bin/dhcprelay", - "/bin/diff", - "/bin/dirname", "/bin/dmesg", - "/bin/dnsd", "/bin/dnsdomainname", - "/bin/dos2unix", - "/bin/dpkg", - "/bin/dpkg-deb", - "/bin/du", "/bin/dumpkmap", - "/bin/dumpleases", "/bin/echo", "/bin/ed", "/bin/egrep", - "/bin/eject", - "/bin/env", - "/bin/envdir", - "/bin/envuidgid", - "/bin/ether-wake", - "/bin/expand", - "/bin/expr", - "/bin/factor", - "/bin/fakeidentd", - "/bin/fallocate", "/bin/false", "/bin/fatattr", - "/bin/fbset", - "/bin/fbsplash", "/bin/fdflush", - "/bin/fdformat", - "/bin/fdisk", - "/bin/fgconsole", "/bin/fgrep", - "/bin/find", - "/bin/findfs", - "/bin/flock", - "/bin/fold", - "/bin/free", - "/bin/freeramdisk", - "/bin/fsck", - "/bin/fsck.minix", - "/bin/fsfreeze", - "/bin/fstrim", "/bin/fsync", - "/bin/ftpd", - "/bin/ftpget", - "/bin/ftpput", - "/bin/fuser", - "/bin/getconf", "/bin/getopt", - "/bin/getty", "/bin/grep", - "/bin/groups", "/bin/gunzip", "/bin/gzip", - "/bin/halt", - "/bin/hd", - "/bin/hdparm", - "/bin/head", - "/bin/hexdump", - "/bin/hexedit", - "/bin/hostid", "/bin/hostname", - "/bin/httpd", - "/bin/hush", - "/bin/hwclock", - "/bin/i2cdetect", - "/bin/i2cdump", - "/bin/i2cget", - "/bin/i2cset", - "/bin/i2ctransfer", - "/bin/id", - "/bin/ifconfig", - "/bin/ifdown", - "/bin/ifenslave", - "/bin/ifplugd", - "/bin/ifup", - "/bin/inetd", - "/bin/init", - "/bin/insmod", - "/bin/install", "/bin/ionice", "/bin/iostat", - "/bin/ip", - "/bin/ipaddr", "/bin/ipcalc", - "/bin/ipcrm", - "/bin/ipcs", - "/bin/iplink", - "/bin/ipneigh", - "/bin/iproute", - "/bin/iprule", - "/bin/iptunnel", "/bin/kbd_mode", "/bin/kill", - "/bin/killall", - "/bin/killall5", - "/bin/klogd", - "/bin/last", - "/bin/less", "/bin/link", "/bin/linux32", "/bin/linux64", - "/bin/linuxrc", "/bin/ln", - "/bin/loadfont", - "/bin/loadkmap", - "/bin/logger", "/bin/login", - "/bin/logname", - "/bin/logread", - "/bin/losetup", - "/bin/lpd", - "/bin/lpq", - "/bin/lpr", "/bin/ls", "/bin/lsattr", - "/bin/lsmod", - "/bin/lsof", - "/bin/lspci", - "/bin/lsscsi", - "/bin/lsusb", - "/bin/lzcat", - "/bin/lzma", "/bin/lzop", - "/bin/makedevs", "/bin/makemime", - "/bin/man", - "/bin/md5sum", - "/bin/mdev", - "/bin/mesg", - "/bin/microcom", - "/bin/mim", "/bin/mkdir", - "/bin/mkdosfs", - "/bin/mke2fs", - "/bin/mkfifo", - "/bin/mkfs.ext2", - "/bin/mkfs.minix", - "/bin/mkfs.vfat", "/bin/mknod", - "/bin/mkpasswd", - "/bin/mkswap", "/bin/mktemp", - "/bin/modinfo", - "/bin/modprobe", "/bin/more", "/bin/mount", "/bin/mountpoint", "/bin/mpstat", - "/bin/mt", "/bin/mv", - "/bin/nameif", - "/bin/nanddump", - "/bin/nandwrite", - "/bin/nbd-client", - "/bin/nc", "/bin/netstat", "/bin/nice", - "/bin/nl", - "/bin/nmeter", - "/bin/nohup", - "/bin/nologin", - "/bin/nproc", - "/bin/nsenter", - "/bin/nslookup", - "/bin/ntpd", - "/bin/od", - "/bin/openvt", - "/bin/partprobe", - "/bin/passwd", - "/bin/paste", - "/bin/patch", - "/bin/pgrep", "/bin/pidof", "/bin/ping", "/bin/ping6", "/bin/pipe_progress", - "/bin/pivot_root", - "/bin/pkill", - "/bin/pmap", - "/bin/popmaildir", - "/bin/poweroff", - "/bin/powertop", "/bin/printenv", - "/bin/printf", "/bin/ps", - "/bin/pscan", - "/bin/pstree", "/bin/pwd", - "/bin/pwdx", - "/bin/raidautorun", - "/bin/rdate", - "/bin/rdev", - "/bin/readahead", - "/bin/readlink", - "/bin/readprofile", - "/bin/realpath", - "/bin/reboot", "/bin/reformime", - "/bin/remove-shell", - "/bin/renice", - "/bin/reset", - "/bin/resize", - "/bin/resume", "/bin/rev", "/bin/rm", "/bin/rmdir", - "/bin/rmmod", - "/bin/route", - "/bin/rpm", - "/bin/rpm2cpio", - "/bin/rtcwake", - "/bin/run-init", "/bin/run-parts", - "/bin/runlevel", - "/bin/runsv", - "/bin/runsvdir", - "/bin/rx", - "/bin/script", - "/bin/scriptreplay", "/bin/sed", - "/bin/sendmail", - "/bin/seq", - "/bin/setarch", - "/bin/setconsole", - "/bin/setfattr", - "/bin/setfont", - "/bin/setkeycodes", - "/bin/setlogcons", "/bin/setpriv", "/bin/setserial", - "/bin/setsid", - "/bin/setuidgid", "/bin/sh", - "/bin/sha1sum", - "/bin/sha256sum", - "/bin/sha3sum", - "/bin/sha512sum", - "/bin/showkey", - "/bin/shred", - "/bin/shuf", - "/bin/slattach", "/bin/sleep", - "/bin/smemcap", - "/bin/softlimit", - "/bin/sort", - "/bin/split", - "/bin/ssl_client", - "/bin/start-stop-daemon", "/bin/stat", - "/bin/strings", "/bin/stty", "/bin/su", - "/bin/sulogin", - "/bin/sum", - "/bin/sv", - "/bin/svc", - "/bin/svlogd", - "/bin/svok", - "/bin/swapoff", - "/bin/swapon", - "/bin/switch_root", "/bin/sync", - "/bin/sysctl", - "/bin/syslogd", - "/bin/tac", - "/bin/tail", "/bin/tar", - "/bin/taskset", - "/bin/tc", - "/bin/tcpsvd", - "/bin/tee", - "/bin/telnet", - "/bin/telnetd", - "/bin/test", - "/bin/tftp", - "/bin/tftpd", - "/bin/time", - "/bin/timeout", - "/bin/top", "/bin/touch", - "/bin/tr", - "/bin/traceroute", - "/bin/traceroute6", "/bin/true", - "/bin/truncate", - "/bin/ts", - "/bin/tty", - "/bin/ttysize", - "/bin/tunctl", - "/bin/ubiattach", - "/bin/ubidetach", - "/bin/ubimkvol", - "/bin/ubirename", - "/bin/ubirmvol", - "/bin/ubirsvol", - "/bin/ubiupdatevol", - "/bin/udhcpc", - "/bin/udhcpc6", - "/bin/udhcpd", - "/bin/udpsvd", - "/bin/uevent", "/bin/umount", "/bin/uname", - "/bin/unexpand", - "/bin/uniq", - "/bin/unix2dos", - "/bin/unlink", - "/bin/unlzma", - "/bin/unshare", - "/bin/unxz", - "/bin/unzip", - "/bin/uptime", - "/bin/users", "/bin/usleep", - "/bin/uudecode", - "/bin/uuencode", - "/bin/vconfig", - "/bin/vi", - "/bin/vlock", - "/bin/volname", - "/bin/w", - "/bin/wall", "/bin/watch", - "/bin/watchdog", - "/bin/wc", - "/bin/wget", - "/bin/which", - "/bin/who", - "/bin/whoami", - "/bin/whois", - "/bin/xargs", - "/bin/xxd", - "/bin/xz", - "/bin/xzcat", - "/bin/yes", "/bin/zcat", - "/bin/zcip", - "/dev", - "/etc", - "/etc/group", - "/etc/localtime", - "/etc/network", - "/etc/network/if-down.d", - "/etc/network/if-post-down.d", - "/etc/network/if-pre-up.d", - "/etc/network/if-up.d", - "/etc/passwd", - "/etc/shadow", - "/home", - "/proc", - "/root", - "/sys", - "/tmp", - "/usr", - "/usr/sbin", - "/var", - "/var/spool", - "/var/spool/mail", - "/var/www", + "/lib", + "/lib/apk", + "/lib/apk/db", + "/lib/apk/db/installed", + "/lib/apk/db/lock", + "/lib/apk/db/scripts.tar", + "/lib/apk/db/triggers", + "/lib/apk/exec", + "/lib/firmware", + "/lib/ld-musl-x86_64.so.1", + "/lib/libapk.so.3.12.0", + "/lib/libc.musl-x86_64.so.1", + "/lib/libcrypto.so.3", + "/lib/libssl.so.3", + "/lib/libz.so.1", + "/lib/libz.so.1.2.13", + "/lib/mdev", + "/lib/modules-load.d", + "/lib/sysctl.d", + "/lib/sysctl.d/00-alpine.conf", } + // depending on how the image is built (either from linux or mac), sys and proc might accidentally be added to the image. + // this isn't important for the test, so we remove them. + paths.Remove("/proc", "/sys", "/dev", "/etc") + pathsList := paths.List() sort.Strings(pathsList) diff --git a/syft/source/image_squash_resolver_test.go b/syft/source/image_squash_resolver_test.go index d614ce83b4b..cfbeff1677f 100644 --- a/syft/source/image_squash_resolver_test.go +++ b/syft/source/image_squash_resolver_test.go @@ -663,7 +663,7 @@ func compareLocations(t *testing.T, expected, actual []Location) { } func TestSquashResolver_AllLocations(t *testing.T) { - img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") + img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") resolver, err := newImageSquashResolver(img) assert.NoError(t, err) @@ -673,446 +673,16 @@ func TestSquashResolver_AllLocations(t *testing.T) { paths.Add(loc.RealPath) } expected := []string{ - "/file-1.txt", - "/file-2.txt", + "/Dockerfile", "/file-3.txt", - "/link-1", - "/link-2", - "/link-dead", - "/link-indirect", - "/link-within", - "/parent", - "/parent-link", - "/parent/file-4.txt", - - // from base image... - "/bin", - "/bin/[", - "/bin/[[", - "/bin/acpid", - "/bin/add-shell", - "/bin/addgroup", - "/bin/adduser", - "/bin/adjtimex", - "/bin/ar", - "/bin/arch", - "/bin/arp", - "/bin/arping", - "/bin/ascii", - "/bin/ash", - "/bin/awk", - "/bin/base32", - "/bin/base64", - "/bin/basename", - "/bin/bc", - "/bin/beep", - "/bin/blkdiscard", - "/bin/blkid", - "/bin/blockdev", - "/bin/bootchartd", - "/bin/brctl", - "/bin/bunzip2", - "/bin/busybox", - "/bin/bzcat", - "/bin/bzip2", - "/bin/cal", - "/bin/cat", - "/bin/chat", - "/bin/chattr", - "/bin/chgrp", - "/bin/chmod", - "/bin/chown", - "/bin/chpasswd", - "/bin/chpst", - "/bin/chroot", - "/bin/chrt", - "/bin/chvt", - "/bin/cksum", - "/bin/clear", - "/bin/cmp", - "/bin/comm", - "/bin/conspy", - "/bin/cp", - "/bin/cpio", - "/bin/crc32", - "/bin/crond", - "/bin/crontab", - "/bin/cryptpw", - "/bin/cttyhack", - "/bin/cut", - "/bin/date", - "/bin/dc", - "/bin/dd", - "/bin/deallocvt", - "/bin/delgroup", - "/bin/deluser", - "/bin/depmod", - "/bin/devmem", - "/bin/df", - "/bin/dhcprelay", - "/bin/diff", - "/bin/dirname", - "/bin/dmesg", - "/bin/dnsd", - "/bin/dnsdomainname", - "/bin/dos2unix", - "/bin/dpkg", - "/bin/dpkg-deb", - "/bin/du", - "/bin/dumpkmap", - "/bin/dumpleases", - "/bin/echo", - "/bin/ed", - "/bin/egrep", - "/bin/eject", - "/bin/env", - "/bin/envdir", - "/bin/envuidgid", - "/bin/ether-wake", - "/bin/expand", - "/bin/expr", - "/bin/factor", - "/bin/fakeidentd", - "/bin/fallocate", - "/bin/false", - "/bin/fatattr", - "/bin/fbset", - "/bin/fbsplash", - "/bin/fdflush", - "/bin/fdformat", - "/bin/fdisk", - "/bin/fgconsole", - "/bin/fgrep", - "/bin/find", - "/bin/findfs", - "/bin/flock", - "/bin/fold", - "/bin/free", - "/bin/freeramdisk", - "/bin/fsck", - "/bin/fsck.minix", - "/bin/fsfreeze", - "/bin/fstrim", - "/bin/fsync", - "/bin/ftpd", - "/bin/ftpget", - "/bin/ftpput", - "/bin/fuser", - "/bin/getconf", - "/bin/getopt", - "/bin/getty", - "/bin/grep", - "/bin/groups", - "/bin/gunzip", - "/bin/gzip", - "/bin/halt", - "/bin/hd", - "/bin/hdparm", - "/bin/head", - "/bin/hexdump", - "/bin/hexedit", - "/bin/hostid", - "/bin/hostname", - "/bin/httpd", - "/bin/hush", - "/bin/hwclock", - "/bin/i2cdetect", - "/bin/i2cdump", - "/bin/i2cget", - "/bin/i2cset", - "/bin/i2ctransfer", - "/bin/id", - "/bin/ifconfig", - "/bin/ifdown", - "/bin/ifenslave", - "/bin/ifplugd", - "/bin/ifup", - "/bin/inetd", - "/bin/init", - "/bin/insmod", - "/bin/install", - "/bin/ionice", - "/bin/iostat", - "/bin/ip", - "/bin/ipaddr", - "/bin/ipcalc", - "/bin/ipcrm", - "/bin/ipcs", - "/bin/iplink", - "/bin/ipneigh", - "/bin/iproute", - "/bin/iprule", - "/bin/iptunnel", - "/bin/kbd_mode", - "/bin/kill", - "/bin/killall", - "/bin/killall5", - "/bin/klogd", - "/bin/last", - "/bin/less", - "/bin/link", - "/bin/linux32", - "/bin/linux64", - "/bin/linuxrc", - "/bin/ln", - "/bin/loadfont", - "/bin/loadkmap", - "/bin/logger", - "/bin/login", - "/bin/logname", - "/bin/logread", - "/bin/losetup", - "/bin/lpd", - "/bin/lpq", - "/bin/lpr", - "/bin/ls", - "/bin/lsattr", - "/bin/lsmod", - "/bin/lsof", - "/bin/lspci", - "/bin/lsscsi", - "/bin/lsusb", - "/bin/lzcat", - "/bin/lzma", - "/bin/lzop", - "/bin/makedevs", - "/bin/makemime", - "/bin/man", - "/bin/md5sum", - "/bin/mdev", - "/bin/mesg", - "/bin/microcom", - "/bin/mim", - "/bin/mkdir", - "/bin/mkdosfs", - "/bin/mke2fs", - "/bin/mkfifo", - "/bin/mkfs.ext2", - "/bin/mkfs.minix", - "/bin/mkfs.vfat", - "/bin/mknod", - "/bin/mkpasswd", - "/bin/mkswap", - "/bin/mktemp", - "/bin/modinfo", - "/bin/modprobe", - "/bin/more", - "/bin/mount", - "/bin/mountpoint", - "/bin/mpstat", - "/bin/mt", - "/bin/mv", - "/bin/nameif", - "/bin/nanddump", - "/bin/nandwrite", - "/bin/nbd-client", - "/bin/nc", - "/bin/netstat", - "/bin/nice", - "/bin/nl", - "/bin/nmeter", - "/bin/nohup", - "/bin/nologin", - "/bin/nproc", - "/bin/nsenter", - "/bin/nslookup", - "/bin/ntpd", - "/bin/od", - "/bin/openvt", - "/bin/partprobe", - "/bin/passwd", - "/bin/paste", - "/bin/patch", - "/bin/pgrep", - "/bin/pidof", - "/bin/ping", - "/bin/ping6", - "/bin/pipe_progress", - "/bin/pivot_root", - "/bin/pkill", - "/bin/pmap", - "/bin/popmaildir", - "/bin/poweroff", - "/bin/powertop", - "/bin/printenv", - "/bin/printf", - "/bin/ps", - "/bin/pscan", - "/bin/pstree", - "/bin/pwd", - "/bin/pwdx", - "/bin/raidautorun", - "/bin/rdate", - "/bin/rdev", - "/bin/readahead", - "/bin/readlink", - "/bin/readprofile", - "/bin/realpath", - "/bin/reboot", - "/bin/reformime", - "/bin/remove-shell", - "/bin/renice", - "/bin/reset", - "/bin/resize", - "/bin/resume", - "/bin/rev", - "/bin/rm", - "/bin/rmdir", - "/bin/rmmod", - "/bin/route", - "/bin/rpm", - "/bin/rpm2cpio", - "/bin/rtcwake", - "/bin/run-init", - "/bin/run-parts", - "/bin/runlevel", - "/bin/runsv", - "/bin/runsvdir", - "/bin/rx", - "/bin/script", - "/bin/scriptreplay", - "/bin/sed", - "/bin/sendmail", - "/bin/seq", - "/bin/setarch", - "/bin/setconsole", - "/bin/setfattr", - "/bin/setfont", - "/bin/setkeycodes", - "/bin/setlogcons", - "/bin/setpriv", - "/bin/setserial", - "/bin/setsid", - "/bin/setuidgid", - "/bin/sh", - "/bin/sha1sum", - "/bin/sha256sum", - "/bin/sha3sum", - "/bin/sha512sum", - "/bin/showkey", - "/bin/shred", - "/bin/shuf", - "/bin/slattach", - "/bin/sleep", - "/bin/smemcap", - "/bin/softlimit", - "/bin/sort", - "/bin/split", - "/bin/ssl_client", - "/bin/start-stop-daemon", - "/bin/stat", - "/bin/strings", - "/bin/stty", - "/bin/su", - "/bin/sulogin", - "/bin/sum", - "/bin/sv", - "/bin/svc", - "/bin/svlogd", - "/bin/svok", - "/bin/swapoff", - "/bin/swapon", - "/bin/switch_root", - "/bin/sync", - "/bin/sysctl", - "/bin/syslogd", - "/bin/tac", - "/bin/tail", - "/bin/tar", - "/bin/taskset", - "/bin/tc", - "/bin/tcpsvd", - "/bin/tee", - "/bin/telnet", - "/bin/telnetd", - "/bin/test", - "/bin/tftp", - "/bin/tftpd", - "/bin/time", - "/bin/timeout", - "/bin/top", - "/bin/touch", - "/bin/tr", - "/bin/traceroute", - "/bin/traceroute6", - "/bin/true", - "/bin/truncate", - "/bin/ts", - "/bin/tty", - "/bin/ttysize", - "/bin/tunctl", - "/bin/ubiattach", - "/bin/ubidetach", - "/bin/ubimkvol", - "/bin/ubirename", - "/bin/ubirmvol", - "/bin/ubirsvol", - "/bin/ubiupdatevol", - "/bin/udhcpc", - "/bin/udhcpc6", - "/bin/udhcpd", - "/bin/udpsvd", - "/bin/uevent", - "/bin/umount", - "/bin/uname", - "/bin/unexpand", - "/bin/uniq", - "/bin/unix2dos", - "/bin/unlink", - "/bin/unlzma", - "/bin/unshare", - "/bin/unxz", - "/bin/unzip", - "/bin/uptime", - "/bin/users", - "/bin/usleep", - "/bin/uudecode", - "/bin/uuencode", - "/bin/vconfig", - "/bin/vi", - "/bin/vlock", - "/bin/volname", - "/bin/w", - "/bin/wall", - "/bin/watch", - "/bin/watchdog", - "/bin/wc", - "/bin/wget", - "/bin/which", - "/bin/who", - "/bin/whoami", - "/bin/whois", - "/bin/xargs", - "/bin/xxd", - "/bin/xz", - "/bin/xzcat", - "/bin/yes", - "/bin/zcat", - "/bin/zcip", - "/dev", - "/etc", - "/etc/group", - "/etc/localtime", - "/etc/network", - "/etc/network/if-down.d", - "/etc/network/if-post-down.d", - "/etc/network/if-pre-up.d", - "/etc/network/if-up.d", - "/etc/passwd", - "/etc/shadow", - "/home", - "/proc", - "/root", - "/sys", - "/tmp", - "/usr", - "/usr/sbin", - "/var", - "/var/spool", - "/var/spool/mail", - "/var/www", + "/target", + "/target/file-2.txt", } + // depending on how the image is built (either from linux or mac), sys and proc might accidentally be added to the image. + // this isn't important for the test, so we remove them. + paths.Remove("/proc", "/sys", "/dev", "/etc") + pathsList := paths.List() sort.Strings(pathsList) diff --git a/syft/source/test-fixtures/image-files-deleted/Dockerfile b/syft/source/test-fixtures/image-files-deleted/Dockerfile new file mode 100644 index 00000000000..5c5755194f3 --- /dev/null +++ b/syft/source/test-fixtures/image-files-deleted/Dockerfile @@ -0,0 +1,6 @@ +FROM alpine:3.17.1 as tools +FROM scratch +COPY --from=tools /bin /bin +COPY --from=tools /lib /lib +ADD . . +RUN rm -rf file-1.txt /bin /lib diff --git a/syft/source/test-fixtures/image-files-deleted/file-1.txt b/syft/source/test-fixtures/image-files-deleted/file-1.txt new file mode 100644 index 00000000000..985d3408e98 --- /dev/null +++ b/syft/source/test-fixtures/image-files-deleted/file-1.txt @@ -0,0 +1 @@ +this file has contents \ No newline at end of file diff --git a/syft/source/test-fixtures/image-files-deleted/file-3.txt b/syft/source/test-fixtures/image-files-deleted/file-3.txt new file mode 120000 index 00000000000..4036f0fd4d3 --- /dev/null +++ b/syft/source/test-fixtures/image-files-deleted/file-3.txt @@ -0,0 +1 @@ +file-1.txt \ No newline at end of file diff --git a/syft/source/test-fixtures/image-files-deleted/target/file-2.txt b/syft/source/test-fixtures/image-files-deleted/target/file-2.txt new file mode 100644 index 00000000000..396d08bbc72 --- /dev/null +++ b/syft/source/test-fixtures/image-files-deleted/target/file-2.txt @@ -0,0 +1 @@ +file-2 contents! \ No newline at end of file From b92999b5dddbca38d8d9edcf81da4edef7a023e3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sun, 5 Feb 2023 18:36:10 -0500 Subject: [PATCH 48/54] fix test fixture reference Signed-off-by: Alex Goodman --- syft/pkg/cataloger/alpm/parse_alpm_db_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db_test.go b/syft/pkg/cataloger/alpm/parse_alpm_db_test.go index dcb458e40f9..907b2aa36e2 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db_test.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db_test.go @@ -92,7 +92,7 @@ func TestDatabaseParser(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - f, err := os.Open("test-fixtures/files") + f, err := os.Open(test.fixture) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, f.Close()) }) From 473f51f1d7322b19917cfb262c0a1d2fe5fa34dd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 7 Feb 2023 11:10:10 -0500 Subject: [PATCH 49/54] rename file.Type Signed-off-by: Alex Goodman --- go.mod | 2 ++ syft/file/all_regular_files.go | 2 +- syft/file/digest_cataloger.go | 2 +- syft/file/metadata_cataloger_test.go | 8 ++++---- syft/formats/syftjson/encoder_test.go | 8 ++++---- syft/formats/syftjson/to_format_model.go | 8 ++++---- syft/formats/syftjson/to_format_model_test.go | 8 ++++---- syft/source/directory_indexer.go | 16 +++++++++++----- syft/source/directory_resolver.go | 2 +- syft/source/image_all_layers_resolver.go | 6 +++--- syft/source/image_squash_resolver.go | 4 ++-- syft/source/mock_resolver.go | 4 ++-- 12 files changed, 39 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 8f398175989..3c40fe547e5 100644 --- a/go.mod +++ b/go.mod @@ -158,3 +158,5 @@ retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) + +replace github.com/anchore/stereoscope => ../stereoscope diff --git a/syft/file/all_regular_files.go b/syft/file/all_regular_files.go index 313c1e91442..5dcf8974430 100644 --- a/syft/file/all_regular_files.go +++ b/syft/file/all_regular_files.go @@ -21,7 +21,7 @@ func allRegularFiles(resolver source.FileResolver) (locations []source.Location) continue } - if metadata.Type != file.TypeReg { + if metadata.Type != file.TypeRegular { continue } locations = append(locations, resolvedLocation) diff --git a/syft/file/digest_cataloger.go b/syft/file/digest_cataloger.go index 5b3bf09c84f..1ce45b1d722 100644 --- a/syft/file/digest_cataloger.go +++ b/syft/file/digest_cataloger.go @@ -66,7 +66,7 @@ func (i *DigestsCataloger) catalogLocation(resolver source.FileResolver, locatio } // we should only attempt to report digests for files that are regular files (don't attempt to resolve links) - if meta.Type != file.TypeReg { + if meta.Type != file.TypeRegular { return nil, errUndigestableFile } diff --git a/syft/file/metadata_cataloger_test.go b/syft/file/metadata_cataloger_test.go index c29b80a2598..5166857ba42 100644 --- a/syft/file/metadata_cataloger_test.go +++ b/syft/file/metadata_cataloger_test.go @@ -53,7 +53,7 @@ func TestFileMetadataCataloger(t *testing.T) { expected: source.FileMetadata{ Path: "/file-1.txt", Mode: 0644, - Type: file.TypeReg, + Type: file.TypeRegular, UserID: 1, GroupID: 2, Size: 7, @@ -79,7 +79,7 @@ func TestFileMetadataCataloger(t *testing.T) { expected: source.FileMetadata{ Path: "/symlink-1", Mode: 0777 | os.ModeSymlink, - Type: file.TypeSymlink, + Type: file.TypeSymLink, LinkDestination: "file-1.txt", UserID: 0, GroupID: 0, @@ -116,7 +116,7 @@ func TestFileMetadataCataloger(t *testing.T) { expected: source.FileMetadata{ Path: "/fifo-1", Mode: 0644 | os.ModeNamedPipe, - Type: file.TypeFifo, + Type: file.TypeFIFO, UserID: 0, GroupID: 0, MIMEType: "", @@ -128,7 +128,7 @@ func TestFileMetadataCataloger(t *testing.T) { expected: source.FileMetadata{ Path: "/bin", Mode: 0755 | os.ModeDir, - Type: file.TypeDir, + Type: file.TypeDirectory, UserID: 0, GroupID: 0, MIMEType: "", diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 433590e2420..3c0ee8c54f4 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -108,26 +108,26 @@ func TestEncodeFullJSONDocument(t *testing.T) { FileMetadata: map[source.Coordinates]source.FileMetadata{ source.NewLocation("/a/place").Coordinates: { Mode: 0775, - Type: stereoFile.TypeDir, + Type: stereoFile.TypeDirectory, UserID: 0, GroupID: 0, }, source.NewLocation("/a/place/a").Coordinates: { Mode: 0775, - Type: stereoFile.TypeReg, + Type: stereoFile.TypeRegular, UserID: 0, GroupID: 0, }, source.NewLocation("/b").Coordinates: { Mode: 0775, - Type: stereoFile.TypeSymlink, + Type: stereoFile.TypeSymLink, LinkDestination: "/c", UserID: 0, GroupID: 0, }, source.NewLocation("/b/place/b").Coordinates: { Mode: 0644, - Type: stereoFile.TypeReg, + Type: stereoFile.TypeRegular, UserID: 1, GroupID: 2, }, diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index ba025272b46..e81a1a197d2 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -148,11 +148,11 @@ func toFileMetadataEntry(coordinates source.Coordinates, metadata *source.FileMe func toFileType(ty stereoscopeFile.Type) string { switch ty { - case stereoscopeFile.TypeSymlink: + case stereoscopeFile.TypeSymLink: return "SymbolicLink" case stereoscopeFile.TypeHardLink: return "HardLink" - case stereoscopeFile.TypeDir: + case stereoscopeFile.TypeDirectory: return "Directory" case stereoscopeFile.TypeSocket: return "Socket" @@ -160,9 +160,9 @@ func toFileType(ty stereoscopeFile.Type) string { return "BlockDevice" case stereoscopeFile.TypeCharacterDevice: return "CharacterDevice" - case stereoscopeFile.TypeFifo: + case stereoscopeFile.TypeFIFO: return "FIFONode" - case stereoscopeFile.TypeReg: + case stereoscopeFile.TypeRegular: return "RegularFile" case stereoscopeFile.TypeIrregular: return "IrregularFile" diff --git a/syft/formats/syftjson/to_format_model_test.go b/syft/formats/syftjson/to_format_model_test.go index 6811e6ac90f..2813f0e36b7 100644 --- a/syft/formats/syftjson/to_format_model_test.go +++ b/syft/formats/syftjson/to_format_model_test.go @@ -109,15 +109,15 @@ func Test_toFileType(t *testing.T) { name string }{ { - ty: file.TypeReg, + ty: file.TypeRegular, name: "RegularFile", }, { - ty: file.TypeDir, + ty: file.TypeDirectory, name: "Directory", }, { - ty: file.TypeSymlink, + ty: file.TypeSymLink, name: "SymbolicLink", }, { @@ -137,7 +137,7 @@ func Test_toFileType(t *testing.T) { name: "BlockDevice", }, { - ty: file.TypeFifo, + ty: file.TypeFIFO, name: "FIFONode", }, { diff --git a/syft/source/directory_indexer.go b/syft/source/directory_indexer.go index d9c728c1cca..4f414a670ae 100644 --- a/syft/source/directory_indexer.go +++ b/syft/source/directory_indexer.go @@ -42,7 +42,13 @@ func newDirectoryIndexer(path, base string, visitors ...pathIndexVisitor) *direc } // these additional stateful visitors should be the first thing considered when walking / indexing - i.pathIndexVisitors = append([]pathIndexVisitor{i.disallowRevisitingVisitor, i.disallowFileAccessErr}, i.pathIndexVisitors...) + i.pathIndexVisitors = append( + []pathIndexVisitor{ + i.disallowRevisitingVisitor, + i.disallowFileAccessErr, + }, + i.pathIndexVisitors..., + ) return i } @@ -192,11 +198,11 @@ func (r *directoryIndexer) isFileAccessErr(path string, err error) bool { func (r directoryIndexer) addPathToIndex(p string, info os.FileInfo) (string, error) { switch t := file.TypeFromMode(info.Mode()); t { - case file.TypeSymlink: + case file.TypeSymLink: return r.addSymlinkToIndex(p, info) - case file.TypeDir: + case file.TypeDirectory: return "", r.addDirectoryToIndex(p, info) - case file.TypeReg: + case file.TypeRegular: return "", r.addFileToIndex(p, info) default: return "", fmt.Errorf("unsupported file type: %s", t) @@ -323,7 +329,7 @@ func disallowByFileType(_ string, info os.FileInfo, _ error) error { return nil } switch file.TypeFromMode(info.Mode()) { - case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFifo, file.TypeIrregular: + case file.TypeCharacterDevice, file.TypeSocket, file.TypeBlockDevice, file.TypeFIFO, file.TypeIrregular: return errSkipPath // note: symlinks that point to these files may still get by. // We handle this later in processing to help prevent against infinite links traversal. diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index c72795b7c9b..5592aa4c247 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -276,7 +276,7 @@ func (r directoryResolver) FileContentsByLocation(location Location) (io.ReadClo } // don't consider directories - if entry.Type == file.TypeDir { + if entry.Type == file.TypeDirectory { return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath) } diff --git a/syft/source/image_all_layers_resolver.go b/syft/source/image_all_layers_resolver.go index 3d5bc0e80a6..ca40b12718c 100644 --- a/syft/source/image_all_layers_resolver.go +++ b/syft/source/image_all_layers_resolver.go @@ -55,7 +55,7 @@ func (r *imageAllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs fil return nil, fmt.Errorf("unable to fetch metadata (ref=%+v): %w", ref, err) } - if entry.Metadata.Type == file.TypeHardLink || entry.Metadata.Type == file.TypeSymlink { + if entry.Metadata.Type == file.TypeHardLink || entry.Metadata.Type == file.TypeSymLink { // a link may resolve in this layer or higher, assuming a squashed tree is used to search // we should search all possible resolutions within the valid source for _, subLayerIdx := range r.layers[layerIdx:] { @@ -190,7 +190,7 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R } switch entry.Metadata.Type { - case file.TypeSymlink, file.TypeHardLink: + case file.TypeSymLink, file.TypeHardLink: // the location we are searching may be a symlink, we should always work with the resolved file newLocation := r.RelativeFileByPath(location, location.VirtualPath) if newLocation == nil { @@ -198,7 +198,7 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R return nil, fmt.Errorf("no contents for location=%q", location.VirtualPath) } location = *newLocation - case file.TypeDir: + case file.TypeDirectory: return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath) } diff --git a/syft/source/image_squash_resolver.go b/syft/source/image_squash_resolver.go index c2cc9408947..d62927b309c 100644 --- a/syft/source/image_squash_resolver.go +++ b/syft/source/image_squash_resolver.go @@ -149,7 +149,7 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read } switch entry.Metadata.Type { - case file.TypeSymlink, file.TypeHardLink: + case file.TypeSymLink, file.TypeHardLink: // the location we are searching may be a symlink, we should always work with the resolved file locations, err := r.FilesByPath(location.RealPath) if err != nil { @@ -164,7 +164,7 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read default: return nil, fmt.Errorf("link resolution resulted in multiple results while resolving content location: %+v", location) } - case file.TypeDir: + case file.TypeDirectory: return nil, fmt.Errorf("unable to get file contents for directory: %+v", location) } diff --git a/syft/source/mock_resolver.go b/syft/source/mock_resolver.go index e0b624b8cc2..aad47abd94d 100644 --- a/syft/source/mock_resolver.go +++ b/syft/source/mock_resolver.go @@ -161,9 +161,9 @@ func (r MockResolver) FileMetadataByLocation(l Location) (FileMetadata, error) { } // other types not supported - ty := file.TypeReg + ty := file.TypeRegular if info.IsDir() { - ty = file.TypeDir + ty = file.TypeDirectory } return FileMetadata{ From ab21f2ebb39007bc2bedfd806a1dbd391e092927 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 7 Feb 2023 11:11:52 -0500 Subject: [PATCH 50/54] bump stereoscope Signed-off-by: Alex Goodman --- go.mod | 4 +--- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 3c40fe547e5..70b231da745 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5 + github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639 github.com/docker/docker v23.0.0+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 @@ -158,5 +158,3 @@ retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) - -replace github.com/anchore/stereoscope => ../stereoscope diff --git a/go.sum b/go.sum index dbc9d7fcd46..20f5fea52e6 100644 --- a/go.sum +++ b/go.sum @@ -90,10 +90,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5 h1:UaK2lgDh5u2wVQcDNIQMVLI+5z2ONwY/5tAs2L3+Wr8= -github.com/anchore/stereoscope v0.0.0-20230205154733-b0fc17226ef5/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= -github.com/anchore/stereoscope v0.0.0-20230203152723-c49244e4d66f h1:hyEFgDzqZRr/+q1cPfjgIKXWJ7lMHDHmDXAOrhKMhRA= -github.com/anchore/stereoscope v0.0.0-20230203152723-c49244e4d66f/go.mod h1:YerDPu5voTWZUmjrAHhak7gGGdGLilqroEEFLA/PUHo= +github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639 h1:VcszzRFjzTAWuoBsq+5bC1chdTHEPzNopMOSoTYFlRA= +github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= 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= From 70e7c49e1b4ae6be3c84a7603af3dfa6525604d7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 7 Feb 2023 19:39:05 -0500 Subject: [PATCH 51/54] fix PR comment to exclude extra * Signed-off-by: Alex Goodman --- syft/pkg/cataloger/deb/cataloger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index 0e8f597e4fc..fb25550c404 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -13,6 +13,6 @@ const catalogerName = "dpkgdb-cataloger" func NewDpkgdbCataloger() *generic.Cataloger { return generic.NewCataloger(catalogerName). // note: these globs have been intentionally split up in order to improve search performance, - // please do NOT combine into: "**/var/lib/dpkg/{status,status.d/**}" + // please do NOT combine into: "**/var/lib/dpkg/{status,status.d/*}" WithParserByGlobs(parseDpkgDB, "**/var/lib/dpkg/status", "**/var/lib/dpkg/status.d/*") } From 5fb636b8dbd5598d9a40aa30210a2fa17fba43e4 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Feb 2023 10:41:42 -0500 Subject: [PATCH 52/54] bump to dev version of stereoscope Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 70b231da745..3e95a548142 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639 + github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002 github.com/docker/docker v23.0.0+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 diff --git a/go.sum b/go.sum index 20f5fea52e6..d064f2bb0eb 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639 h1:VcszzRFjzTAWuoBsq+5bC1chdTHEPzNopMOSoTYFlRA= -github.com/anchore/stereoscope v0.0.0-20230207160434-c5bfa9878639/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= +github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002 h1:optBGSxhGzrZbzFzczNgk0YvmZKBKBwACH0O8mSezEM= +github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= 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= From e99c223151fe830afb2491e86a3883c8834e5181 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Feb 2023 10:48:11 -0500 Subject: [PATCH 53/54] bump to final version of stereoscope Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3e95a548142..fd5a715a757 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 - github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002 + github.com/anchore/stereoscope v0.0.0-20230208154630-5a306f07f2e7 github.com/docker/docker v23.0.0+incompatible github.com/google/go-containerregistry v0.13.0 github.com/invopop/jsonschema v0.7.0 diff --git a/go.sum b/go.sum index d064f2bb0eb..84708ee8b57 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002 h1:optBGSxhGzrZbzFzczNgk0YvmZKBKBwACH0O8mSezEM= -github.com/anchore/stereoscope v0.0.0-20230208153618-90b1ac403002/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= +github.com/anchore/stereoscope v0.0.0-20230208154630-5a306f07f2e7 h1:PrdFBPMyika+AM1/AwDmYqrVeUATDU90wbrd81ugicU= +github.com/anchore/stereoscope v0.0.0-20230208154630-5a306f07f2e7/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw= 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= From f246744fed4d10403b6a04b1ce4d9aa37a1d6af5 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Feb 2023 17:16:49 -0500 Subject: [PATCH 54/54] move observing resolver to pkgtest Signed-off-by: Alex Goodman --- .../internal/{srctest => pkgtest}/observing_resolver.go | 2 +- syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) rename syft/pkg/cataloger/internal/{srctest => pkgtest}/observing_resolver.go (99%) diff --git a/syft/pkg/cataloger/internal/srctest/observing_resolver.go b/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go similarity index 99% rename from syft/pkg/cataloger/internal/srctest/observing_resolver.go rename to syft/pkg/cataloger/internal/pkgtest/observing_resolver.go index b045c5c3a5f..fd0a5428a08 100644 --- a/syft/pkg/cataloger/internal/srctest/observing_resolver.go +++ b/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go @@ -1,4 +1,4 @@ -package srctest +package pkgtest import ( "fmt" diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 325dd0309db..99df368f45e 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -17,7 +17,6 @@ import ( "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" - "github.com/anchore/syft/syft/pkg/cataloger/internal/srctest" "github.com/anchore/syft/syft/source" ) @@ -185,7 +184,7 @@ func (p *CatalogTester) TestParser(t *testing.T, parser generic.Parser) { func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) { t.Helper() - resolver := srctest.NewObservingResolver(p.resolver) + resolver := NewObservingResolver(p.resolver) pkgs, relationships, err := cataloger.Catalog(resolver)