mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
rename scope to source
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
495fb0a45f
commit
9668341a14
55 changed files with 340 additions and 334 deletions
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/anchore/syft/syft/presenter"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/syft/internal/config"
|
||||
|
@ -49,8 +49,8 @@ func setGlobalCliOptions() {
|
|||
// scan options
|
||||
flag := "scope"
|
||||
rootCmd.Flags().StringP(
|
||||
"scope", "s", scope.SquashedScope.String(),
|
||||
fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
|
||||
"scope", "s", source.SquashedScope.String(),
|
||||
fmt.Sprintf("selection of layers to catalog, options=%v", source.Options))
|
||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/adrg/xdg"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/presenter"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -23,7 +23,7 @@ type Application struct {
|
|||
ConfigPath string
|
||||
PresenterOpt presenter.Option
|
||||
Output string `mapstructure:"output"`
|
||||
ScopeOpt scope.Option
|
||||
ScopeOpt source.Scope
|
||||
Scope string `mapstructure:"scope"`
|
||||
Quiet bool `mapstructure:"quiet"`
|
||||
Log Logging `mapstructure:"log"`
|
||||
|
@ -79,9 +79,9 @@ func (cfg *Application) Build() error {
|
|||
}
|
||||
cfg.PresenterOpt = presenterOption
|
||||
|
||||
// set the scope
|
||||
scopeOption := scope.ParseOption(cfg.Scope)
|
||||
if scopeOption == scope.UnknownScope {
|
||||
// set the source
|
||||
scopeOption := source.ParseOption(cfg.Scope)
|
||||
if scopeOption == source.UnknownScope {
|
||||
return fmt.Errorf("bad --scope value '%s'", cfg.Scope)
|
||||
}
|
||||
cfg.ScopeOpt = scopeOption
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
@ -32,11 +32,11 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
|
|||
return &filesProcessed, &packagesDiscovered
|
||||
}
|
||||
|
||||
// Catalog a given scope (container image or filesystem) with the given catalogers, returning all discovered packages.
|
||||
// Catalog a given source (container image or filesystem) with the given catalogers, returning all discovered packages.
|
||||
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
|
||||
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
|
||||
// request.
|
||||
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
|
||||
func Catalog(resolver source.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
|
||||
catalog := pkg.NewCatalog()
|
||||
filesProcessed, packagesDiscovered := newMonitor()
|
||||
|
||||
|
@ -54,6 +54,8 @@ func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, er
|
|||
log.Debugf("cataloger '%s' discovered '%d' packages", theCataloger.Name(), catalogedPackages)
|
||||
packagesDiscovered.N += int64(catalogedPackages)
|
||||
|
||||
// helper function to add synthesized information...
|
||||
|
||||
for _, p := range packages {
|
||||
catalog.Add(p)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/anchore/syft/syft/cataloger/rpmdb"
|
||||
"github.com/anchore/syft/syft/cataloger/ruby"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// Cataloger describes behavior for an object to participate in parsing container image or file system
|
||||
|
@ -25,7 +25,7 @@ type Cataloger interface {
|
|||
// Name returns a string that uniquely describes a cataloger
|
||||
Name() string
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source.
|
||||
Catalog(resolver scope.Resolver) ([]pkg.Package, error)
|
||||
Catalog(resolver source.Resolver) ([]pkg.Package, error)
|
||||
}
|
||||
|
||||
// ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages.
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// GenericCataloger implements the Catalog interface and is responsible for dispatching the proper parser function for
|
||||
|
@ -53,7 +53,7 @@ func (c *GenericCataloger) clear() {
|
|||
}
|
||||
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source.
|
||||
func (c *GenericCataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) {
|
||||
func (c *GenericCataloger) Catalog(resolver source.Resolver) ([]pkg.Package, error) {
|
||||
fileSelection := c.selectFiles(resolver)
|
||||
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
|
||||
if err != nil {
|
||||
|
@ -63,7 +63,7 @@ func (c *GenericCataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, erro
|
|||
}
|
||||
|
||||
// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
|
||||
func (c *GenericCataloger) selectFiles(resolver scope.FileResolver) []file.Reference {
|
||||
func (c *GenericCataloger) selectFiles(resolver source.FileResolver) []file.Reference {
|
||||
// select by exact path
|
||||
for path, parser := range c.pathParsers {
|
||||
files, err := resolver.FilesByPath(file.Path(path))
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -33,7 +33,7 @@ func (c *Cataloger) Name() string {
|
|||
}
|
||||
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing dpkg support files.
|
||||
func (c *Cataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) {
|
||||
func (c *Cataloger) Catalog(resolver source.Resolver) ([]pkg.Package, error) {
|
||||
dbFileMatches, err := resolver.FilesByGlob(dpkgStatusGlob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find dpkg status files's by glob: %w", err)
|
||||
|
@ -93,7 +93,7 @@ func (c *Cataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) {
|
|||
return pkgs, nil
|
||||
}
|
||||
|
||||
func fetchMd5Contents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) {
|
||||
func fetchMd5Contents(resolver source.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) {
|
||||
// fetch all MD5 file contents. This approach is more efficient than fetching each MD5 file one at a time
|
||||
|
||||
var md5FileMatches []file.Reference
|
||||
|
@ -146,7 +146,7 @@ func fetchMd5Contents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.
|
|||
return contentsByName, refsByName, nil
|
||||
}
|
||||
|
||||
func fetchCopyrightContents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) {
|
||||
func fetchCopyrightContents(resolver source.Resolver, dbRef file.Reference, pkgs []pkg.Package) (map[string]io.Reader, map[string]file.Reference, error) {
|
||||
// fetch all copyright file contents. This approach is more efficient than fetching each copyright file one at a time
|
||||
|
||||
var copyrightFileMatches []file.Reference
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/go-test/deep"
|
||||
)
|
||||
|
||||
|
@ -54,7 +54,7 @@ func TestDpkgCataloger(t *testing.T) {
|
|||
img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-dpkg")
|
||||
defer cleanup()
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -33,7 +33,7 @@ func (c *PackageCataloger) Name() string {
|
|||
}
|
||||
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing python egg and wheel installations.
|
||||
func (c *PackageCataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) {
|
||||
func (c *PackageCataloger) Catalog(resolver source.Resolver) ([]pkg.Package, error) {
|
||||
// nolint:prealloc
|
||||
var fileMatches []file.Reference
|
||||
|
||||
|
@ -59,7 +59,7 @@ func (c *PackageCataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, erro
|
|||
}
|
||||
|
||||
// catalogEggOrWheel takes the primary metadata file reference and returns the python package it represents.
|
||||
func (c *PackageCataloger) catalogEggOrWheel(resolver scope.Resolver, metadataRef file.Reference) (*pkg.Package, error) {
|
||||
func (c *PackageCataloger) catalogEggOrWheel(resolver source.Resolver, metadataRef file.Reference) (*pkg.Package, error) {
|
||||
metadata, sources, err := c.assembleEggOrWheelMetadata(resolver, metadataRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -84,7 +84,7 @@ func (c *PackageCataloger) catalogEggOrWheel(resolver scope.Resolver, metadataRe
|
|||
}
|
||||
|
||||
// fetchRecordFiles finds a corresponding RECORD file for the given python package metadata file and returns the set of file records contained.
|
||||
func (c *PackageCataloger) fetchRecordFiles(resolver scope.Resolver, metadataRef file.Reference) (files []pkg.PythonFileRecord, sources []file.Reference, err error) {
|
||||
func (c *PackageCataloger) fetchRecordFiles(resolver source.Resolver, metadataRef file.Reference) (files []pkg.PythonFileRecord, sources []file.Reference, err error) {
|
||||
// we've been given a file reference to a specific wheel METADATA file. note: this may be for a directory
|
||||
// or for an image... for an image the METADATA file may be present within multiple layers, so it is important
|
||||
// to reconcile the RECORD path to the same layer (or the next adjacent lower layer).
|
||||
|
@ -116,7 +116,7 @@ func (c *PackageCataloger) fetchRecordFiles(resolver scope.Resolver, metadataRef
|
|||
}
|
||||
|
||||
// fetchTopLevelPackages finds a corresponding top_level.txt file for the given python package metadata file and returns the set of package names contained.
|
||||
func (c *PackageCataloger) fetchTopLevelPackages(resolver scope.Resolver, metadataRef file.Reference) (pkgs []string, sources []file.Reference, err error) {
|
||||
func (c *PackageCataloger) fetchTopLevelPackages(resolver source.Resolver, metadataRef file.Reference) (pkgs []string, sources []file.Reference, err error) {
|
||||
// a top_level.txt file specifies the python top-level packages (provided by this python package) installed into site-packages
|
||||
parentDir := filepath.Dir(string(metadataRef.Path))
|
||||
topLevelPath := filepath.Join(parentDir, "top_level.txt")
|
||||
|
@ -149,7 +149,7 @@ func (c *PackageCataloger) fetchTopLevelPackages(resolver scope.Resolver, metada
|
|||
}
|
||||
|
||||
// assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from.
|
||||
func (c *PackageCataloger) assembleEggOrWheelMetadata(resolver scope.Resolver, metadataRef file.Reference) (*pkg.PythonPackageMetadata, []file.Reference, error) {
|
||||
func (c *PackageCataloger) assembleEggOrWheelMetadata(resolver source.Resolver, metadataRef file.Reference) (*pkg.PythonPackageMetadata, []file.Reference, error) {
|
||||
var sources = []file.Reference{metadataRef}
|
||||
|
||||
metadataContents, err := resolver.FileContentsByRef(metadataRef)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,7 +28,7 @@ func (c *Cataloger) Name() string {
|
|||
}
|
||||
|
||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation.
|
||||
func (c *Cataloger) Catalog(resolver scope.Resolver) ([]pkg.Package, error) {
|
||||
func (c *Cataloger) Catalog(resolver source.Resolver) ([]pkg.Package, error) {
|
||||
fileMatches, err := resolver.FilesByGlob(packagesGlob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find rpmdb's by glob: %w", err)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/file"
|
||||
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
|
||||
rpmdb "github.com/anchore/go-rpmdb/pkg"
|
||||
"github.com/anchore/syft/internal"
|
||||
|
@ -80,7 +81,7 @@ func parseRpmDB(resolver scope.FileResolver, dbRef file.Reference, reader io.Rea
|
|||
return allPkgs, nil
|
||||
}
|
||||
|
||||
func extractRpmdbFileRecords(resolver scope.FileResolver, entry *rpmdb.PackageInfo) ([]pkg.RpmdbFileRecord, error) {
|
||||
func extractRpmdbFileRecords(resolver source.FileResolver, entry *rpmdb.PackageInfo) ([]pkg.RpmdbFileRecord, error) {
|
||||
var records = make([]pkg.RpmdbFileRecord, 0)
|
||||
|
||||
for _, record := range entry.Files {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// returns a distro or nil
|
||||
|
@ -18,7 +18,7 @@ type parseEntry struct {
|
|||
}
|
||||
|
||||
// Identify parses distro-specific files to determine distro metadata like version and release.
|
||||
func Identify(resolver scope.Resolver) Distro {
|
||||
func Identify(resolver source.Resolver) Distro {
|
||||
distro := NewUnknownDistro()
|
||||
|
||||
identityFiles := []parseEntry{
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/anchore/syft/internal"
|
||||
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestIdentifyDistro(t *testing.T) {
|
||||
|
@ -84,9 +84,9 @@ func TestIdentifyDistro(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
s, err := scope.NewScopeFromDir(test.fixture)
|
||||
s, err := source.NewFromDirectory(test.fixture)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to produce a new scope for testing: %s", test.fixture)
|
||||
t.Fatalf("unable to produce a new source for testing: %s", test.fixture)
|
||||
}
|
||||
|
||||
d := Identify(s.Resolver)
|
||||
|
|
26
syft/lib.go
26
syft/lib.go
|
@ -7,8 +7,8 @@ Here is what the main execution path for syft does:
|
|||
2. Invoke all catalogers to catalog the image, adding discovered packages to a single catalog object
|
||||
3. Invoke a single presenter to show the contents of the catalog
|
||||
|
||||
A Scope object encapsulates the image object to be cataloged and the user options (catalog all layers vs. squashed layer),
|
||||
providing a way to inspect paths and file content within the image. The Scope object, not the image object, is used
|
||||
A Source object encapsulates the image object to be cataloged and the user options (catalog all layers vs. squashed layer),
|
||||
providing a way to inspect paths and file content within the image. The Source object, not the image object, is used
|
||||
throughout the main execution path. This abstraction allows for decoupling of what is cataloged (a docker image, an OCI
|
||||
image, a filesystem, etc) and how it is cataloged (the individual catalogers).
|
||||
|
||||
|
@ -28,15 +28,15 @@ import (
|
|||
"github.com/anchore/syft/syft/logger"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
jsonPresenter "github.com/anchore/syft/syft/presenter/json"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
)
|
||||
|
||||
// Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered
|
||||
// set of packages, the identified Linux distribution, and the scope object used to wrap the data source.
|
||||
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
|
||||
// Catalog the given image from a particular perspective (e.g. squashed source, all-layers source). Returns the discovered
|
||||
// set of packages, the identified Linux distribution, and the source object used to wrap the data source.
|
||||
func Catalog(userInput string, scoptOpt source.Scope) (*pkg.Catalog, *source.Source, *distro.Distro, error) {
|
||||
log.Info("cataloging image")
|
||||
s, cleanup, err := scope.NewScope(userInput, scoptOpt)
|
||||
s, cleanup, err := source.NewSource(userInput, scoptOpt)
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -53,8 +53,8 @@ func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scop
|
|||
}
|
||||
|
||||
// IdentifyDistro attempts to discover what the underlying Linux distribution may be from the available flat files
|
||||
// provided by the given scope object. If results are inconclusive a "UnknownDistro" Type is returned.
|
||||
func IdentifyDistro(s scope.Scope) distro.Distro {
|
||||
// provided by the given source object. If results are inconclusive a "UnknownDistro" Type is returned.
|
||||
func IdentifyDistro(s source.Source) distro.Distro {
|
||||
d := distro.Identify(s.Resolver)
|
||||
if d.Type != distro.UnknownDistroType {
|
||||
log.Infof("identified distro: %s", d.String())
|
||||
|
@ -64,16 +64,16 @@ func IdentifyDistro(s scope.Scope) distro.Distro {
|
|||
return d
|
||||
}
|
||||
|
||||
// Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages.
|
||||
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
|
||||
// Catalog the given source, which may represent a container image or filesystem. Returns the discovered set of packages.
|
||||
func CatalogFromScope(s source.Source) (*pkg.Catalog, error) {
|
||||
log.Info("building the catalog")
|
||||
|
||||
// conditionally have two sets of catalogers
|
||||
var catalogers []cataloger.Cataloger
|
||||
switch s.Scheme {
|
||||
case scope.ImageScheme:
|
||||
case source.ImageScheme:
|
||||
catalogers = cataloger.ImageCatalogers()
|
||||
case scope.DirectoryScheme:
|
||||
case source.DirectoryScheme:
|
||||
catalogers = cataloger.DirectoryCatalogers()
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme)
|
||||
|
|
|
@ -17,11 +17,12 @@ type ID int64
|
|||
|
||||
// Package represents an application or library that has been bundled into a distributable format.
|
||||
type Package struct {
|
||||
id ID // uniquely identifies a package, set by the cataloger
|
||||
Name string `json:"manifest"` // the package name
|
||||
Version string `json:"version"` // the version of the package
|
||||
FoundBy string `json:"foundBy"` // the specific cataloger that discovered this package
|
||||
Source []file.Reference `json:"sources"` // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||
id ID // uniquely identifies a package, set by the cataloger
|
||||
Name string `json:"manifest"` // the package name
|
||||
Version string `json:"version"` // the version of the package
|
||||
FoundBy string `json:"foundBy"` // the specific cataloger that discovered this package
|
||||
Source []file.Reference `json:"-"` // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||
Location interface{} `json:"locations"`
|
||||
// TODO: should we move licenses into metadata?
|
||||
Licenses []string `json:"licenses"` // licenses discovered with the package metadata
|
||||
Language Language `json:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
||||
|
|
|
@ -15,7 +15,7 @@ type Component struct {
|
|||
Description string `xml:"description,omitempty"` // A description of the component
|
||||
Licenses *[]License `xml:"licenses>license"` // A node describing zero or more license names, SPDX license IDs or expressions
|
||||
PackageURL string `xml:"purl,omitempty"` // Specifies the package-url (PackageURL). The purl, if specified, must be valid and conform to the specification defined at: https://github.com/package-url/purl-spec
|
||||
// TODO: scope, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences
|
||||
// TODO: source, hashes, copyright, cpe, purl, swid, modified, pedigree, externalReferences
|
||||
// TODO: add user-defined parameters for syft-specific values (image layer index, cataloger, location path, etc.)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,21 +11,21 @@ import (
|
|||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// Presenter writes a CycloneDX report from the given Catalog and Scope contents
|
||||
// Presenter writes a CycloneDX report from the given Catalog and Source contents
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
source source.Source
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
// NewPresenter creates a CycloneDX presenter from the given Catalog and Scope objects.
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Presenter {
|
||||
// NewPresenter creates a CycloneDX presenter from the given Catalog and Source objects.
|
||||
func NewPresenter(catalog *pkg.Catalog, s source.Source, d distro.Distro) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
source: s,
|
||||
distro: d,
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ 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)
|
||||
|
||||
switch src := pres.scope.Source.(type) {
|
||||
case scope.DirSource:
|
||||
switch src := pres.source.Target.(type) {
|
||||
case source.DirSource:
|
||||
bom.BomDescriptor.Component = &BdComponent{
|
||||
Component: Component{
|
||||
Type: "file",
|
||||
|
@ -43,7 +43,7 @@ func (pres *Presenter) Present(output io.Writer) error {
|
|||
Version: "",
|
||||
},
|
||||
}
|
||||
case scope.ImageSource:
|
||||
case source.ImageSource:
|
||||
var imageID string
|
||||
var versionStr string
|
||||
if len(src.Img.Metadata.Tags) > 0 {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/anchore/go-testutils"
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
|
@ -57,7 +57,7 @@ func TestCycloneDxDirsPresenter(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromDir("/some/path")
|
||||
s, err := source.NewFromDirectory("/some/path")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ func TestCycloneDxImgsPresenter(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Artifact struct {
|
||||
|
@ -33,7 +33,7 @@ type ArtifactMetadataUnpacker struct {
|
|||
Metadata json.RawMessage `json:"metadata"`
|
||||
}
|
||||
|
||||
func NewArtifact(p *pkg.Package, s scope.Scope) (Artifact, error) {
|
||||
func NewArtifact(p *pkg.Package, s source.Source) (Artifact, error) {
|
||||
locations, err := NewLocations(p, s)
|
||||
if err != nil {
|
||||
return Artifact{}, err
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/anchore/syft/internal/version"
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Document struct {
|
||||
|
@ -22,7 +22,7 @@ type Descriptor struct {
|
|||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
ReportTimestamp string `json:"reportTimestamp"`
|
||||
// TODO: we should include scope option here as well (or in source)
|
||||
// TODO: we should include source option here as well (or in source)
|
||||
}
|
||||
|
||||
// Distribution provides information about a detected Linux Distribution
|
||||
|
@ -32,7 +32,7 @@ type Distribution struct {
|
|||
IDLike string `json:"idLike"`
|
||||
}
|
||||
|
||||
func NewDocument(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) (Document, error) {
|
||||
func NewDocument(catalog *pkg.Catalog, s source.Source, d distro.Distro) (Document, error) {
|
||||
src, err := NewSource(s)
|
||||
if err != nil {
|
||||
return Document{}, nil
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
|
@ -18,7 +18,7 @@ type Layer struct {
|
|||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func NewImage(src scope.ImageSource) *Image {
|
||||
func NewImage(src source.ImageSource) *Image {
|
||||
// populate artifacts...
|
||||
tags := make([]string, len(src.Img.Metadata.Tags))
|
||||
for idx, tag := range src.Img.Metadata.Tags {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Locations interface{}
|
||||
|
@ -14,9 +14,9 @@ type ImageLocation struct {
|
|||
LayerIndex uint `json:"layerIndex"`
|
||||
}
|
||||
|
||||
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
|
||||
switch src := s.Source.(type) {
|
||||
case scope.ImageSource:
|
||||
func NewLocations(p *pkg.Package, s source.Source) (Locations, error) {
|
||||
switch src := s.Target.(type) {
|
||||
case source.ImageSource:
|
||||
locations := make([]ImageLocation, len(p.Source))
|
||||
for idx := range p.Source {
|
||||
entry, err := src.Img.FileCatalog.Get(p.Source[idx])
|
||||
|
@ -33,7 +33,7 @@ func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
|
|||
}
|
||||
return locations, nil
|
||||
|
||||
case scope.DirSource:
|
||||
case source.DirSource:
|
||||
locations := make([]string, len(p.Source))
|
||||
for idx := range p.Source {
|
||||
locations[idx] = string(p.Source[idx].Path)
|
||||
|
|
|
@ -6,25 +6,25 @@ import (
|
|||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
source source.Source
|
||||
distro distro.Distro
|
||||
}
|
||||
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Presenter {
|
||||
func NewPresenter(catalog *pkg.Catalog, s source.Source, d distro.Distro) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
source: s,
|
||||
distro: d,
|
||||
}
|
||||
}
|
||||
|
||||
func (pres *Presenter) Present(output io.Writer) error {
|
||||
doc, err := NewDocument(pres.catalog, pres.scope, pres.distro)
|
||||
doc, err := NewDocument(pres.catalog, pres.source, pres.distro)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
|
@ -53,7 +53,7 @@ func TestJsonDirsPresenter(t *testing.T) {
|
|||
},
|
||||
})
|
||||
d := distro.NewUnknownDistro()
|
||||
s, err := scope.NewScopeFromDir("/some/path")
|
||||
s, err := source.NewFromDirectory("/some/path")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ func TestJsonImgsPresenter(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||
d := distro.NewUnknownDistro()
|
||||
pres := NewPresenter(catalog, s, d)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
|
@ -17,14 +17,14 @@ type SourceUnpacker struct {
|
|||
Target json.RawMessage `json:"target"`
|
||||
}
|
||||
|
||||
func NewSource(s scope.Scope) (Source, error) {
|
||||
switch src := s.Source.(type) {
|
||||
case scope.ImageSource:
|
||||
func NewSource(s source.Source) (Source, error) {
|
||||
switch src := s.Target.(type) {
|
||||
case source.ImageSource:
|
||||
return Source{
|
||||
Type: "image",
|
||||
Target: NewImage(src),
|
||||
}, nil
|
||||
case scope.DirSource:
|
||||
case source.DirSource:
|
||||
return Source{
|
||||
Type: "directory",
|
||||
Target: src.Path,
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/anchore/syft/syft/presenter/json"
|
||||
"github.com/anchore/syft/syft/presenter/table"
|
||||
"github.com/anchore/syft/syft/presenter/text"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// Presenter defines the expected behavior for an object responsible for displaying arbitrary input and processed data
|
||||
|
@ -25,7 +25,7 @@ type Presenter interface {
|
|||
}
|
||||
|
||||
// GetPresenter returns a presenter for images or directories
|
||||
func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog, d *distro.Distro) Presenter {
|
||||
func GetPresenter(option Option, s source.Source, catalog *pkg.Catalog, d *distro.Distro) Presenter {
|
||||
switch option {
|
||||
case JSONPresenter:
|
||||
return json.NewPresenter(catalog, s, *d)
|
||||
|
|
|
@ -9,18 +9,18 @@ import (
|
|||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
source source.Source
|
||||
}
|
||||
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter {
|
||||
func NewPresenter(catalog *pkg.Catalog, s source.Source) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
source: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,15 @@ package table
|
|||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"github.com/go-test/deep"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
|
||||
"github.com/anchore/go-testutils"
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
|
@ -43,7 +44,7 @@ func TestTablePresenter(t *testing.T) {
|
|||
Type: pkg.DebPkg,
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||
pres := NewPresenter(catalog, s)
|
||||
|
||||
// run presenter
|
||||
|
|
|
@ -7,18 +7,18 @@ import (
|
|||
"text/tabwriter"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
source source.Source
|
||||
}
|
||||
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter {
|
||||
func NewPresenter(catalog *pkg.Catalog, s source.Source) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
source: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,10 @@ func (pres *Presenter) Present(output io.Writer) error {
|
|||
w := new(tabwriter.Writer)
|
||||
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
|
||||
|
||||
switch src := pres.scope.Source.(type) {
|
||||
case scope.DirSource:
|
||||
switch src := pres.source.Target.(type) {
|
||||
case source.DirSource:
|
||||
fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path))
|
||||
case scope.ImageSource:
|
||||
case source.ImageSource:
|
||||
fmt.Fprintln(w, "[Image]")
|
||||
|
||||
for idx, l := range src.Img.Layers {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
|
@ -32,9 +32,9 @@ func TestTextDirPresenter(t *testing.T) {
|
|||
Type: pkg.DebPkg,
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromDir("/some/path")
|
||||
s, err := source.NewFromDirectory("/some/path")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create scope: %+v", err)
|
||||
t.Fatalf("unable to create source: %+v", err)
|
||||
}
|
||||
pres := NewPresenter(catalog, s)
|
||||
|
||||
|
@ -98,7 +98,7 @@ func TestTextImgPresenter(t *testing.T) {
|
|||
l.Metadata.Digest = "sha256:ad8ecdc058976c07e7e347cb89fa9ad86a294b5ceaae6d09713fb035f84115abf3c4a2388a4af3aa60f13b94f4c6846930bdf53"
|
||||
}
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
/*
|
||||
Package resolvers provides concrete implementations for the scope.Resolver interface for all supported data sources and scope options.
|
||||
*/
|
||||
package resolvers
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
Package scope provides an abstraction to allow a user to loosely define a data source to catalog and expose a common interface that
|
||||
catalogers and use explore and analyze data from the data source. All valid (cataloggable) data sources are defined
|
||||
within this package.
|
||||
*/
|
||||
package scope
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/scope/resolvers"
|
||||
)
|
||||
|
||||
const (
|
||||
UnknownScheme Scheme = "unknown-scheme"
|
||||
DirectoryScheme Scheme = "directory-scheme"
|
||||
ImageScheme Scheme = "image-scheme"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
|
||||
// ImageSource represents a data source that is a container image
|
||||
type ImageSource struct {
|
||||
Img *image.Image // the image object to be cataloged
|
||||
}
|
||||
|
||||
// DirSource represents a data source that is a filesystem directory tree
|
||||
type DirSource struct {
|
||||
Path string // the root path to be cataloged
|
||||
}
|
||||
|
||||
// Scope is an object that captures the data source to be cataloged, configuration, and a specific resolver used
|
||||
// in cataloging (based on the data source and configuration)
|
||||
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
|
||||
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
|
||||
func NewScope(userInput string, o Option) (Scope, func(), error) {
|
||||
fs := afero.NewOsFs()
|
||||
parsedScheme, location, err := detectScheme(fs, image.DetectSource, userInput)
|
||||
if err != nil {
|
||||
return Scope{}, func() {}, fmt.Errorf("unable to parse input=%q: %w", userInput, err)
|
||||
}
|
||||
|
||||
switch parsedScheme {
|
||||
case DirectoryScheme:
|
||||
fileMeta, err := fs.Stat(location)
|
||||
if err != nil {
|
||||
return Scope{}, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err)
|
||||
}
|
||||
|
||||
if !fileMeta.IsDir() {
|
||||
return Scope{}, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", location, err)
|
||||
}
|
||||
|
||||
s, err := NewScopeFromDir(location)
|
||||
if err != nil {
|
||||
return Scope{}, func() {}, fmt.Errorf("could not populate scope from path=%q: %w", location, err)
|
||||
}
|
||||
return s, func() {}, nil
|
||||
|
||||
case ImageScheme:
|
||||
img, err := stereoscope.GetImage(location)
|
||||
cleanup := func() {
|
||||
stereoscope.Cleanup()
|
||||
}
|
||||
|
||||
if err != nil || img == nil {
|
||||
return Scope{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", location, err)
|
||||
}
|
||||
|
||||
s, err := NewScopeFromImage(img, o)
|
||||
if err != nil {
|
||||
return Scope{}, cleanup, fmt.Errorf("could not populate scope with image: %w", err)
|
||||
}
|
||||
return s, cleanup, nil
|
||||
}
|
||||
|
||||
return Scope{}, func() {}, fmt.Errorf("unable to process input for scanning: '%s'", userInput)
|
||||
}
|
||||
|
||||
// NewScopeFromDir creates a new scope object tailored to catalog a given filesystem directory recursively.
|
||||
func NewScopeFromDir(path string) (Scope, error) {
|
||||
return Scope{
|
||||
Resolver: &resolvers.DirectoryResolver{
|
||||
Path: path,
|
||||
},
|
||||
Source: DirSource{
|
||||
Path: path,
|
||||
},
|
||||
Scheme: DirectoryScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewScopeFromImage creates a new scope object tailored to catalog a given container image, relative to the
|
||||
// option given (e.g. all-layers, squashed, etc)
|
||||
func NewScopeFromImage(img *image.Image, option Option) (Scope, error) {
|
||||
if img == nil {
|
||||
return Scope{}, fmt.Errorf("no image given")
|
||||
}
|
||||
|
||||
resolver, err := getImageResolver(img, option)
|
||||
if err != nil {
|
||||
return Scope{}, fmt.Errorf("could not determine file resolver: %w", err)
|
||||
}
|
||||
|
||||
return Scope{
|
||||
Option: option,
|
||||
Resolver: resolver,
|
||||
Source: ImageSource{
|
||||
Img: img,
|
||||
},
|
||||
Scheme: ImageScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type sourceDetector func(string) (image.Source, 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 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)
|
||||
}
|
||||
|
||||
if source == image.UnknownSource {
|
||||
dirLocation, err := homedir.Expand(userInput)
|
||||
if err != nil {
|
||||
return UnknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err)
|
||||
}
|
||||
|
||||
fileMeta, err := fs.Stat(dirLocation)
|
||||
if err != nil {
|
||||
return UnknownScheme, "", nil
|
||||
}
|
||||
|
||||
if fileMeta.IsDir() {
|
||||
return DirectoryScheme, dirLocation, nil
|
||||
}
|
||||
return UnknownScheme, "", nil
|
||||
}
|
||||
|
||||
return ImageScheme, imageSpec, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
|
||||
// AllLayersResolver implements path and content access for the AllLayers scope option for container image data sources.
|
||||
// AllLayersResolver implements path and content access for the AllLayers source option for container image data sources.
|
||||
type AllLayersResolver struct {
|
||||
img *image.Image
|
||||
layers []int
|
||||
|
@ -41,7 +41,7 @@ func (r *AllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.Ref
|
|||
|
||||
if entry.Metadata.TypeFlag == tar.TypeLink || entry.Metadata.TypeFlag == 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 scope
|
||||
// we should search all possible resolutions within the valid source
|
||||
for _, subLayerIdx := range r.layers[layerIdx:] {
|
||||
resolvedRef, err := r.img.ResolveLinkByLayerSquash(ref, subLayerIdx)
|
||||
if err != nil {
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
|
||||
// ImageSquashResolver implements path and content access for the Squashed scope option for container image data sources.
|
||||
// ImageSquashResolver implements path and content access for the Squashed source option for container image data sources.
|
||||
type ImageSquashResolver struct {
|
||||
img *image.Image
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package resolvers
|
||||
package source
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,11 +1,10 @@
|
|||
package scope
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/scope/resolvers"
|
||||
)
|
||||
|
||||
// Resolver is an interface that encompasses how to get specific file references and file contents for a generic data source.
|
||||
|
@ -32,13 +31,13 @@ type FileResolver interface {
|
|||
RelativeFileByPath(reference file.Reference, path string) (*file.Reference, error)
|
||||
}
|
||||
|
||||
// getImageResolver returns the appropriate resolve for a container image given the scope option
|
||||
func getImageResolver(img *image.Image, option Option) (Resolver, error) {
|
||||
// getImageResolver returns the appropriate resolve for a container image given the source option
|
||||
func getImageResolver(img *image.Image, option Scope) (Resolver, error) {
|
||||
switch option {
|
||||
case SquashedScope:
|
||||
return resolvers.NewImageSquashResolver(img)
|
||||
return NewImageSquashResolver(img)
|
||||
case AllLayersScope:
|
||||
return resolvers.NewAllLayersResolver(img)
|
||||
return NewAllLayersResolver(img)
|
||||
default:
|
||||
return nil, fmt.Errorf("bad option provided: %+v", option)
|
||||
}
|
55
syft/source/scheme.go
Normal file
55
syft/source/scheme.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
|
||||
const (
|
||||
UnknownScheme Scheme = "unknown-scheme"
|
||||
DirectoryScheme Scheme = "directory-scheme"
|
||||
ImageScheme Scheme = "image-scheme"
|
||||
)
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
if source == image.UnknownSource {
|
||||
dirLocation, err := homedir.Expand(userInput)
|
||||
if err != nil {
|
||||
return UnknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err)
|
||||
}
|
||||
|
||||
fileMeta, err := fs.Stat(dirLocation)
|
||||
if err != nil {
|
||||
return UnknownScheme, "", nil
|
||||
}
|
||||
|
||||
if fileMeta.IsDir() {
|
||||
return DirectoryScheme, dirLocation, nil
|
||||
}
|
||||
return UnknownScheme, "", nil
|
||||
}
|
||||
|
||||
return ImageScheme, imageSpec, nil
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
package scope
|
||||
package source
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
UnknownScope Option = iota
|
||||
UnknownScope Scope = iota
|
||||
SquashedScope
|
||||
AllLayersScope
|
||||
)
|
||||
|
||||
type Option int
|
||||
type Scope int
|
||||
|
||||
var optionStr = []string{
|
||||
"UnknownScope",
|
||||
|
@ -16,12 +16,12 @@ var optionStr = []string{
|
|||
"AllLayers",
|
||||
}
|
||||
|
||||
var Options = []Option{
|
||||
var Options = []Scope{
|
||||
SquashedScope,
|
||||
AllLayersScope,
|
||||
}
|
||||
|
||||
func ParseOption(userStr string) Option {
|
||||
func ParseOption(userStr string) Scope {
|
||||
switch strings.ToLower(userStr) {
|
||||
case strings.ToLower(SquashedScope.String()):
|
||||
return SquashedScope
|
||||
|
@ -31,7 +31,7 @@ func ParseOption(userStr string) Option {
|
|||
return UnknownScope
|
||||
}
|
||||
|
||||
func (o Option) String() string {
|
||||
func (o Scope) String() string {
|
||||
if int(o) >= len(optionStr) || o < 0 {
|
||||
return optionStr[0]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package scope
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -6,12 +6,12 @@ import (
|
|||
)
|
||||
|
||||
func TestOptionStringerBoundary(t *testing.T) {
|
||||
var _ fmt.Stringer = Option(0)
|
||||
var _ fmt.Stringer = Scope(0)
|
||||
|
||||
for _, c := range []int{-1, 0, 3} {
|
||||
option := Option(c)
|
||||
option := Scope(c)
|
||||
if option.String() != UnknownScope.String() {
|
||||
t.Errorf("expected Option(%d) to be unknown, found '%+v'", c, option)
|
||||
t.Errorf("expected Scope(%d) to be unknown, found '%+v'", c, option)
|
||||
}
|
||||
}
|
||||
}
|
117
syft/source/source.go
Normal file
117
syft/source/source.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Package source provides an abstraction to allow a user to loosely define a data source to catalog and expose a common interface that
|
||||
catalogers and use explore and analyze data from the data source. All valid (cataloggable) data sources are defined
|
||||
within this package.
|
||||
*/
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
|
||||
// ImageSource represents a data source that is a container image
|
||||
type ImageSource struct {
|
||||
Img *image.Image // the image object to be cataloged
|
||||
}
|
||||
|
||||
// DirSource represents a data source that is a filesystem directory tree
|
||||
type DirSource struct {
|
||||
Path string // the root path to be cataloged
|
||||
}
|
||||
|
||||
// Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used
|
||||
// in cataloging (based on the data source and configuration)
|
||||
type Source struct {
|
||||
Scope Scope // specific perspective to catalog
|
||||
Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
|
||||
Target interface{} // the specific source object to be cataloged
|
||||
Scheme Scheme // the source data scheme type (directory or image)
|
||||
}
|
||||
|
||||
type sourceDetector func(string) (image.Source, string, error)
|
||||
|
||||
// NewSource produces a Source based on userInput like dir: or image:tag
|
||||
func NewSource(userInput string, o Scope) (Source, func(), error) {
|
||||
fs := afero.NewOsFs()
|
||||
parsedScheme, location, err := detectScheme(fs, image.DetectSource, userInput)
|
||||
if err != nil {
|
||||
return Source{}, func() {}, fmt.Errorf("unable to parse input=%q: %w", userInput, err)
|
||||
}
|
||||
|
||||
switch parsedScheme {
|
||||
case DirectoryScheme:
|
||||
fileMeta, err := fs.Stat(location)
|
||||
if err != nil {
|
||||
return Source{}, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err)
|
||||
}
|
||||
|
||||
if !fileMeta.IsDir() {
|
||||
return Source{}, func() {}, fmt.Errorf("given path is not a directory (path=%q): %w", location, err)
|
||||
}
|
||||
|
||||
s, err := NewFromDirectory(location)
|
||||
if err != nil {
|
||||
return Source{}, func() {}, fmt.Errorf("could not populate source from path=%q: %w", location, err)
|
||||
}
|
||||
return s, func() {}, nil
|
||||
|
||||
case ImageScheme:
|
||||
img, err := stereoscope.GetImage(location)
|
||||
cleanup := func() {
|
||||
stereoscope.Cleanup()
|
||||
}
|
||||
|
||||
if err != nil || img == nil {
|
||||
return Source{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", location, err)
|
||||
}
|
||||
|
||||
s, err := NewFromImage(img, o)
|
||||
if err != nil {
|
||||
return Source{}, cleanup, fmt.Errorf("could not populate source with image: %w", err)
|
||||
}
|
||||
return s, cleanup, nil
|
||||
}
|
||||
|
||||
return Source{}, func() {}, fmt.Errorf("unable to process input for scanning: '%s'", userInput)
|
||||
}
|
||||
|
||||
// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively.
|
||||
func NewFromDirectory(path string) (Source, error) {
|
||||
return Source{
|
||||
Resolver: &DirectoryResolver{
|
||||
Path: path,
|
||||
},
|
||||
Target: DirSource{
|
||||
Path: path,
|
||||
},
|
||||
Scheme: DirectoryScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewFromImage creates a new source object tailored to catalog a given container image, relative to the
|
||||
// option given (e.g. all-layers, squashed, etc)
|
||||
func NewFromImage(img *image.Image, option Scope) (Source, error) {
|
||||
if img == nil {
|
||||
return Source{}, fmt.Errorf("no image given")
|
||||
}
|
||||
|
||||
resolver, err := getImageResolver(img, option)
|
||||
if err != nil {
|
||||
return Source{}, fmt.Errorf("could not determine file resolver: %w", err)
|
||||
}
|
||||
|
||||
return Source{
|
||||
Scope: option,
|
||||
Resolver: resolver,
|
||||
Target: ImageSource{
|
||||
Img: img,
|
||||
},
|
||||
Scheme: ImageScheme,
|
||||
}, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package scope
|
||||
package source
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func TestNewScopeFromImageFails(t *testing.T) {
|
||||
t.Run("no image given", func(t *testing.T) {
|
||||
_, err := NewScopeFromImage(nil, AllLayersScope)
|
||||
_, err := NewFromImage(nil, AllLayersScope)
|
||||
if err == nil {
|
||||
t.Errorf("expected an error condition but none was given")
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func TestNewScopeFromImageUnknownOption(t *testing.T) {
|
|||
img := image.Image{}
|
||||
|
||||
t.Run("unknown option is an error", func(t *testing.T) {
|
||||
_, err := NewScopeFromImage(&img, UnknownScope)
|
||||
_, err := NewFromImage(&img, UnknownScope)
|
||||
if err == nil {
|
||||
t.Errorf("expected an error condition but none was given")
|
||||
}
|
||||
|
@ -36,10 +36,10 @@ func TestNewScopeFromImage(t *testing.T) {
|
|||
Layers: []*image.Layer{layer},
|
||||
}
|
||||
|
||||
t.Run("create a new Scope object from image", func(t *testing.T) {
|
||||
_, err := NewScopeFromImage(&img, AllLayersScope)
|
||||
t.Run("create a new Source object from image", func(t *testing.T) {
|
||||
_, err := NewFromImage(&img, AllLayersScope)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error when creating a new Scope from img: %w", err)
|
||||
t.Errorf("unexpected error when creating a new Source from img: %w", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -79,13 +79,13 @@ func TestDirectoryScope(t *testing.T) {
|
|||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
p, err := NewScopeFromDir(test.input)
|
||||
p, err := NewFromDirectory(test.input)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("could not create NewDirScope: %w", err)
|
||||
}
|
||||
if p.Source.(DirSource).Path != test.input {
|
||||
t.Errorf("mismatched stringer: '%s' != '%s'", p.Source.(DirSource).Path, test.input)
|
||||
if p.Target.(DirSource).Path != test.input {
|
||||
t.Errorf("mismatched stringer: '%s' != '%s'", p.Target.(DirSource).Path, test.input)
|
||||
}
|
||||
|
||||
refs, err := p.Resolver.FilesByPath(test.inputPaths...)
|
||||
|
@ -123,7 +123,7 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
|
|||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
p, err := NewScopeFromDir(test.input)
|
||||
p, err := NewFromDirectory(test.input)
|
||||
if err != nil {
|
||||
t.Errorf("could not create NewDirScope: %w", err)
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func TestMultipleFileContentsByRefNoContents(t *testing.T) {
|
|||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
p, err := NewScopeFromDir(test.input)
|
||||
p, err := NewFromDirectory(test.input)
|
||||
if err != nil {
|
||||
t.Errorf("could not create NewDirScope: %w", err)
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ func TestFilesByGlob(t *testing.T) {
|
|||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
p, err := NewScopeFromDir(test.input)
|
||||
p, err := NewFromDirectory(test.input)
|
||||
if err != nil {
|
||||
t.Errorf("could not create NewDirScope: %w", err)
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/go-test/deep"
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,7 @@ func TestDistroImage(t *testing.T) {
|
|||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||
defer cleanup()
|
||||
|
||||
_, _, actualDistro, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
||||
_, _, actualDistro, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to catalog image: %+v", err)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/presenter/json"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/go-test/deep"
|
||||
)
|
||||
|
||||
|
@ -31,7 +31,7 @@ func TestCatalogFromJSON(t *testing.T) {
|
|||
tarPath := imagetest.GetFixtureImageTarPath(t, test.fixture)
|
||||
defer cleanup()
|
||||
|
||||
expectedCatalog, s, expectedDistro, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
||||
expectedCatalog, s, expectedDistro, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to catalog image: %+v", err)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/presenter"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
|
@ -53,7 +53,7 @@ func validateAgainstV1Schema(t *testing.T, json string) {
|
|||
}
|
||||
}
|
||||
|
||||
func testJsonSchema(t *testing.T, catalog *pkg.Catalog, theScope *scope.Scope, prefix string) {
|
||||
func testJsonSchema(t *testing.T, catalog *pkg.Catalog, theScope *source.Source, prefix string) {
|
||||
// make the json output example dir if it does not exist
|
||||
absJsonSchemaExamplesPath := path.Join(repoRoot(t), jsonSchemaExamplesPath)
|
||||
if _, err := os.Stat(absJsonSchemaExamplesPath); os.IsNotExist(err) {
|
||||
|
@ -101,7 +101,7 @@ func TestJsonSchemaImg(t *testing.T) {
|
|||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||
defer cleanup()
|
||||
|
||||
catalog, theScope, _, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
||||
catalog, theScope, _, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to catalog image: %+v", err)
|
||||
}
|
||||
|
@ -118,9 +118,9 @@ func TestJsonSchemaImg(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonSchemaDirs(t *testing.T) {
|
||||
catalog, theScope, _, err := syft.Catalog("dir:test-fixtures/image-pkg-coverage", scope.AllLayersScope)
|
||||
catalog, theScope, _, err := syft.Catalog("dir:test-fixtures/image-pkg-coverage", source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Errorf("unable to create scope from dir: %+v", err)
|
||||
t.Errorf("unable to create source from dir: %+v", err)
|
||||
}
|
||||
|
||||
var cases []testCase
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestPkgCoverageImage(t *testing.T) {
|
||||
|
@ -18,7 +18,7 @@ func TestPkgCoverageImage(t *testing.T) {
|
|||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||
defer cleanup()
|
||||
|
||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to catalog image: %+v", err)
|
||||
}
|
||||
|
@ -100,10 +100,10 @@ func TestPkgCoverageImage(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPkgCoverageDirectory(t *testing.T) {
|
||||
catalog, _, _, err := syft.Catalog("dir:test-fixtures/image-pkg-coverage", scope.AllLayersScope)
|
||||
catalog, _, _, err := syft.Catalog("dir:test-fixtures/image-pkg-coverage", source.AllLayersScope)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unable to create scope from dir: %+v", err)
|
||||
t.Errorf("unable to create source from dir: %+v", err)
|
||||
}
|
||||
|
||||
observedLanguages := internal.NewStringSet()
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func TestRegression212ApkBufferSize(t *testing.T) {
|
||||
|
@ -21,7 +21,7 @@ func TestRegression212ApkBufferSize(t *testing.T) {
|
|||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||
defer cleanup()
|
||||
|
||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, scope.SquashedScope)
|
||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, source.SquashedScope)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to catalog image: %+v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue