Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add external sources configuration #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/syft/cli/options/packages.go
Expand Up @@ -26,6 +26,7 @@ type PackagesOptions struct {
OverwriteExistingImage bool
ImportTimeout uint
Catalogers []string
ExternalSourcesEnabled bool
}

var _ Interface = (*PackagesOptions)(nil)
Expand Down Expand Up @@ -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 //////////////////////////////////////////////

Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion internal/config/application.go
Expand Up @@ -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 {
Expand All @@ -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,
}
}

Expand Down
11 changes: 11 additions & 0 deletions 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)
}
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/alpm/cataloger.go
Expand Up @@ -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)
Expand Down
45 changes: 38 additions & 7 deletions syft/pkg/cataloger/cataloger.go
Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -84,7 +86,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
cpp.NewConanfileCataloger(),
portage.NewPortageCataloger(),
haskell.NewHackageCataloger(),
}, cfg.Catalogers)
}, cfg)
}

// AllCatalogers returns all implemented catalogers
Expand Down Expand Up @@ -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
}
}
}
mdeicas marked this conversation as resolved.
Show resolved Hide resolved

for _, enableCatalogerPattern := range cfg.Catalogers {
if enableCatalogerPattern == AllCatalogersPattern {
return true
Expand All @@ -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 {
mdeicas marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -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 {
Expand Down
34 changes: 28 additions & 6 deletions 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)
Expand All @@ -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",
Expand Down Expand Up @@ -142,14 +148,30 @@ 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) {
var catalogers []Cataloger
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())
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/common/generic_cataloger.go
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions syft/pkg/cataloger/config.go
Expand Up @@ -5,8 +5,9 @@ import (
)

type Config struct {
Search SearchConfig
Catalogers []string
Search SearchConfig
Catalogers []string
ExternalSourcesEnabled bool
}

func DefaultConfig() Config {
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/deb/cataloger.go
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/golang/binary_cataloger.go
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/portage/cataloger.go
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/python/package_cataloger.go
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/rpmdb/cataloger.go
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/rust/audit_binary_cataloger.go
Expand Up @@ -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
Expand Down