mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 00:43:20 +01:00
rename scope to source
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
495fb0a45f
commit
9668341a14
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/presenter"
|
"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/stereoscope"
|
||||||
"github.com/anchore/syft/internal/config"
|
"github.com/anchore/syft/internal/config"
|
||||||
@ -49,8 +49,8 @@ func setGlobalCliOptions() {
|
|||||||
// scan options
|
// scan options
|
||||||
flag := "scope"
|
flag := "scope"
|
||||||
rootCmd.Flags().StringP(
|
rootCmd.Flags().StringP(
|
||||||
"scope", "s", scope.SquashedScope.String(),
|
"scope", "s", source.SquashedScope.String(),
|
||||||
fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
|
fmt.Sprintf("selection of layers to catalog, options=%v", source.Options))
|
||||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/presenter"
|
"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/mitchellh/go-homedir"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -23,7 +23,7 @@ type Application struct {
|
|||||||
ConfigPath string
|
ConfigPath string
|
||||||
PresenterOpt presenter.Option
|
PresenterOpt presenter.Option
|
||||||
Output string `mapstructure:"output"`
|
Output string `mapstructure:"output"`
|
||||||
ScopeOpt scope.Option
|
ScopeOpt source.Scope
|
||||||
Scope string `mapstructure:"scope"`
|
Scope string `mapstructure:"scope"`
|
||||||
Quiet bool `mapstructure:"quiet"`
|
Quiet bool `mapstructure:"quiet"`
|
||||||
Log Logging `mapstructure:"log"`
|
Log Logging `mapstructure:"log"`
|
||||||
@ -79,9 +79,9 @@ func (cfg *Application) Build() error {
|
|||||||
}
|
}
|
||||||
cfg.PresenterOpt = presenterOption
|
cfg.PresenterOpt = presenterOption
|
||||||
|
|
||||||
// set the scope
|
// set the source
|
||||||
scopeOption := scope.ParseOption(cfg.Scope)
|
scopeOption := source.ParseOption(cfg.Scope)
|
||||||
if scopeOption == scope.UnknownScope {
|
if scopeOption == source.UnknownScope {
|
||||||
return fmt.Errorf("bad --scope value '%s'", cfg.Scope)
|
return fmt.Errorf("bad --scope value '%s'", cfg.Scope)
|
||||||
}
|
}
|
||||||
cfg.ScopeOpt = scopeOption
|
cfg.ScopeOpt = scopeOption
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"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/hashicorp/go-multierror"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
@ -32,11 +32,11 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
|
|||||||
return &filesProcessed, &packagesDiscovered
|
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
|
// 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
|
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
|
||||||
// request.
|
// request.
|
||||||
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
|
func Catalog(resolver source.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
|
||||||
catalog := pkg.NewCatalog()
|
catalog := pkg.NewCatalog()
|
||||||
filesProcessed, packagesDiscovered := newMonitor()
|
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)
|
log.Debugf("cataloger '%s' discovered '%d' packages", theCataloger.Name(), catalogedPackages)
|
||||||
packagesDiscovered.N += int64(catalogedPackages)
|
packagesDiscovered.N += int64(catalogedPackages)
|
||||||
|
|
||||||
|
// helper function to add synthesized information...
|
||||||
|
|
||||||
for _, p := range packages {
|
for _, p := range packages {
|
||||||
catalog.Add(p)
|
catalog.Add(p)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/cataloger/rpmdb"
|
"github.com/anchore/syft/syft/cataloger/rpmdb"
|
||||||
"github.com/anchore/syft/syft/cataloger/ruby"
|
"github.com/anchore/syft/syft/cataloger/ruby"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"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
|
// 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 returns a string that uniquely describes a cataloger
|
||||||
Name() string
|
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 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.
|
// 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/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"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
|
// 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.
|
// 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)
|
fileSelection := c.selectFiles(resolver)
|
||||||
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
|
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
|
||||||
if err != nil {
|
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
|
// 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
|
// select by exact path
|
||||||
for path, parser := range c.pathParsers {
|
for path, parser := range c.pathParsers {
|
||||||
files, err := resolver.FilesByPath(file.Path(path))
|
files, err := resolver.FilesByPath(file.Path(path))
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// 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)
|
dbFileMatches, err := resolver.FilesByGlob(dpkgStatusGlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find dpkg status files's by glob: %w", err)
|
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
|
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
|
// fetch all MD5 file contents. This approach is more efficient than fetching each MD5 file one at a time
|
||||||
|
|
||||||
var md5FileMatches []file.Reference
|
var md5FileMatches []file.Reference
|
||||||
@ -146,7 +146,7 @@ func fetchMd5Contents(resolver scope.Resolver, dbRef file.Reference, pkgs []pkg.
|
|||||||
return contentsByName, refsByName, nil
|
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
|
// fetch all copyright file contents. This approach is more efficient than fetching each copyright file one at a time
|
||||||
|
|
||||||
var copyrightFileMatches []file.Reference
|
var copyrightFileMatches []file.Reference
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ func TestDpkgCataloger(t *testing.T) {
|
|||||||
img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-dpkg")
|
img, cleanup := imagetest.GetFixtureImage(t, "docker-archive", "image-dpkg")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// 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
|
// nolint:prealloc
|
||||||
var fileMatches []file.Reference
|
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.
|
// 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)
|
metadata, sources, err := c.assembleEggOrWheelMetadata(resolver, metadataRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// 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
|
// 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
|
// 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).
|
// 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.
|
// 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
|
// 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))
|
parentDir := filepath.Dir(string(metadataRef.Path))
|
||||||
topLevelPath := filepath.Join(parentDir, "top_level.txt")
|
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.
|
// 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}
|
var sources = []file.Reference{metadataRef}
|
||||||
|
|
||||||
metadataContents, err := resolver.FileContentsByRef(metadataRef)
|
metadataContents, err := resolver.FileContentsByRef(metadataRef)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// 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)
|
fileMatches, err := resolver.FilesByGlob(packagesGlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to find rpmdb's by glob: %w", err)
|
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/stereoscope/pkg/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/scope"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
|
||||||
rpmdb "github.com/anchore/go-rpmdb/pkg"
|
rpmdb "github.com/anchore/go-rpmdb/pkg"
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
@ -80,7 +81,7 @@ func parseRpmDB(resolver scope.FileResolver, dbRef file.Reference, reader io.Rea
|
|||||||
return allPkgs, nil
|
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)
|
var records = make([]pkg.RpmdbFileRecord, 0)
|
||||||
|
|
||||||
for _, record := range entry.Files {
|
for _, record := range entry.Files {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
// returns a distro or nil
|
// 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.
|
// 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()
|
distro := NewUnknownDistro()
|
||||||
|
|
||||||
identityFiles := []parseEntry{
|
identityFiles := []parseEntry{
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIdentifyDistro(t *testing.T) {
|
func TestIdentifyDistro(t *testing.T) {
|
||||||
@ -84,9 +84,9 @@ func TestIdentifyDistro(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.fixture, func(t *testing.T) {
|
t.Run(test.fixture, func(t *testing.T) {
|
||||||
s, err := scope.NewScopeFromDir(test.fixture)
|
s, err := source.NewFromDirectory(test.fixture)
|
||||||
if err != nil {
|
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)
|
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
|
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
|
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),
|
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 Scope object, not the image object, is used
|
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
|
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).
|
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/logger"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
jsonPresenter "github.com/anchore/syft/syft/presenter/json"
|
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"
|
"github.com/wagoodman/go-partybus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered
|
// 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 scope object used to wrap the data source.
|
// set of packages, the identified Linux distribution, and the source object used to wrap the data source.
|
||||||
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
|
func Catalog(userInput string, scoptOpt source.Scope) (*pkg.Catalog, *source.Source, *distro.Distro, error) {
|
||||||
log.Info("cataloging image")
|
log.Info("cataloging image")
|
||||||
s, cleanup, err := scope.NewScope(userInput, scoptOpt)
|
s, cleanup, err := source.NewSource(userInput, scoptOpt)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
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
|
// 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.
|
// provided by the given source object. If results are inconclusive a "UnknownDistro" Type is returned.
|
||||||
func IdentifyDistro(s scope.Scope) distro.Distro {
|
func IdentifyDistro(s source.Source) distro.Distro {
|
||||||
d := distro.Identify(s.Resolver)
|
d := distro.Identify(s.Resolver)
|
||||||
if d.Type != distro.UnknownDistroType {
|
if d.Type != distro.UnknownDistroType {
|
||||||
log.Infof("identified distro: %s", d.String())
|
log.Infof("identified distro: %s", d.String())
|
||||||
@ -64,16 +64,16 @@ func IdentifyDistro(s scope.Scope) distro.Distro {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages.
|
// Catalog the given source, which may represent a container image or filesystem. Returns the discovered set of packages.
|
||||||
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
|
func CatalogFromScope(s source.Source) (*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
|
||||||
switch s.Scheme {
|
switch s.Scheme {
|
||||||
case scope.ImageScheme:
|
case source.ImageScheme:
|
||||||
catalogers = cataloger.ImageCatalogers()
|
catalogers = cataloger.ImageCatalogers()
|
||||||
case scope.DirectoryScheme:
|
case source.DirectoryScheme:
|
||||||
catalogers = cataloger.DirectoryCatalogers()
|
catalogers = cataloger.DirectoryCatalogers()
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme)
|
return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme)
|
||||||
|
|||||||
@ -21,7 +21,8 @@ type Package struct {
|
|||||||
Name string `json:"manifest"` // the package name
|
Name string `json:"manifest"` // the package name
|
||||||
Version string `json:"version"` // the version of the package
|
Version string `json:"version"` // the version of the package
|
||||||
FoundBy string `json:"foundBy"` // the specific cataloger that discovered this 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)
|
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?
|
// TODO: should we move licenses into metadata?
|
||||||
Licenses []string `json:"licenses"` // licenses discovered with the package 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)
|
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
|
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
|
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
|
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.)
|
// 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/distro"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"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 {
|
type Presenter struct {
|
||||||
catalog *pkg.Catalog
|
catalog *pkg.Catalog
|
||||||
scope scope.Scope
|
source source.Source
|
||||||
distro distro.Distro
|
distro distro.Distro
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPresenter creates a CycloneDX presenter from the given Catalog and Scope objects.
|
// NewPresenter creates a CycloneDX presenter from the given Catalog and Source objects.
|
||||||
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{
|
return &Presenter{
|
||||||
catalog: catalog,
|
catalog: catalog,
|
||||||
scope: s,
|
source: s,
|
||||||
distro: d,
|
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 {
|
func (pres *Presenter) Present(output io.Writer) error {
|
||||||
bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
|
bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
|
||||||
|
|
||||||
switch src := pres.scope.Source.(type) {
|
switch src := pres.source.Target.(type) {
|
||||||
case scope.DirSource:
|
case source.DirSource:
|
||||||
bom.BomDescriptor.Component = &BdComponent{
|
bom.BomDescriptor.Component = &BdComponent{
|
||||||
Component: Component{
|
Component: Component{
|
||||||
Type: "file",
|
Type: "file",
|
||||||
@ -43,7 +43,7 @@ func (pres *Presenter) Present(output io.Writer) error {
|
|||||||
Version: "",
|
Version: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case scope.ImageSource:
|
case source.ImageSource:
|
||||||
var imageID string
|
var imageID string
|
||||||
var versionStr string
|
var versionStr string
|
||||||
if len(src.Img.Metadata.Tags) > 0 {
|
if len(src.Img.Metadata.Tags) > 0 {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/anchore/go-testutils"
|
"github.com/anchore/go-testutils"
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Artifact struct {
|
type Artifact struct {
|
||||||
@ -33,7 +33,7 @@ type ArtifactMetadataUnpacker struct {
|
|||||||
Metadata json.RawMessage `json:"metadata"`
|
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)
|
locations, err := NewLocations(p, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Artifact{}, err
|
return Artifact{}, err
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/anchore/syft/internal/version"
|
"github.com/anchore/syft/internal/version"
|
||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Document struct {
|
type Document struct {
|
||||||
@ -22,7 +22,7 @@ type Descriptor struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ReportTimestamp string `json:"reportTimestamp"`
|
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
|
// Distribution provides information about a detected Linux Distribution
|
||||||
@ -32,7 +32,7 @@ type Distribution struct {
|
|||||||
IDLike string `json:"idLike"`
|
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)
|
src, err := NewSource(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Document{}, nil
|
return Document{}, nil
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
@ -18,7 +18,7 @@ type Layer struct {
|
|||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImage(src scope.ImageSource) *Image {
|
func NewImage(src source.ImageSource) *Image {
|
||||||
// populate artifacts...
|
// populate artifacts...
|
||||||
tags := make([]string, len(src.Img.Metadata.Tags))
|
tags := make([]string, len(src.Img.Metadata.Tags))
|
||||||
for idx, tag := range src.Img.Metadata.Tags {
|
for idx, tag := range src.Img.Metadata.Tags {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Locations interface{}
|
type Locations interface{}
|
||||||
@ -14,9 +14,9 @@ type ImageLocation struct {
|
|||||||
LayerIndex uint `json:"layerIndex"`
|
LayerIndex uint `json:"layerIndex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
|
func NewLocations(p *pkg.Package, s source.Source) (Locations, error) {
|
||||||
switch src := s.Source.(type) {
|
switch src := s.Target.(type) {
|
||||||
case scope.ImageSource:
|
case source.ImageSource:
|
||||||
locations := make([]ImageLocation, len(p.Source))
|
locations := make([]ImageLocation, len(p.Source))
|
||||||
for idx := range p.Source {
|
for idx := range p.Source {
|
||||||
entry, err := src.Img.FileCatalog.Get(p.Source[idx])
|
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
|
return locations, nil
|
||||||
|
|
||||||
case scope.DirSource:
|
case source.DirSource:
|
||||||
locations := make([]string, len(p.Source))
|
locations := make([]string, len(p.Source))
|
||||||
for idx := range p.Source {
|
for idx := range p.Source {
|
||||||
locations[idx] = string(p.Source[idx].Path)
|
locations[idx] = string(p.Source[idx].Path)
|
||||||
|
|||||||
@ -6,25 +6,25 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Presenter struct {
|
type Presenter struct {
|
||||||
catalog *pkg.Catalog
|
catalog *pkg.Catalog
|
||||||
scope scope.Scope
|
source source.Source
|
||||||
distro distro.Distro
|
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{
|
return &Presenter{
|
||||||
catalog: catalog,
|
catalog: catalog,
|
||||||
scope: s,
|
source: s,
|
||||||
distro: d,
|
distro: d,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pres *Presenter) Present(output io.Writer) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ func TestJsonDirsPresenter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
d := distro.NewUnknownDistro()
|
d := distro.NewUnknownDistro()
|
||||||
s, err := scope.NewScopeFromDir("/some/path")
|
s, err := source.NewFromDirectory("/some/path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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()
|
d := distro.NewUnknownDistro()
|
||||||
pres := NewPresenter(catalog, s, d)
|
pres := NewPresenter(catalog, s, d)
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Source struct {
|
type Source struct {
|
||||||
@ -17,14 +17,14 @@ type SourceUnpacker struct {
|
|||||||
Target json.RawMessage `json:"target"`
|
Target json.RawMessage `json:"target"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSource(s scope.Scope) (Source, error) {
|
func NewSource(s source.Source) (Source, error) {
|
||||||
switch src := s.Source.(type) {
|
switch src := s.Target.(type) {
|
||||||
case scope.ImageSource:
|
case source.ImageSource:
|
||||||
return Source{
|
return Source{
|
||||||
Type: "image",
|
Type: "image",
|
||||||
Target: NewImage(src),
|
Target: NewImage(src),
|
||||||
}, nil
|
}, nil
|
||||||
case scope.DirSource:
|
case source.DirSource:
|
||||||
return Source{
|
return Source{
|
||||||
Type: "directory",
|
Type: "directory",
|
||||||
Target: src.Path,
|
Target: src.Path,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/presenter/json"
|
"github.com/anchore/syft/syft/presenter/json"
|
||||||
"github.com/anchore/syft/syft/presenter/table"
|
"github.com/anchore/syft/syft/presenter/table"
|
||||||
"github.com/anchore/syft/syft/presenter/text"
|
"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
|
// 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
|
// 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 {
|
switch option {
|
||||||
case JSONPresenter:
|
case JSONPresenter:
|
||||||
return json.NewPresenter(catalog, s, *d)
|
return json.NewPresenter(catalog, s, *d)
|
||||||
|
|||||||
@ -9,18 +9,18 @@ import (
|
|||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Presenter struct {
|
type Presenter struct {
|
||||||
catalog *pkg.Catalog
|
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{
|
return &Presenter{
|
||||||
catalog: catalog,
|
catalog: catalog,
|
||||||
scope: s,
|
source: s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,14 +3,15 @@ package table
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/go-test/deep"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
|
|
||||||
"github.com/anchore/go-testutils"
|
"github.com/anchore/go-testutils"
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ func TestTablePresenter(t *testing.T) {
|
|||||||
Type: pkg.DebPkg,
|
Type: pkg.DebPkg,
|
||||||
})
|
})
|
||||||
|
|
||||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||||
pres := NewPresenter(catalog, s)
|
pres := NewPresenter(catalog, s)
|
||||||
|
|
||||||
// run presenter
|
// run presenter
|
||||||
|
|||||||
@ -7,18 +7,18 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Presenter struct {
|
type Presenter struct {
|
||||||
catalog *pkg.Catalog
|
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{
|
return &Presenter{
|
||||||
catalog: catalog,
|
catalog: catalog,
|
||||||
scope: s,
|
source: s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ func (pres *Presenter) Present(output io.Writer) error {
|
|||||||
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)
|
||||||
|
|
||||||
switch src := pres.scope.Source.(type) {
|
switch src := pres.source.Target.(type) {
|
||||||
case scope.DirSource:
|
case source.DirSource:
|
||||||
fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path))
|
fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path))
|
||||||
case scope.ImageSource:
|
case source.ImageSource:
|
||||||
fmt.Fprintln(w, "[Image]")
|
fmt.Fprintln(w, "[Image]")
|
||||||
|
|
||||||
for idx, l := range src.Img.Layers {
|
for idx, l := range src.Img.Layers {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
"github.com/sergi/go-diff/diffmatchpatch"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,9 +32,9 @@ func TestTextDirPresenter(t *testing.T) {
|
|||||||
Type: pkg.DebPkg,
|
Type: pkg.DebPkg,
|
||||||
})
|
})
|
||||||
|
|
||||||
s, err := scope.NewScopeFromDir("/some/path")
|
s, err := source.NewFromDirectory("/some/path")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create scope: %+v", err)
|
t.Fatalf("unable to create source: %+v", err)
|
||||||
}
|
}
|
||||||
pres := NewPresenter(catalog, s)
|
pres := NewPresenter(catalog, s)
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ func TestTextImgPresenter(t *testing.T) {
|
|||||||
l.Metadata.Digest = "sha256:ad8ecdc058976c07e7e347cb89fa9ad86a294b5ceaae6d09713fb035f84115abf3c4a2388a4af3aa60f13b94f4c6846930bdf53"
|
l.Metadata.Digest = "sha256:ad8ecdc058976c07e7e347cb89fa9ad86a294b5ceaae6d09713fb035f84115abf3c4a2388a4af3aa60f13b94f4c6846930bdf53"
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
s, err := source.NewFromImage(img, source.AllLayersScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/anchore/stereoscope/pkg/image"
|
"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 {
|
type AllLayersResolver struct {
|
||||||
img *image.Image
|
img *image.Image
|
||||||
layers []int
|
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 {
|
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
|
// 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:] {
|
for _, subLayerIdx := range r.layers[layerIdx:] {
|
||||||
resolvedRef, err := r.img.ResolveLinkByLayerSquash(ref, subLayerIdx)
|
resolvedRef, err := r.img.ResolveLinkByLayerSquash(ref, subLayerIdx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package resolvers
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package resolvers
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package resolvers
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package resolvers
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/anchore/stereoscope/pkg/image"
|
"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 {
|
type ImageSquashResolver struct {
|
||||||
img *image.Image
|
img *image.Image
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package resolvers
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,11 +1,10 @@
|
|||||||
package scope
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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/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.
|
// 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)
|
RelativeFileByPath(reference file.Reference, path string) (*file.Reference, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getImageResolver returns the appropriate resolve for a container image given the scope option
|
// getImageResolver returns the appropriate resolve for a container image given the source option
|
||||||
func getImageResolver(img *image.Image, option Option) (Resolver, error) {
|
func getImageResolver(img *image.Image, option Scope) (Resolver, error) {
|
||||||
switch option {
|
switch option {
|
||||||
case SquashedScope:
|
case SquashedScope:
|
||||||
return resolvers.NewImageSquashResolver(img)
|
return NewImageSquashResolver(img)
|
||||||
case AllLayersScope:
|
case AllLayersScope:
|
||||||
return resolvers.NewAllLayersResolver(img)
|
return NewAllLayersResolver(img)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("bad option provided: %+v", option)
|
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"
|
import "strings"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UnknownScope Option = iota
|
UnknownScope Scope = iota
|
||||||
SquashedScope
|
SquashedScope
|
||||||
AllLayersScope
|
AllLayersScope
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option int
|
type Scope int
|
||||||
|
|
||||||
var optionStr = []string{
|
var optionStr = []string{
|
||||||
"UnknownScope",
|
"UnknownScope",
|
||||||
@ -16,12 +16,12 @@ var optionStr = []string{
|
|||||||
"AllLayers",
|
"AllLayers",
|
||||||
}
|
}
|
||||||
|
|
||||||
var Options = []Option{
|
var Options = []Scope{
|
||||||
SquashedScope,
|
SquashedScope,
|
||||||
AllLayersScope,
|
AllLayersScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseOption(userStr string) Option {
|
func ParseOption(userStr string) Scope {
|
||||||
switch strings.ToLower(userStr) {
|
switch strings.ToLower(userStr) {
|
||||||
case strings.ToLower(SquashedScope.String()):
|
case strings.ToLower(SquashedScope.String()):
|
||||||
return SquashedScope
|
return SquashedScope
|
||||||
@ -31,7 +31,7 @@ func ParseOption(userStr string) Option {
|
|||||||
return UnknownScope
|
return UnknownScope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Option) String() string {
|
func (o Scope) String() string {
|
||||||
if int(o) >= len(optionStr) || o < 0 {
|
if int(o) >= len(optionStr) || o < 0 {
|
||||||
return optionStr[0]
|
return optionStr[0]
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package scope
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -6,12 +6,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestOptionStringerBoundary(t *testing.T) {
|
func TestOptionStringerBoundary(t *testing.T) {
|
||||||
var _ fmt.Stringer = Option(0)
|
var _ fmt.Stringer = Scope(0)
|
||||||
|
|
||||||
for _, c := range []int{-1, 0, 3} {
|
for _, c := range []int{-1, 0, 3} {
|
||||||
option := Option(c)
|
option := Scope(c)
|
||||||
if option.String() != UnknownScope.String() {
|
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 (
|
import (
|
||||||
"os"
|
"os"
|
||||||
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func TestNewScopeFromImageFails(t *testing.T) {
|
func TestNewScopeFromImageFails(t *testing.T) {
|
||||||
t.Run("no image given", func(t *testing.T) {
|
t.Run("no image given", func(t *testing.T) {
|
||||||
_, err := NewScopeFromImage(nil, AllLayersScope)
|
_, err := NewFromImage(nil, AllLayersScope)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected an error condition but none was given")
|
t.Errorf("expected an error condition but none was given")
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ func TestNewScopeFromImageUnknownOption(t *testing.T) {
|
|||||||
img := image.Image{}
|
img := image.Image{}
|
||||||
|
|
||||||
t.Run("unknown option is an error", func(t *testing.T) {
|
t.Run("unknown option is an error", func(t *testing.T) {
|
||||||
_, err := NewScopeFromImage(&img, UnknownScope)
|
_, err := NewFromImage(&img, UnknownScope)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected an error condition but none was given")
|
t.Errorf("expected an error condition but none was given")
|
||||||
}
|
}
|
||||||
@ -36,10 +36,10 @@ func TestNewScopeFromImage(t *testing.T) {
|
|||||||
Layers: []*image.Layer{layer},
|
Layers: []*image.Layer{layer},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("create a new Scope object from image", func(t *testing.T) {
|
t.Run("create a new Source object from image", func(t *testing.T) {
|
||||||
_, err := NewScopeFromImage(&img, AllLayersScope)
|
_, err := NewFromImage(&img, AllLayersScope)
|
||||||
if err != nil {
|
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 {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
p, err := NewScopeFromDir(test.input)
|
p, err := NewFromDirectory(test.input)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not create NewDirScope: %w", err)
|
t.Errorf("could not create NewDirScope: %w", err)
|
||||||
}
|
}
|
||||||
if p.Source.(DirSource).Path != test.input {
|
if p.Target.(DirSource).Path != test.input {
|
||||||
t.Errorf("mismatched stringer: '%s' != '%s'", p.Source.(DirSource).Path, test.input)
|
t.Errorf("mismatched stringer: '%s' != '%s'", p.Target.(DirSource).Path, test.input)
|
||||||
}
|
}
|
||||||
|
|
||||||
refs, err := p.Resolver.FilesByPath(test.inputPaths...)
|
refs, err := p.Resolver.FilesByPath(test.inputPaths...)
|
||||||
@ -123,7 +123,7 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
p, err := NewScopeFromDir(test.input)
|
p, err := NewFromDirectory(test.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not create NewDirScope: %w", err)
|
t.Errorf("could not create NewDirScope: %w", err)
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ func TestMultipleFileContentsByRefNoContents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
p, err := NewScopeFromDir(test.input)
|
p, err := NewFromDirectory(test.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not create NewDirScope: %w", err)
|
t.Errorf("could not create NewDirScope: %w", err)
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ func TestFilesByGlob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
p, err := NewScopeFromDir(test.input)
|
p, err := NewFromDirectory(test.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not create NewDirScope: %w", err)
|
t.Errorf("could not create NewDirScope: %w", err)
|
||||||
}
|
}
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft"
|
"github.com/anchore/syft/syft"
|
||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ func TestDistroImage(t *testing.T) {
|
|||||||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
_, _, actualDistro, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
_, _, actualDistro, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft"
|
"github.com/anchore/syft/syft"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/presenter/json"
|
"github.com/anchore/syft/syft/presenter/json"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ func TestCatalogFromJSON(t *testing.T) {
|
|||||||
tarPath := imagetest.GetFixtureImageTarPath(t, test.fixture)
|
tarPath := imagetest.GetFixtureImageTarPath(t, test.fixture)
|
||||||
defer cleanup()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/presenter"
|
"github.com/anchore/syft/syft/presenter"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"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
|
// make the json output example dir if it does not exist
|
||||||
absJsonSchemaExamplesPath := path.Join(repoRoot(t), jsonSchemaExamplesPath)
|
absJsonSchemaExamplesPath := path.Join(repoRoot(t), jsonSchemaExamplesPath)
|
||||||
if _, err := os.Stat(absJsonSchemaExamplesPath); os.IsNotExist(err) {
|
if _, err := os.Stat(absJsonSchemaExamplesPath); os.IsNotExist(err) {
|
||||||
@ -101,7 +101,7 @@ func TestJsonSchemaImg(t *testing.T) {
|
|||||||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
@ -118,9 +118,9 @@ func TestJsonSchemaImg(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJsonSchemaDirs(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 {
|
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
|
var cases []testCase
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"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"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPkgCoverageImage(t *testing.T) {
|
func TestPkgCoverageImage(t *testing.T) {
|
||||||
@ -18,7 +18,7 @@ func TestPkgCoverageImage(t *testing.T) {
|
|||||||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, scope.AllLayersScope)
|
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, source.AllLayersScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
@ -100,10 +100,10 @@ func TestPkgCoverageImage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPkgCoverageDirectory(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 {
|
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()
|
observedLanguages := internal.NewStringSet()
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft"
|
"github.com/anchore/syft/syft"
|
||||||
"github.com/anchore/syft/syft/scope"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegression212ApkBufferSize(t *testing.T) {
|
func TestRegression212ApkBufferSize(t *testing.T) {
|
||||||
@ -21,7 +21,7 @@ func TestRegression212ApkBufferSize(t *testing.T) {
|
|||||||
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, scope.SquashedScope)
|
catalog, _, _, err := syft.Catalog("docker-archive:"+tarPath, source.SquashedScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user