split catalogers into two sets, one for images another for directory scans

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-10-06 13:11:35 -04:00 committed by Toure Dunnon
parent 1c320a8382
commit 10b44f5311
19 changed files with 218 additions and 149 deletions

View File

@ -130,7 +130,7 @@ unit: fixtures ## Run unit tests (with coverage)
.PHONY: integration .PHONY: integration
integration: ## Run integration tests integration: ## Run integration tests
$(call title,Running 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 # note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
integration-fingerprint: integration-fingerprint:

View File

@ -44,7 +44,7 @@ func parseGemfileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error)
Name: candidate[0], Name: candidate[0],
Version: strings.Trim(candidate[1], "()"), Version: strings.Trim(candidate[1], "()"),
Language: pkg.Ruby, Language: pkg.Ruby,
Type: pkg.BundlerPkg, Type: pkg.GemPkg,
}) })
} }
} }

View File

@ -94,7 +94,7 @@ func TestParseGemfileLockEntries(t *testing.T) {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language) 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) t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
} }
} }

View File

@ -104,7 +104,7 @@ func parseGemspecEntries(_ string, reader io.Reader) ([]pkg.Package, error) {
Name: metadata.Name, Name: metadata.Name,
Version: metadata.Version, Version: metadata.Version,
Language: pkg.Ruby, Language: pkg.Ruby,
Type: pkg.BundlerPkg, Type: pkg.GemPkg,
Metadata: metadata, Metadata: metadata,
}) })
} }

View File

@ -43,7 +43,7 @@ func TestParseGemspec(t *testing.T) {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language) 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) t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
} }
} }

View File

@ -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). // 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). // ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages.
func All() []Cataloger { func ImageCatalogers() []Cataloger {
return []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(), deb.NewDpkgdbCataloger(),
bundler.NewGemfileLockCataloger(),
python.NewPythonCataloger(),
rpmdb.NewRpmdbCataloger(), rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(), java.NewJavaCataloger(),
apkdb.NewApkdbCataloger(), apkdb.NewApkdbCataloger(),
golang.NewGoModCataloger(), golang.NewGoModCataloger(),
javascript.NewJavascriptCataloger(),
} }
} }

View File

@ -17,6 +17,8 @@ Similar to the cataloging process, Linux distribution identification is also per
package syft package syft
import ( import (
"fmt"
"github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloger" "github.com/anchore/syft/syft/cataloger"
@ -64,15 +66,17 @@ func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
log.Info("building the catalog") log.Info("building the catalog")
// conditionally have two sets of catalogers // conditionally have two sets of catalogers
//var catalogers []cataloger.Cataloger var catalogers []cataloger.Cataloger
//// if image switch s.Scheme {
//// use one set of catalogers case scope.ImageScheme:
//catalogers = ... catalogers = cataloger.ImageCatalogers()
// case scope.DirectoryScheme:
//// if dir catalogers = cataloger.DirectoryCatalogers()
//// use another set of catalogers 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. // SetLogger sets the logger object used for all syft logging calls.

View File

@ -1,9 +1,10 @@
package pkg package pkg
import ( import (
"testing"
"github.com/anchore/syft/syft/distro" "github.com/anchore/syft/syft/distro"
"github.com/sergi/go-diff/diffmatchpatch" "github.com/sergi/go-diff/diffmatchpatch"
"testing"
) )
func TestPackage_pURL(t *testing.T) { func TestPackage_pURL(t *testing.T) {
@ -56,7 +57,7 @@ func TestPackage_pURL(t *testing.T) {
pkg: Package{ pkg: Package{
Name: "name", Name: "name",
Version: "v0.1.0", Version: "v0.1.0",
Type: BundlerPkg, Type: GemPkg,
}, },
expected: "pkg:gem/name@v0.1.0", expected: "pkg:gem/name@v0.1.0",
}, },

View File

@ -8,7 +8,7 @@ type Type string
const ( const (
UnknownPkg Type = "UnknownPackage" UnknownPkg Type = "UnknownPackage"
ApkPkg Type = "apk" ApkPkg Type = "apk"
BundlerPkg Type = "bundle" GemPkg Type = "gem"
DebPkg Type = "deb" DebPkg Type = "deb"
EggPkg Type = "egg" EggPkg Type = "egg"
// PacmanPkg Type = "pacman" // PacmanPkg Type = "pacman"
@ -26,7 +26,7 @@ const (
var AllPkgs = []Type{ var AllPkgs = []Type{
ApkPkg, ApkPkg,
BundlerPkg, GemPkg,
DebPkg, DebPkg,
EggPkg, EggPkg,
// PacmanPkg, // PacmanPkg,
@ -45,7 +45,7 @@ func (t Type) PackageURLType() string {
switch t { switch t {
case ApkPkg: case ApkPkg:
return "alpine" return "alpine"
case BundlerPkg: case GemPkg:
return packageurl.TypeGem return packageurl.TypeGem
case DebPkg: case DebPkg:
return "deb" return "deb"

View File

@ -34,9 +34,7 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Present
func (pres *Presenter) Present(output io.Writer) error { func (pres *Presenter) Present(output io.Writer) error {
bom := NewDocumentFromCatalog(pres.catalog, pres.distro) bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
srcObj := pres.scope.Source() switch src := pres.scope.Source.(type) {
switch src := srcObj.(type) {
case scope.DirSource: case scope.DirSource:
bom.BomDescriptor.Component = &BdComponent{ bom.BomDescriptor.Component = &BdComponent{
Component: Component{ Component: Component{

View File

@ -15,8 +15,7 @@ type ImageLocation struct {
} }
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) { func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
srcObj := s.Source() switch src := s.Source.(type) {
switch src := srcObj.(type) {
case scope.ImageSource: case scope.ImageSource:
locations := make([]ImageLocation, len(p.Source)) locations := make([]ImageLocation, len(p.Source))
for idx := range p.Source { for idx := range p.Source {

View File

@ -12,8 +12,7 @@ type Source struct {
} }
func NewSource(s scope.Scope) (Source, error) { func NewSource(s scope.Scope) (Source, error) {
srcObj := s.Source() switch src := s.Source.(type) {
switch src := srcObj.(type) {
case scope.ImageSource: case scope.ImageSource:
return Source{ return Source{
Type: "image", Type: "image",
@ -22,7 +21,7 @@ func NewSource(s scope.Scope) (Source, error) {
case scope.DirSource: case scope.DirSource:
return Source{ return Source{
Type: "directory", Type: "directory",
Target: s.DirSrc.Path, Target: src.Path,
}, nil }, nil
default: default:
return Source{}, fmt.Errorf("unsupported source: %T", src) return Source{}, fmt.Errorf("unsupported source: %T", src)

View File

@ -27,9 +27,8 @@ func (pres *Presenter) Present(output io.Writer) error {
// init the tabular writer // init the tabular writer
w := new(tabwriter.Writer) w := new(tabwriter.Writer)
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight) 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: case scope.DirSource:
fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path)) fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path))
case scope.ImageSource: case scope.ImageSource:

View File

@ -20,12 +20,12 @@ import (
) )
const ( const (
unknownScheme scheme = "unknown-scheme" UnknownScheme Scheme = "unknown-scheme"
directoryScheme scheme = "directory-scheme" DirectoryScheme Scheme = "directory-scheme"
imageScheme scheme = "image-scheme" ImageScheme Scheme = "image-scheme"
) )
type scheme string type Scheme string
// ImageSource represents a data source that is a container image // ImageSource represents a data source that is a container image
type ImageSource struct { type ImageSource struct {
@ -42,8 +42,8 @@ type DirSource struct {
type Scope struct { type Scope struct {
Option Option // specific perspective to catalog Option Option // specific perspective to catalog
Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
ImgSrc ImageSource // the specific image to be cataloged Source interface{} // the specific source object to be cataloged
DirSrc DirSource // the specific directory 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 // 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 { switch parsedScheme {
case directoryScheme: case DirectoryScheme:
fileMeta, err := fs.Stat(location) fileMeta, err := fs.Stat(location)
if err != nil { if err != nil {
return Scope{}, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err) 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 return s, func() {}, nil
case imageScheme: case ImageScheme:
img, err := stereoscope.GetImage(location) img, err := stereoscope.GetImage(location)
cleanup := func() { cleanup := func() {
stereoscope.Cleanup() stereoscope.Cleanup()
@ -97,9 +97,10 @@ func NewScopeFromDir(path string) (Scope, error) {
Resolver: &resolvers.DirectoryResolver{ Resolver: &resolvers.DirectoryResolver{
Path: path, Path: path,
}, },
DirSrc: DirSource{ Source: DirSource{
Path: path, Path: path,
}, },
Scheme: DirectoryScheme,
}, nil }, nil
} }
@ -118,59 +119,48 @@ func NewScopeFromImage(img *image.Image, option Option) (Scope, error) {
return Scope{ return Scope{
Option: option, Option: option,
Resolver: resolver, Resolver: resolver,
ImgSrc: ImageSource{ Source: ImageSource{
Img: img, Img: img,
}, },
Scheme: ImageScheme,
}, nil }, 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) 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:") { if strings.HasPrefix(userInput, "dir:") {
// blindly trust the user's scheme // blindly trust the user's scheme
dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:")) dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:"))
if err != nil { 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 // 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. // 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) source, imageSpec, err := imageDetector(userInput)
if err != nil { 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 { if source == image.UnknownSource {
dirLocation, err := homedir.Expand(userInput) dirLocation, err := homedir.Expand(userInput)
if err != nil { 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) fileMeta, err := fs.Stat(dirLocation)
if err != nil { if err != nil {
return unknownScheme, "", nil return UnknownScheme, "", nil
} }
if fileMeta.IsDir() { 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
} }

View File

@ -1,13 +1,13 @@
package scope package scope
import ( import (
"github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
"os" "os"
"testing" "testing"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
"github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
) )
func TestNewScopeFromImageFails(t *testing.T) { func TestNewScopeFromImageFails(t *testing.T) {
@ -78,8 +78,8 @@ func TestDirectoryScope(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not create NewDirScope: %w", err) t.Errorf("could not create NewDirScope: %w", err)
} }
if p.DirSrc.Path != test.input { if p.Source.(DirSource).Path != test.input {
t.Errorf("mismatched stringer: '%s' != '%s'", p.DirSrc.Path, test.input) t.Errorf("mismatched stringer: '%s' != '%s'", p.Source.(DirSource).Path, test.input)
} }
refs, err := p.Resolver.FilesByPath(test.inputPaths...) refs, err := p.Resolver.FilesByPath(test.inputPaths...)
@ -229,7 +229,7 @@ func TestDetectScheme(t *testing.T) {
userInput string userInput string
dirs []string dirs []string
detection detectorResult detection detectorResult
expectedScheme scheme expectedScheme Scheme
expectedLocation string expectedLocation string
}{ }{
{ {
@ -239,7 +239,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "wagoodman/dive:latest", ref: "wagoodman/dive:latest",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive:latest", expectedLocation: "wagoodman/dive:latest",
}, },
{ {
@ -249,7 +249,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "wagoodman/dive", ref: "wagoodman/dive",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive", expectedLocation: "wagoodman/dive",
}, },
{ {
@ -259,7 +259,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "wagoodman/dive:latest", ref: "wagoodman/dive:latest",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive:latest", expectedLocation: "wagoodman/dive:latest",
}, },
{ {
@ -269,7 +269,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "wagoodman/dive", ref: "wagoodman/dive",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive", expectedLocation: "wagoodman/dive",
}, },
{ {
@ -279,7 +279,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "latest", 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 // 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) // the user will need to provide more explicit input (docker:docker:latest)
expectedLocation: "latest", expectedLocation: "latest",
@ -291,7 +291,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "docker:latest", 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 // 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) // the user will need to provide more explicit input (docker:docker:latest)
expectedLocation: "docker:latest", expectedLocation: "docker:latest",
@ -303,7 +303,7 @@ func TestDetectScheme(t *testing.T) {
src: image.OciTarballSource, src: image.OciTarballSource,
ref: "some/path-to-file", ref: "some/path-to-file",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "some/path-to-file", expectedLocation: "some/path-to-file",
}, },
{ {
@ -314,7 +314,7 @@ func TestDetectScheme(t *testing.T) {
ref: "some/path-to-dir", ref: "some/path-to-dir",
}, },
dirs: []string{"some/path-to-dir"}, dirs: []string{"some/path-to-dir"},
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "some/path-to-dir", expectedLocation: "some/path-to-dir",
}, },
{ {
@ -325,7 +325,7 @@ func TestDetectScheme(t *testing.T) {
ref: "", ref: "",
}, },
dirs: []string{"some/path-to-dir"}, dirs: []string{"some/path-to-dir"},
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: "some/path-to-dir", expectedLocation: "some/path-to-dir",
}, },
{ {
@ -335,7 +335,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource, src: image.DockerDaemonSource,
ref: "some/path-to-dir", ref: "some/path-to-dir",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "some/path-to-dir", expectedLocation: "some/path-to-dir",
}, },
{ {
@ -346,7 +346,7 @@ func TestDetectScheme(t *testing.T) {
ref: "", ref: "",
}, },
dirs: []string{"some/path-to-dir"}, dirs: []string{"some/path-to-dir"},
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: "some/path-to-dir", expectedLocation: "some/path-to-dir",
}, },
{ {
@ -356,7 +356,7 @@ func TestDetectScheme(t *testing.T) {
src: image.UnknownSource, src: image.UnknownSource,
ref: "", ref: "",
}, },
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: ".", expectedLocation: ".",
}, },
{ {
@ -366,7 +366,7 @@ func TestDetectScheme(t *testing.T) {
src: image.UnknownSource, src: image.UnknownSource,
ref: "", ref: "",
}, },
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: ".", expectedLocation: ".",
}, },
// we should support tilde expansion // we should support tilde expansion
@ -377,7 +377,7 @@ func TestDetectScheme(t *testing.T) {
src: image.OciDirectorySource, src: image.OciDirectorySource,
ref: "~/some-path", ref: "~/some-path",
}, },
expectedScheme: imageScheme, expectedScheme: ImageScheme,
expectedLocation: "~/some-path", expectedLocation: "~/some-path",
}, },
{ {
@ -388,26 +388,26 @@ func TestDetectScheme(t *testing.T) {
ref: "", ref: "",
}, },
dirs: []string{"~/some-path"}, dirs: []string{"~/some-path"},
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path", expectedLocation: "~/some-path",
}, },
{ {
name: "tilde-expansion-dir-explicit-exists", name: "tilde-expansion-dir-explicit-exists",
userInput: "dir:~/some-path", userInput: "dir:~/some-path",
dirs: []string{"~/some-path"}, dirs: []string{"~/some-path"},
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path", expectedLocation: "~/some-path",
}, },
{ {
name: "tilde-expansion-dir-explicit-dne", name: "tilde-expansion-dir-explicit-dne",
userInput: "dir:~/some-path", userInput: "dir:~/some-path",
expectedScheme: directoryScheme, expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path", expectedLocation: "~/some-path",
}, },
{ {
name: "tilde-expansion-dir-implicit-dne", name: "tilde-expansion-dir-implicit-dne",
userInput: "~/some-path", userInput: "~/some-path",
expectedScheme: unknownScheme, expectedScheme: UnknownScheme,
expectedLocation: "", expectedLocation: "",
}, },
} }

View File

@ -108,6 +108,10 @@ func TestJsonSchemaImg(t *testing.T) {
t.Fatalf("failed to catalog image: %+v", err) t.Fatalf("failed to catalog image: %+v", err)
} }
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, imageOnlyTestCases...)
for _, c := range cases { for _, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testJsonSchema(t, catalog, theScope, "img") testJsonSchema(t, catalog, theScope, "img")
@ -121,6 +125,10 @@ func TestJsonSchemaDirs(t *testing.T) {
t.Errorf("unable to create scope from dir: %+v", err) 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 { for _, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
testJsonSchema(t, catalog, theScope, "dir") testJsonSchema(t, catalog, theScope, "dir")

View File

@ -4,12 +4,86 @@ package integration
import "github.com/anchore/syft/syft/pkg" import "github.com/anchore/syft/syft/pkg"
var cases = []struct { type testCase struct {
name string name string
pkgType pkg.Type pkgType pkg.Type
pkgLanguage pkg.Language pkgLanguage pkg.Language
pkgInfo map[string]string 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", name: "find rpmdb packages",
pkgType: pkg.RpmPkg, pkgType: pkg.RpmPkg,
@ -98,64 +172,6 @@ var cases = []struct {
"mypy": "v0.770", "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", name: "find apkdb packages",

View File

@ -3,9 +3,11 @@
package integration package integration
import ( import (
"github.com/anchore/stereoscope/pkg/imagetest"
"testing" "testing"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/go-test/deep"
"github.com/anchore/syft/internal" "github.com/anchore/syft/internal"
"github.com/anchore/syft/syft" "github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@ -35,6 +37,10 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Add(string(p)) definedPkgs.Add(string(p))
} }
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, imageOnlyTestCases...)
for _, c := range cases { for _, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
pkgCount := 0 pkgCount := 0
@ -81,10 +87,16 @@ func TestPkgCoverageImage(t *testing.T) {
// ensure that integration test cases stay in sync with the available catalogers // ensure that integration test cases stay in sync with the available catalogers
if len(observedLanguages) < len(definedLanguages) { if len(observedLanguages) < len(definedLanguages) {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages)) 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) { if len(observedPkgs) < len(definedPkgs) {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", len(definedPkgs), len(observedPkgs)) 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)) definedPkgs.Add(string(p))
} }
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, dirOnlyTestCases...)
for _, c := range cases { for _, c := range cases {
t.Run(c.name, func(t *testing.T) { t.Run(c.name, func(t *testing.T) {
pkgCount := 0 pkgCount := 0
@ -150,7 +166,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
observedPkgs.Remove(string(pkg.UnknownPkg)) observedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.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) { if len(observedLanguages) < len(definedLanguages) {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages)) t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages))
} }

View File

@ -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