From 13296880cdafb378eda10b5c76909a584a5fdb2a Mon Sep 17 00:00:00 2001 From: Marco Deicas <60855123+mdeicas@users.noreply.github.com> Date: Mon, 22 Aug 2022 11:22:18 -0400 Subject: [PATCH] External sources configuration (#1158) --- cmd/syft/cli/options/packages.go | 9 ++++ internal/config/application.go | 4 +- internal/config/datasources.go | 11 +++++ syft/pkg/cataloger/alpm/cataloger.go | 5 +++ syft/pkg/cataloger/cataloger.go | 45 ++++++++++++++++--- syft/pkg/cataloger/cataloger_test.go | 34 +++++++++++--- .../pkg/cataloger/common/generic_cataloger.go | 5 +++ syft/pkg/cataloger/config.go | 5 ++- syft/pkg/cataloger/deb/cataloger.go | 5 +++ syft/pkg/cataloger/golang/binary_cataloger.go | 5 +++ syft/pkg/cataloger/portage/cataloger.go | 5 +++ .../pkg/cataloger/python/package_cataloger.go | 5 +++ syft/pkg/cataloger/rpmdb/cataloger.go | 5 +++ .../cataloger/rust/audit_binary_cataloger.go | 5 +++ 14 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 internal/config/datasources.go diff --git a/cmd/syft/cli/options/packages.go b/cmd/syft/cli/options/packages.go index 8891f814e918..93ff3ac85a72 100644 --- a/cmd/syft/cli/options/packages.go +++ b/cmd/syft/cli/options/packages.go @@ -26,6 +26,7 @@ type PackagesOptions struct { OverwriteExistingImage bool ImportTimeout uint Catalogers []string + ExternalSourcesEnabled bool } var _ Interface = (*PackagesOptions)(nil) @@ -70,9 +71,13 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { cmd.Flags().UintVarP(&o.ImportTimeout, "import-timeout", "", 30, "set a timeout duration (in seconds) for the upload to Anchore Enterprise") + cmd.Flags().BoolVarP(&o.ExternalSourcesEnabled, "external-sources-enabled", "", false, + "shut off any use of external sources during sbom generation (default false") + return bindPackageConfigOptions(cmd.Flags(), v) } +//nolint:funlen func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { // Formatting & Input options ////////////////////////////////////////////// @@ -104,6 +109,10 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { return err } + if err := v.BindPFlag("external_sources.external-sources-enabled", flags.Lookup("external-sources-enabled")); err != nil { + return err + } + // Upload options ////////////////////////////////////////////////////////// if err := v.BindPFlag("anchore.host", flags.Lookup("host")); err != nil { diff --git a/internal/config/application.go b/internal/config/application.go index d5898b0d103a..3a7a0e790cc2 100644 --- a/internal/config/application.go +++ b/internal/config/application.go @@ -57,6 +57,7 @@ type Application struct { Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"` Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"` Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` + ExternalSources ExternalSources `yaml:"external_sources" json:"external_sources" mapstructure:"external_sources"` } func (cfg Application) ToCatalogerConfig() cataloger.Config { @@ -66,7 +67,8 @@ func (cfg Application) ToCatalogerConfig() cataloger.Config { IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives, Scope: cfg.Package.Cataloger.ScopeOpt, }, - Catalogers: cfg.Catalogers, + Catalogers: cfg.Catalogers, + ExternalSourcesEnabled: cfg.ExternalSources.ExternalSourcesEnabled, } } diff --git a/internal/config/datasources.go b/internal/config/datasources.go new file mode 100644 index 000000000000..cc0e507be4fa --- /dev/null +++ b/internal/config/datasources.go @@ -0,0 +1,11 @@ +package config + +import "github.com/spf13/viper" + +type ExternalSources struct { + ExternalSourcesEnabled bool `yaml:"external-sources-enabled" json:"external-sources-enabled" mapstructure:"external-sources-enabled"` +} + +func (e ExternalSources) loadDefaultValues(v *viper.Viper) { + v.SetDefault("external-sources-enabled", false) +} diff --git a/syft/pkg/cataloger/alpm/cataloger.go b/syft/pkg/cataloger/alpm/cataloger.go index 87a7b285e89d..2e099df1468d 100644 --- a/syft/pkg/cataloger/alpm/cataloger.go +++ b/syft/pkg/cataloger/alpm/cataloger.go @@ -23,6 +23,11 @@ func (c *Cataloger) Name() string { return catalogerName } +// UsesExternalSources indicates that the alpmdb cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { fileMatches, err := resolver.FilesByGlob(pkg.AlpmDBGlob) diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index 57ddcf5cf33c..3170c70ea15e 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -41,6 +41,8 @@ type Cataloger interface { 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) + // UsesExternalSources returns if the cataloger uses external sources, such as querying a database + UsesExternalSources() bool } // ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages. @@ -58,7 +60,7 @@ func ImageCatalogers(cfg Config) []Cataloger { golang.NewGoModuleBinaryCataloger(), dotnet.NewDotnetDepsCataloger(), portage.NewPortageCataloger(), - }, cfg.Catalogers) + }, cfg) } // DirectoryCatalogers returns a slice of locally implemented catalogers that are fit for detecting packages from index files (and select installations) @@ -84,7 +86,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger { cpp.NewConanfileCataloger(), portage.NewPortageCataloger(), haskell.NewHackageCataloger(), - }, cfg.Catalogers) + }, cfg) } // AllCatalogers returns all implemented catalogers @@ -114,10 +116,20 @@ func AllCatalogers(cfg Config) []Cataloger { cpp.NewConanfileCataloger(), portage.NewPortageCataloger(), haskell.NewHackageCataloger(), - }, cfg.Catalogers) + }, cfg) } +// RequestedAllCatalogers returns true if all Catalogers have been requested. Takes into account cfg.ExternalSourcesEnabled func RequestedAllCatalogers(cfg Config) bool { + // if external sources are disabled, only return false if there actually are any catalogers that use external sources + if !cfg.ExternalSourcesEnabled { + for _, cat := range AllCatalogers(Config{Catalogers: []string{"all"}, ExternalSourcesEnabled: true}) { + if cat.UsesExternalSources() { + return false + } + } + } + for _, enableCatalogerPattern := range cfg.Catalogers { if enableCatalogerPattern == AllCatalogersPattern { return true @@ -126,14 +138,33 @@ func RequestedAllCatalogers(cfg Config) bool { return false } -func filterCatalogers(catalogers []Cataloger, enabledCatalogerPatterns []string) []Cataloger { +func filterForExternalSources(catalogers []Cataloger, cfg Config) []Cataloger { + if cfg.ExternalSourcesEnabled { + return catalogers + } + + var enabledCatalogers []Cataloger + for _, cataloger := range catalogers { + if !cataloger.UsesExternalSources() { + enabledCatalogers = append(enabledCatalogers, cataloger) + } else { + log.Infof("cataloger %v will not be used because external sources are disabled", cataloger.Name()) + } + } + + return enabledCatalogers +} + +func filterCatalogers(catalogers []Cataloger, cfg Config) []Cataloger { + enabledCatalogerPatterns := cfg.Catalogers + // if cataloger is not set, all applicable catalogers are enabled by default if len(enabledCatalogerPatterns) == 0 { - return catalogers + return filterForExternalSources(catalogers, cfg) } for _, enableCatalogerPattern := range enabledCatalogerPatterns { if enableCatalogerPattern == AllCatalogersPattern { - return catalogers + return filterForExternalSources(catalogers, cfg) } } var keepCatalogers []Cataloger @@ -144,7 +175,7 @@ func filterCatalogers(catalogers []Cataloger, enabledCatalogerPatterns []string) } log.Infof("skipping cataloger %q", cataloger.Name()) } - return keepCatalogers + return filterForExternalSources(keepCatalogers, cfg) } func contains(enabledPartial []string, catalogerName string) bool { diff --git a/syft/pkg/cataloger/cataloger_test.go b/syft/pkg/cataloger/cataloger_test.go index e47944dab823..7b6931804ba7 100644 --- a/syft/pkg/cataloger/cataloger_test.go +++ b/syft/pkg/cataloger/cataloger_test.go @@ -1,11 +1,12 @@ package cataloger import ( + "testing" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" "github.com/stretchr/testify/assert" - "testing" ) var _ Cataloger = (*dummy)(nil) @@ -22,12 +23,17 @@ func (d dummy) Catalog(_ source.FileResolver) ([]pkg.Package, []artifact.Relatio panic("not implemented") } +func (d dummy) UsesExternalSources() bool { + return false +} + func Test_filterCatalogers(t *testing.T) { tests := []struct { - name string - patterns []string - catalogers []string - want []string + name string + patterns []string + ExternalSourcesEnabled bool + catalogers []string + want []string }{ { name: "no filtering", @@ -142,6 +148,21 @@ func Test_filterCatalogers(t *testing.T) { "go-module-binary-cataloger", }, }, + { // Note: no catalogers with external sources are currently implemented + name: "external sources enabled", + patterns: []string{"all"}, + ExternalSourcesEnabled: true, + catalogers: []string{ + "ruby-gemspec-cataloger", + "python-package-cataloger", + "rekor-cataloger", + }, + want: []string{ + "ruby-gemspec-cataloger", + "python-package-cataloger", + "rekor-cataloger", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -149,7 +170,8 @@ func Test_filterCatalogers(t *testing.T) { for _, n := range tt.catalogers { catalogers = append(catalogers, dummy{name: n}) } - got := filterCatalogers(catalogers, tt.patterns) + cfg := Config{Catalogers: tt.patterns, ExternalSourcesEnabled: tt.ExternalSourcesEnabled} + got := filterCatalogers(catalogers, cfg) var gotNames []string for _, g := range got { gotNames = append(gotNames, g.Name()) diff --git a/syft/pkg/cataloger/common/generic_cataloger.go b/syft/pkg/cataloger/common/generic_cataloger.go index 5de3666bffab..beef2ab7409f 100644 --- a/syft/pkg/cataloger/common/generic_cataloger.go +++ b/syft/pkg/cataloger/common/generic_cataloger.go @@ -39,6 +39,11 @@ func (c *GenericCataloger) Name() string { return c.upstreamCataloger } +// UsesExternalSources indicates that any GenericCatalogor does not use external sources +func (c *GenericCataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { var packages []pkg.Package diff --git a/syft/pkg/cataloger/config.go b/syft/pkg/cataloger/config.go index 478fc292d111..20920b86ff5b 100644 --- a/syft/pkg/cataloger/config.go +++ b/syft/pkg/cataloger/config.go @@ -5,8 +5,9 @@ import ( ) type Config struct { - Search SearchConfig - Catalogers []string + Search SearchConfig + Catalogers []string + ExternalSourcesEnabled bool } func DefaultConfig() Config { diff --git a/syft/pkg/cataloger/deb/cataloger.go b/syft/pkg/cataloger/deb/cataloger.go index 4b9146fd73b9..c341f4ccc1b0 100644 --- a/syft/pkg/cataloger/deb/cataloger.go +++ b/syft/pkg/cataloger/deb/cataloger.go @@ -36,6 +36,11 @@ func (c *Cataloger) Name() string { return "dpkgdb-cataloger" } +// UsesExternalSources indicates that the dpkgdb cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { dbFileMatches, err := resolver.FilesByGlob(pkg.DpkgDBGlob) diff --git a/syft/pkg/cataloger/golang/binary_cataloger.go b/syft/pkg/cataloger/golang/binary_cataloger.go index 494c7da86c23..f6917e942d3e 100644 --- a/syft/pkg/cataloger/golang/binary_cataloger.go +++ b/syft/pkg/cataloger/golang/binary_cataloger.go @@ -28,6 +28,11 @@ func (c *Cataloger) Name() string { return catalogerName } +// UsesExternalSources indicates that the golang binary cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { var pkgs []pkg.Package diff --git a/syft/pkg/cataloger/portage/cataloger.go b/syft/pkg/cataloger/portage/cataloger.go index a8cd5b796460..632e986e9ab7 100644 --- a/syft/pkg/cataloger/portage/cataloger.go +++ b/syft/pkg/cataloger/portage/cataloger.go @@ -37,6 +37,11 @@ func (c *Cataloger) Name() string { return "portage-cataloger" } +// UsesExternalSources indicates that the portage cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing portage support files. func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { dbFileMatches, err := resolver.FilesByGlob(pkg.PortageDBGlob) diff --git a/syft/pkg/cataloger/python/package_cataloger.go b/syft/pkg/cataloger/python/package_cataloger.go index d58fe7ccd3b9..983d5969d88c 100644 --- a/syft/pkg/cataloger/python/package_cataloger.go +++ b/syft/pkg/cataloger/python/package_cataloger.go @@ -33,6 +33,11 @@ func (c *PackageCataloger) Name() string { return "python-package-cataloger" } +// UsesExternalSources indicates that the python package cataloger does not use external sources +func (c *PackageCataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { var fileMatches []source.Location diff --git a/syft/pkg/cataloger/rpmdb/cataloger.go b/syft/pkg/cataloger/rpmdb/cataloger.go index 74fdbfebb64c..67425802bbb2 100644 --- a/syft/pkg/cataloger/rpmdb/cataloger.go +++ b/syft/pkg/cataloger/rpmdb/cataloger.go @@ -27,6 +27,11 @@ func (c *Cataloger) Name() string { return catalogerName } +// UsesExternalSources indicates that the rpmdb cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // 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 source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { fileMatches, err := resolver.FilesByGlob(pkg.RpmDBGlob) diff --git a/syft/pkg/cataloger/rust/audit_binary_cataloger.go b/syft/pkg/cataloger/rust/audit_binary_cataloger.go index eeab99cf1d28..1508aebdb8fc 100644 --- a/syft/pkg/cataloger/rust/audit_binary_cataloger.go +++ b/syft/pkg/cataloger/rust/audit_binary_cataloger.go @@ -27,6 +27,11 @@ func (c *Cataloger) Name() string { return catalogerName } +// UsesExternalSources indicates that the audit binary cataloger does not use external sources +func (c *Cataloger) UsesExternalSources() bool { + return false +} + // Catalog identifies executables then attempts to read Rust dependency information from them func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { var pkgs []pkg.Package