From 10b44f5311767a321404a24f36ec03b3f86d7dfd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 6 Oct 2020 13:11:35 -0400 Subject: [PATCH] split catalogers into two sets, one for images another for directory scans Signed-off-by: Alex Goodman --- Makefile | 2 +- syft/cataloger/bundler/parse_gemfile_lock.go | 2 +- .../bundler/parse_gemfile_lock_test.go | 2 +- syft/cataloger/bundler/parse_gemspec.go | 2 +- syft/cataloger/bundler/parse_gemspec_test.go | 2 +- syft/cataloger/cataloger.go | 24 +++- syft/lib.go | 20 +-- syft/pkg/package_test.go | 5 +- syft/pkg/type.go | 6 +- syft/presenter/cyclonedx/presenter.go | 4 +- syft/presenter/json/location.go | 3 +- syft/presenter/json/source.go | 5 +- syft/presenter/text/presenter.go | 3 +- syft/scope/scope.go | 52 +++---- syft/scope/scope_test.go | 46 +++--- test/integration/json_schema_test.go | 8 ++ test/integration/pkg_cases.go | 136 ++++++++++-------- test/integration/pkg_coverage_test.go | 20 ++- .../ruby/specification/bundler.gemspec | 25 ++++ 19 files changed, 218 insertions(+), 149 deletions(-) create mode 100644 test/integration/test-fixtures/image-pkg-coverage/ruby/specification/bundler.gemspec diff --git a/Makefile b/Makefile index ade92a5e0..e421c5050 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ unit: fixtures ## Run unit tests (with coverage) .PHONY: integration integration: ## Run integration tests $(call title,Running integration tests) - go test -v -tags=integration ./test/integration + go test -tags=integration ./test/integration # note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted integration-fingerprint: diff --git a/syft/cataloger/bundler/parse_gemfile_lock.go b/syft/cataloger/bundler/parse_gemfile_lock.go index d59274289..76e8990e5 100644 --- a/syft/cataloger/bundler/parse_gemfile_lock.go +++ b/syft/cataloger/bundler/parse_gemfile_lock.go @@ -44,7 +44,7 @@ func parseGemfileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error) Name: candidate[0], Version: strings.Trim(candidate[1], "()"), Language: pkg.Ruby, - Type: pkg.BundlerPkg, + Type: pkg.GemPkg, }) } } diff --git a/syft/cataloger/bundler/parse_gemfile_lock_test.go b/syft/cataloger/bundler/parse_gemfile_lock_test.go index 428a80c01..4e3faf7e5 100644 --- a/syft/cataloger/bundler/parse_gemfile_lock_test.go +++ b/syft/cataloger/bundler/parse_gemfile_lock_test.go @@ -94,7 +94,7 @@ func TestParseGemfileLockEntries(t *testing.T) { t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language) } - if a.Type != pkg.BundlerPkg { + if a.Type != pkg.GemPkg { t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type) } } diff --git a/syft/cataloger/bundler/parse_gemspec.go b/syft/cataloger/bundler/parse_gemspec.go index 91cfa8c01..728668899 100644 --- a/syft/cataloger/bundler/parse_gemspec.go +++ b/syft/cataloger/bundler/parse_gemspec.go @@ -104,7 +104,7 @@ func parseGemspecEntries(_ string, reader io.Reader) ([]pkg.Package, error) { Name: metadata.Name, Version: metadata.Version, Language: pkg.Ruby, - Type: pkg.BundlerPkg, + Type: pkg.GemPkg, Metadata: metadata, }) } diff --git a/syft/cataloger/bundler/parse_gemspec_test.go b/syft/cataloger/bundler/parse_gemspec_test.go index f05bb982b..b792405b3 100644 --- a/syft/cataloger/bundler/parse_gemspec_test.go +++ b/syft/cataloger/bundler/parse_gemspec_test.go @@ -43,7 +43,7 @@ func TestParseGemspec(t *testing.T) { t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language) } - if a.Type != pkg.BundlerPkg { + if a.Type != pkg.GemPkg { t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type) } } diff --git a/syft/cataloger/cataloger.go b/syft/cataloger/cataloger.go index 48c1bc341..b4987f236 100644 --- a/syft/cataloger/cataloger.go +++ b/syft/cataloger/cataloger.go @@ -33,16 +33,30 @@ type Cataloger interface { // TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering). } -// All returns a slice of all locally defined catalogers (defined in child packages). -func All() []Cataloger { +// ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages. +func ImageCatalogers() []Cataloger { return []Cataloger{ + bundler.NewGemspecCataloger(), + python.NewPythonCataloger(), // TODO: split and replace me + javascript.NewJavascriptCataloger(), // TODO: split and replace me + deb.NewDpkgdbCataloger(), + rpmdb.NewRpmdbCataloger(), + java.NewJavaCataloger(), + apkdb.NewApkdbCataloger(), + golang.NewGoModCataloger(), + } +} + +// DirectoryCatalogers returns a slice of locally implemented catalogers that are fit for detecting packages from index files (and select installations) +func DirectoryCatalogers() []Cataloger { + return []Cataloger{ + bundler.NewGemfileLockCataloger(), + python.NewPythonCataloger(), // TODO: split and replace me + javascript.NewJavascriptCataloger(), // TODO: split and replace me deb.NewDpkgdbCataloger(), - bundler.NewGemfileLockCataloger(), - python.NewPythonCataloger(), rpmdb.NewRpmdbCataloger(), java.NewJavaCataloger(), apkdb.NewApkdbCataloger(), golang.NewGoModCataloger(), - javascript.NewJavascriptCataloger(), } } diff --git a/syft/lib.go b/syft/lib.go index e766247fb..cb7dc66a1 100644 --- a/syft/lib.go +++ b/syft/lib.go @@ -17,6 +17,8 @@ Similar to the cataloging process, Linux distribution identification is also per package syft import ( + "fmt" + "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/cataloger" @@ -64,15 +66,17 @@ func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) { log.Info("building the catalog") // conditionally have two sets of catalogers - //var catalogers []cataloger.Cataloger - //// if image - //// use one set of catalogers - //catalogers = ... - // - //// if dir - //// use another set of catalogers + var catalogers []cataloger.Cataloger + switch s.Scheme { + case scope.ImageScheme: + catalogers = cataloger.ImageCatalogers() + case scope.DirectoryScheme: + catalogers = cataloger.DirectoryCatalogers() + default: + return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme) + } - return cataloger.Catalog(s.Resolver, cataloger.All()...) + return cataloger.Catalog(s.Resolver, catalogers...) } // SetLogger sets the logger object used for all syft logging calls. diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 89a712eee..220146725 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -1,9 +1,10 @@ package pkg import ( + "testing" + "github.com/anchore/syft/syft/distro" "github.com/sergi/go-diff/diffmatchpatch" - "testing" ) func TestPackage_pURL(t *testing.T) { @@ -56,7 +57,7 @@ func TestPackage_pURL(t *testing.T) { pkg: Package{ Name: "name", Version: "v0.1.0", - Type: BundlerPkg, + Type: GemPkg, }, expected: "pkg:gem/name@v0.1.0", }, diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 08a126c8f..61ed24878 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -8,7 +8,7 @@ type Type string const ( UnknownPkg Type = "UnknownPackage" ApkPkg Type = "apk" - BundlerPkg Type = "bundle" + GemPkg Type = "gem" DebPkg Type = "deb" EggPkg Type = "egg" // PacmanPkg Type = "pacman" @@ -26,7 +26,7 @@ const ( var AllPkgs = []Type{ ApkPkg, - BundlerPkg, + GemPkg, DebPkg, EggPkg, // PacmanPkg, @@ -45,7 +45,7 @@ func (t Type) PackageURLType() string { switch t { case ApkPkg: return "alpine" - case BundlerPkg: + case GemPkg: return packageurl.TypeGem case DebPkg: return "deb" diff --git a/syft/presenter/cyclonedx/presenter.go b/syft/presenter/cyclonedx/presenter.go index cf02716d1..1b8accebe 100644 --- a/syft/presenter/cyclonedx/presenter.go +++ b/syft/presenter/cyclonedx/presenter.go @@ -34,9 +34,7 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Present func (pres *Presenter) Present(output io.Writer) error { bom := NewDocumentFromCatalog(pres.catalog, pres.distro) - srcObj := pres.scope.Source() - - switch src := srcObj.(type) { + switch src := pres.scope.Source.(type) { case scope.DirSource: bom.BomDescriptor.Component = &BdComponent{ Component: Component{ diff --git a/syft/presenter/json/location.go b/syft/presenter/json/location.go index 1987ec297..8a0fe1209 100644 --- a/syft/presenter/json/location.go +++ b/syft/presenter/json/location.go @@ -15,8 +15,7 @@ type ImageLocation struct { } func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) { - srcObj := s.Source() - switch src := srcObj.(type) { + switch src := s.Source.(type) { case scope.ImageSource: locations := make([]ImageLocation, len(p.Source)) for idx := range p.Source { diff --git a/syft/presenter/json/source.go b/syft/presenter/json/source.go index a44ad8676..471390999 100644 --- a/syft/presenter/json/source.go +++ b/syft/presenter/json/source.go @@ -12,8 +12,7 @@ type Source struct { } func NewSource(s scope.Scope) (Source, error) { - srcObj := s.Source() - switch src := srcObj.(type) { + switch src := s.Source.(type) { case scope.ImageSource: return Source{ Type: "image", @@ -22,7 +21,7 @@ func NewSource(s scope.Scope) (Source, error) { case scope.DirSource: return Source{ Type: "directory", - Target: s.DirSrc.Path, + Target: src.Path, }, nil default: return Source{}, fmt.Errorf("unsupported source: %T", src) diff --git a/syft/presenter/text/presenter.go b/syft/presenter/text/presenter.go index 2376f1aac..3291370d9 100644 --- a/syft/presenter/text/presenter.go +++ b/syft/presenter/text/presenter.go @@ -27,9 +27,8 @@ func (pres *Presenter) Present(output io.Writer) error { // init the tabular writer w := new(tabwriter.Writer) w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight) - srcObj := pres.scope.Source() - switch src := srcObj.(type) { + switch src := pres.scope.Source.(type) { case scope.DirSource: fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path)) case scope.ImageSource: diff --git a/syft/scope/scope.go b/syft/scope/scope.go index 88406d4eb..bf2ed74f3 100644 --- a/syft/scope/scope.go +++ b/syft/scope/scope.go @@ -20,12 +20,12 @@ import ( ) const ( - unknownScheme scheme = "unknown-scheme" - directoryScheme scheme = "directory-scheme" - imageScheme scheme = "image-scheme" + UnknownScheme Scheme = "unknown-scheme" + DirectoryScheme Scheme = "directory-scheme" + ImageScheme Scheme = "image-scheme" ) -type scheme string +type Scheme string // ImageSource represents a data source that is a container image type ImageSource struct { @@ -42,8 +42,8 @@ type DirSource struct { type Scope struct { Option Option // specific perspective to catalog Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution - ImgSrc ImageSource // the specific image to be cataloged - DirSrc DirSource // the specific directory to be cataloged + Source interface{} // the specific source object to be cataloged + Scheme Scheme // the source data scheme type (directory or image) } // NewScope produces a Scope based on userInput like dir: or image:tag @@ -55,7 +55,7 @@ func NewScope(userInput string, o Option) (Scope, func(), error) { } switch parsedScheme { - case directoryScheme: + case DirectoryScheme: fileMeta, err := fs.Stat(location) if err != nil { return Scope{}, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err) @@ -71,7 +71,7 @@ func NewScope(userInput string, o Option) (Scope, func(), error) { } return s, func() {}, nil - case imageScheme: + case ImageScheme: img, err := stereoscope.GetImage(location) cleanup := func() { stereoscope.Cleanup() @@ -97,9 +97,10 @@ func NewScopeFromDir(path string) (Scope, error) { Resolver: &resolvers.DirectoryResolver{ Path: path, }, - DirSrc: DirSource{ + Source: DirSource{ Path: path, }, + Scheme: DirectoryScheme, }, nil } @@ -118,59 +119,48 @@ func NewScopeFromImage(img *image.Image, option Option) (Scope, error) { return Scope{ Option: option, Resolver: resolver, - ImgSrc: ImageSource{ + Source: ImageSource{ Img: img, }, + Scheme: ImageScheme, }, nil } -// Source returns the configured data source (either a dir source or container image source) -func (s Scope) Source() interface{} { - if s.ImgSrc != (ImageSource{}) { - return s.ImgSrc - } - if s.DirSrc != (DirSource{}) { - return s.DirSrc - } - - return nil -} - type sourceDetector func(string) (image.Source, string, error) -func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (scheme, string, error) { +func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (Scheme, string, error) { if strings.HasPrefix(userInput, "dir:") { // blindly trust the user's scheme dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:")) if err != nil { - return unknownScheme, "", fmt.Errorf("unable to expand directory path: %w", err) + return UnknownScheme, "", fmt.Errorf("unable to expand directory path: %w", err) } - return directoryScheme, dirLocation, nil + return DirectoryScheme, dirLocation, nil } // we should attempt to let stereoscope determine what the source is first --just because the source is a valid directory // doesn't mean we yet know if it is an OCI layout directory (to be treated as an image) or if it is a generic filesystem directory. source, imageSpec, err := imageDetector(userInput) if err != nil { - return unknownScheme, "", fmt.Errorf("unable to detect the scheme from %q: %w", userInput, err) + return UnknownScheme, "", fmt.Errorf("unable to detect the scheme from %q: %w", userInput, err) } if source == image.UnknownSource { dirLocation, err := homedir.Expand(userInput) if err != nil { - return unknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err) + return UnknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err) } fileMeta, err := fs.Stat(dirLocation) if err != nil { - return unknownScheme, "", nil + return UnknownScheme, "", nil } if fileMeta.IsDir() { - return directoryScheme, dirLocation, nil + return DirectoryScheme, dirLocation, nil } - return unknownScheme, "", nil + return UnknownScheme, "", nil } - return imageScheme, imageSpec, nil + return ImageScheme, imageSpec, nil } diff --git a/syft/scope/scope_test.go b/syft/scope/scope_test.go index 017d45e0a..deb55c1d5 100644 --- a/syft/scope/scope_test.go +++ b/syft/scope/scope_test.go @@ -1,13 +1,13 @@ package scope import ( - "github.com/mitchellh/go-homedir" - "github.com/spf13/afero" "os" "testing" "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/image" + "github.com/mitchellh/go-homedir" + "github.com/spf13/afero" ) func TestNewScopeFromImageFails(t *testing.T) { @@ -78,8 +78,8 @@ func TestDirectoryScope(t *testing.T) { if err != nil { t.Errorf("could not create NewDirScope: %w", err) } - if p.DirSrc.Path != test.input { - t.Errorf("mismatched stringer: '%s' != '%s'", p.DirSrc.Path, test.input) + if p.Source.(DirSource).Path != test.input { + t.Errorf("mismatched stringer: '%s' != '%s'", p.Source.(DirSource).Path, test.input) } refs, err := p.Resolver.FilesByPath(test.inputPaths...) @@ -229,7 +229,7 @@ func TestDetectScheme(t *testing.T) { userInput string dirs []string detection detectorResult - expectedScheme scheme + expectedScheme Scheme expectedLocation string }{ { @@ -239,7 +239,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "wagoodman/dive:latest", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "wagoodman/dive:latest", }, { @@ -249,7 +249,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "wagoodman/dive", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "wagoodman/dive", }, { @@ -259,7 +259,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "wagoodman/dive:latest", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "wagoodman/dive:latest", }, { @@ -269,7 +269,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "wagoodman/dive", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "wagoodman/dive", }, { @@ -279,7 +279,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "latest", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, // we want to be able to handle this case better, however, I don't see a way to do this // the user will need to provide more explicit input (docker:docker:latest) expectedLocation: "latest", @@ -291,7 +291,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "docker:latest", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, // we want to be able to handle this case better, however, I don't see a way to do this // the user will need to provide more explicit input (docker:docker:latest) expectedLocation: "docker:latest", @@ -303,7 +303,7 @@ func TestDetectScheme(t *testing.T) { src: image.OciTarballSource, ref: "some/path-to-file", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "some/path-to-file", }, { @@ -314,7 +314,7 @@ func TestDetectScheme(t *testing.T) { ref: "some/path-to-dir", }, dirs: []string{"some/path-to-dir"}, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "some/path-to-dir", }, { @@ -325,7 +325,7 @@ func TestDetectScheme(t *testing.T) { ref: "", }, dirs: []string{"some/path-to-dir"}, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: "some/path-to-dir", }, { @@ -335,7 +335,7 @@ func TestDetectScheme(t *testing.T) { src: image.DockerDaemonSource, ref: "some/path-to-dir", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "some/path-to-dir", }, { @@ -346,7 +346,7 @@ func TestDetectScheme(t *testing.T) { ref: "", }, dirs: []string{"some/path-to-dir"}, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: "some/path-to-dir", }, { @@ -356,7 +356,7 @@ func TestDetectScheme(t *testing.T) { src: image.UnknownSource, ref: "", }, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: ".", }, { @@ -366,7 +366,7 @@ func TestDetectScheme(t *testing.T) { src: image.UnknownSource, ref: "", }, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: ".", }, // we should support tilde expansion @@ -377,7 +377,7 @@ func TestDetectScheme(t *testing.T) { src: image.OciDirectorySource, ref: "~/some-path", }, - expectedScheme: imageScheme, + expectedScheme: ImageScheme, expectedLocation: "~/some-path", }, { @@ -388,26 +388,26 @@ func TestDetectScheme(t *testing.T) { ref: "", }, dirs: []string{"~/some-path"}, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: "~/some-path", }, { name: "tilde-expansion-dir-explicit-exists", userInput: "dir:~/some-path", dirs: []string{"~/some-path"}, - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: "~/some-path", }, { name: "tilde-expansion-dir-explicit-dne", userInput: "dir:~/some-path", - expectedScheme: directoryScheme, + expectedScheme: DirectoryScheme, expectedLocation: "~/some-path", }, { name: "tilde-expansion-dir-implicit-dne", userInput: "~/some-path", - expectedScheme: unknownScheme, + expectedScheme: UnknownScheme, expectedLocation: "", }, } diff --git a/test/integration/json_schema_test.go b/test/integration/json_schema_test.go index 63e93b0c6..8545c0978 100644 --- a/test/integration/json_schema_test.go +++ b/test/integration/json_schema_test.go @@ -108,6 +108,10 @@ func TestJsonSchemaImg(t *testing.T) { t.Fatalf("failed to catalog image: %+v", err) } + var cases []testCase + cases = append(cases, commonTestCases...) + cases = append(cases, imageOnlyTestCases...) + for _, c := range cases { t.Run(c.name, func(t *testing.T) { testJsonSchema(t, catalog, theScope, "img") @@ -121,6 +125,10 @@ func TestJsonSchemaDirs(t *testing.T) { t.Errorf("unable to create scope from dir: %+v", err) } + var cases []testCase + cases = append(cases, commonTestCases...) + cases = append(cases, dirOnlyTestCases...) + for _, c := range cases { t.Run(c.name, func(t *testing.T) { testJsonSchema(t, catalog, theScope, "dir") diff --git a/test/integration/pkg_cases.go b/test/integration/pkg_cases.go index 6981304d2..54762a151 100644 --- a/test/integration/pkg_cases.go +++ b/test/integration/pkg_cases.go @@ -4,12 +4,86 @@ package integration import "github.com/anchore/syft/syft/pkg" -var cases = []struct { +type testCase struct { name string pkgType pkg.Type pkgLanguage pkg.Language pkgInfo map[string]string -}{ +} + +var imageOnlyTestCases = []testCase{ + { + name: "find gemspec packages", + pkgType: pkg.GemPkg, + pkgLanguage: pkg.Ruby, + pkgInfo: map[string]string{ + "bundler": "2.1.4", + }, + }, +} + +var dirOnlyTestCases = []testCase{ + { + name: "find gemfile packages", + pkgType: pkg.GemPkg, + pkgLanguage: pkg.Ruby, + pkgInfo: map[string]string{ + "actionmailer": "4.1.1", + "actionpack": "4.1.1", + "actionview": "4.1.1", + "activemodel": "4.1.1", + "activerecord": "4.1.1", + "activesupport": "4.1.1", + "arel": "5.0.1.20140414130214", + "bootstrap-sass": "3.1.1.1", + "builder": "3.2.2", + "coffee-rails": "4.0.1", + "coffee-script": "2.2.0", + "coffee-script-source": "1.7.0", + "erubis": "2.7.0", + "execjs": "2.0.2", + "hike": "1.2.3", + "i18n": "0.6.9", + "jbuilder": "2.0.7", + "jquery-rails": "3.1.0", + "json": "1.8.1", + "kgio": "2.9.2", + "libv8": "3.16.14.3", + "mail": "2.5.4", + "mime-types": "1.25.1", + "minitest": "5.3.4", + "multi_json": "1.10.1", + "mysql2": "0.3.16", + "polyglot": "0.3.4", + "rack": "1.5.2", + "rack-test": "0.6.2", + "rails": "4.1.1", + "railties": "4.1.1", + "raindrops": "0.13.0", + "rake": "10.3.2", + "rdoc": "4.1.1", + "ref": "1.0.5", + "sass": "3.2.19", + "sass-rails": "4.0.3", + "sdoc": "0.4.0", + "spring": "1.1.3", + "sprockets": "2.11.0", + "sprockets-rails": "2.1.3", + "sqlite3": "1.3.9", + "therubyracer": "0.12.1", + "thor": "0.19.1", + "thread_safe": "0.3.3", + "tilt": "1.4.1", + "treetop": "1.4.15", + "turbolinks": "2.2.2", + "tzinfo": "1.2.0", + "uglifier": "2.5.0", + "unicorn": "4.8.3", + }, + }, +} + +var commonTestCases = []testCase{ { name: "find rpmdb packages", pkgType: pkg.RpmPkg, @@ -98,64 +172,6 @@ var cases = []struct { "mypy": "v0.770", }, }, - { - name: "find bundler packages", - pkgType: pkg.BundlerPkg, - pkgLanguage: pkg.Ruby, - pkgInfo: map[string]string{ - "actionmailer": "4.1.1", - "actionpack": "4.1.1", - "actionview": "4.1.1", - "activemodel": "4.1.1", - "activerecord": "4.1.1", - "activesupport": "4.1.1", - "arel": "5.0.1.20140414130214", - "bootstrap-sass": "3.1.1.1", - "builder": "3.2.2", - "coffee-rails": "4.0.1", - "coffee-script": "2.2.0", - "coffee-script-source": "1.7.0", - "erubis": "2.7.0", - "execjs": "2.0.2", - "hike": "1.2.3", - "i18n": "0.6.9", - "jbuilder": "2.0.7", - "jquery-rails": "3.1.0", - "json": "1.8.1", - "kgio": "2.9.2", - "libv8": "3.16.14.3", - "mail": "2.5.4", - "mime-types": "1.25.1", - "minitest": "5.3.4", - "multi_json": "1.10.1", - "mysql2": "0.3.16", - "polyglot": "0.3.4", - "rack": "1.5.2", - "rack-test": "0.6.2", - "rails": "4.1.1", - "railties": "4.1.1", - "raindrops": "0.13.0", - "rake": "10.3.2", - "rdoc": "4.1.1", - "ref": "1.0.5", - "sass": "3.2.19", - "sass-rails": "4.0.3", - "sdoc": "0.4.0", - "spring": "1.1.3", - "sprockets": "2.11.0", - "sprockets-rails": "2.1.3", - "sqlite3": "1.3.9", - "therubyracer": "0.12.1", - "thor": "0.19.1", - "thread_safe": "0.3.3", - "tilt": "1.4.1", - "treetop": "1.4.15", - "turbolinks": "2.2.2", - "tzinfo": "1.2.0", - "uglifier": "2.5.0", - "unicorn": "4.8.3", - }, - }, { name: "find apkdb packages", diff --git a/test/integration/pkg_coverage_test.go b/test/integration/pkg_coverage_test.go index 333c03bc6..f79fbd55d 100644 --- a/test/integration/pkg_coverage_test.go +++ b/test/integration/pkg_coverage_test.go @@ -3,9 +3,11 @@ package integration import ( - "github.com/anchore/stereoscope/pkg/imagetest" "testing" + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/go-test/deep" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/pkg" @@ -35,6 +37,10 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Add(string(p)) } + var cases []testCase + cases = append(cases, commonTestCases...) + cases = append(cases, imageOnlyTestCases...) + for _, c := range cases { t.Run(c.name, func(t *testing.T) { pkgCount := 0 @@ -81,10 +87,16 @@ func TestPkgCoverageImage(t *testing.T) { // ensure that integration test cases stay in sync with the available catalogers if len(observedLanguages) < len(definedLanguages) { t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages)) + for _, d := range deep.Equal(observedLanguages, definedLanguages) { + t.Errorf("diff: %+v", d) + } } if len(observedPkgs) < len(definedPkgs) { t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", len(definedPkgs), len(observedPkgs)) + for _, d := range deep.Equal(observedPkgs, definedPkgs) { + t.Errorf("diff: %+v", d) + } } } @@ -107,6 +119,10 @@ func TestPkgCoverageDirectory(t *testing.T) { definedPkgs.Add(string(p)) } + var cases []testCase + cases = append(cases, commonTestCases...) + cases = append(cases, dirOnlyTestCases...) + for _, c := range cases { t.Run(c.name, func(t *testing.T) { pkgCount := 0 @@ -150,7 +166,7 @@ func TestPkgCoverageDirectory(t *testing.T) { observedPkgs.Remove(string(pkg.UnknownPkg)) definedPkgs.Remove(string(pkg.UnknownPkg)) - // ensure that integration test cases stay in sync with the available catalogers + // ensure that integration test commonTestCases stay in sync with the available catalogers if len(observedLanguages) < len(definedLanguages) { t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages)) } diff --git a/test/integration/test-fixtures/image-pkg-coverage/ruby/specification/bundler.gemspec b/test/integration/test-fixtures/image-pkg-coverage/ruby/specification/bundler.gemspec new file mode 100644 index 000000000..450b81096 --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/ruby/specification/bundler.gemspec @@ -0,0 +1,25 @@ +# frozen_string_literal: true +# -*- encoding: utf-8 -*- +# stub: bundler 2.1.4 ruby lib + +Gem::Specification.new do |s| + s.name = "bundler".freeze + s.version = "2.1.4" + + s.required_rubygems_version = Gem::Requirement.new(">= 2.5.2".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Andr\u00E9 Arko".freeze, "Samuel Giddins".freeze, "Colby Swandale".freeze, "Hiroshi Shibata".freeze, "David Rodr\u00EDguez".freeze, "Grey Baker".f + s.bindir = "exe".freeze + s.date = "2020-01-05" + s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably".freeze + s.email = ["team@bundler.io".freeze] + s.executables = ["bundle".freeze, "bundler".freeze] + s.files = ["exe/bundle".freeze, "exe/bundler".freeze] + s.homepage = "https://bundler.io".freeze + s.licenses = ["MIT".freeze] + s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) + s.rubygems_version = "3.1.2".freeze + s.summary = "The best way to manage your application's dependencies".freeze + + s.installed_by_version = "3.1.2" if s.respond_to? :installed_by_version + end \ No newline at end of file