mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
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:
committed by
Toure Dunnon
parent
1c320a8382
commit
10b44f5311
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
+12
-8
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
+3
-3
@@ -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"
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
+21
-31
@@ -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
|
||||
}
|
||||
|
||||
+23
-23
@@ -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: "",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user