github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/cmd/syft/internal/options/catalog.go (about)

     1  package options
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/iancoleman/strcase"
     9  
    10  	"github.com/anchore/clio"
    11  	"github.com/anchore/fangs"
    12  	intFile "github.com/anchore/syft/internal/file"
    13  	"github.com/anchore/syft/internal/log"
    14  	"github.com/anchore/syft/syft"
    15  	"github.com/anchore/syft/syft/cataloging"
    16  	"github.com/anchore/syft/syft/cataloging/filecataloging"
    17  	"github.com/anchore/syft/syft/cataloging/pkgcataloging"
    18  	"github.com/anchore/syft/syft/file/cataloger/executable"
    19  	"github.com/anchore/syft/syft/file/cataloger/filecontent"
    20  	"github.com/anchore/syft/syft/pkg/cataloger/binary"
    21  	"github.com/anchore/syft/syft/pkg/cataloger/golang"
    22  	"github.com/anchore/syft/syft/pkg/cataloger/java"
    23  	"github.com/anchore/syft/syft/pkg/cataloger/javascript"
    24  	"github.com/anchore/syft/syft/pkg/cataloger/kernel"
    25  	"github.com/anchore/syft/syft/pkg/cataloger/python"
    26  	"github.com/anchore/syft/syft/source"
    27  )
    28  
    29  type Catalog struct {
    30  	// high-level cataloger configuration
    31  	Catalogers        []string            `yaml:"-" json:"catalogers" mapstructure:"catalogers"` // deprecated and not shown in yaml output
    32  	DefaultCatalogers []string            `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"`
    33  	SelectCatalogers  []string            `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"`
    34  	Package           packageConfig       `yaml:"package" json:"package" mapstructure:"package"`
    35  	File              fileConfig          `yaml:"file" json:"file" mapstructure:"file"`
    36  	Scope             string              `yaml:"scope" json:"scope" mapstructure:"scope"`
    37  	Parallelism       int                 `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
    38  	Relationships     relationshipsConfig `yaml:"relationships" json:"relationships" mapstructure:"relationships"`
    39  
    40  	// ecosystem-specific cataloger configuration
    41  	Golang      golangConfig      `yaml:"golang" json:"golang" mapstructure:"golang"`
    42  	Java        javaConfig        `yaml:"java" json:"java" mapstructure:"java"`
    43  	JavaScript  javaScriptConfig  `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
    44  	LinuxKernel linuxKernelConfig `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
    45  	Python      pythonConfig      `yaml:"python" json:"python" mapstructure:"python"`
    46  
    47  	// configuration for the source (the subject being analyzed)
    48  	Registry   registryConfig `yaml:"registry" json:"registry" mapstructure:"registry"`
    49  	From       []string       `yaml:"from" json:"from" mapstructure:"from"`
    50  	Platform   string         `yaml:"platform" json:"platform" mapstructure:"platform"`
    51  	Source     sourceConfig   `yaml:"source" json:"source" mapstructure:"source"`
    52  	Exclusions []string       `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
    53  }
    54  
    55  var _ interface {
    56  	clio.FlagAdder
    57  	clio.PostLoader
    58  	fangs.FieldDescriber
    59  } = (*Catalog)(nil)
    60  
    61  func DefaultCatalog() Catalog {
    62  	return Catalog{
    63  		Scope:         source.SquashedScope.String(),
    64  		Package:       defaultPackageConfig(),
    65  		LinuxKernel:   defaultLinuxKernelConfig(),
    66  		Golang:        defaultGolangConfig(),
    67  		File:          defaultFileConfig(),
    68  		Relationships: defaultRelationshipsConfig(),
    69  		Source:        defaultSourceConfig(),
    70  		Parallelism:   1,
    71  	}
    72  }
    73  
    74  func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
    75  	return syft.DefaultCreateSBOMConfig().
    76  		WithTool(id.Name, id.Version).
    77  		WithParallelism(cfg.Parallelism).
    78  		WithRelationshipsConfig(cfg.ToRelationshipsConfig()).
    79  		WithSearchConfig(cfg.ToSearchConfig()).
    80  		WithPackagesConfig(cfg.ToPackagesConfig()).
    81  		WithFilesConfig(cfg.ToFilesConfig()).
    82  		WithCatalogerSelection(
    83  			pkgcataloging.NewSelectionRequest().
    84  				WithDefaults(cfg.DefaultCatalogers...).
    85  				WithExpression(cfg.SelectCatalogers...),
    86  		)
    87  }
    88  
    89  func (cfg Catalog) ToSearchConfig() cataloging.SearchConfig {
    90  	return cataloging.SearchConfig{
    91  		Scope: source.ParseScope(cfg.Scope),
    92  	}
    93  }
    94  
    95  func (cfg Catalog) ToRelationshipsConfig() cataloging.RelationshipsConfig {
    96  	return cataloging.RelationshipsConfig{
    97  		PackageFileOwnership:        cfg.Relationships.PackageFileOwnership,
    98  		PackageFileOwnershipOverlap: cfg.Relationships.PackageFileOwnershipOverlap,
    99  		// note: this option was surfaced in the syft application configuration before this relationships section was added
   100  		ExcludeBinaryPackagesWithFileOwnershipOverlap: cfg.Package.ExcludeBinaryOverlapByOwnership,
   101  	}
   102  }
   103  
   104  func (cfg Catalog) ToFilesConfig() filecataloging.Config {
   105  	hashers, err := intFile.Hashers(cfg.File.Metadata.Digests...)
   106  	if err != nil {
   107  		log.WithFields("error", err).Warn("unable to configure file hashers")
   108  	}
   109  
   110  	return filecataloging.Config{
   111  		Selection: cfg.File.Metadata.Selection,
   112  		Hashers:   hashers,
   113  		Content: filecontent.Config{
   114  			Globs:              cfg.File.Content.Globs,
   115  			SkipFilesAboveSize: cfg.File.Content.SkipFilesAboveSize,
   116  		},
   117  		Executable: executable.Config{
   118  			MIMETypes: executable.DefaultConfig().MIMETypes,
   119  			Globs:     cfg.File.Executable.Globs,
   120  		},
   121  	}
   122  }
   123  
   124  func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
   125  	archiveSearch := cataloging.ArchiveSearchConfig{
   126  		IncludeIndexedArchives:   cfg.Package.SearchIndexedArchives,
   127  		IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
   128  	}
   129  	return pkgcataloging.Config{
   130  		Binary: binary.DefaultClassifierCatalogerConfig(),
   131  		Golang: golang.DefaultCatalogerConfig().
   132  			WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
   133  			WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
   134  			WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
   135  			WithProxy(cfg.Golang.Proxy).
   136  			WithNoProxy(cfg.Golang.NoProxy).
   137  			WithMainModuleVersion(
   138  				golang.DefaultMainModuleVersionConfig().
   139  					WithFromContents(cfg.Golang.MainModuleVersion.FromContents).
   140  					WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings).
   141  					WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
   142  			),
   143  		JavaScript: javascript.DefaultCatalogerConfig().
   144  			WithSearchRemoteLicenses(cfg.JavaScript.SearchRemoteLicenses).
   145  			WithNpmBaseURL(cfg.JavaScript.NpmBaseURL),
   146  		LinuxKernel: kernel.LinuxKernelCatalogerConfig{
   147  			CatalogModules: cfg.LinuxKernel.CatalogModules,
   148  		},
   149  		Python: python.CatalogerConfig{
   150  			GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
   151  		},
   152  		JavaArchive: java.DefaultArchiveCatalogerConfig().
   153  			WithUseNetwork(cfg.Java.UseNetwork).
   154  			WithMavenBaseURL(cfg.Java.MavenURL).
   155  			WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth),
   156  	}
   157  }
   158  
   159  func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
   160  	var validScopeValues []string
   161  	for _, scope := range source.AllScopes {
   162  		validScopeValues = append(validScopeValues, strcase.ToDelimited(string(scope), '-'))
   163  	}
   164  	flags.StringVarP(&cfg.Scope, "scope", "s",
   165  		fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues))
   166  
   167  	flags.StringArrayVarP(&cfg.From, "from", "",
   168  		"specify the source behavior to use (e.g. docker, registry, oci-dir, ...)")
   169  
   170  	flags.StringVarP(&cfg.Platform, "platform", "",
   171  		"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')")
   172  
   173  	flags.StringArrayVarP(&cfg.Exclusions, "exclude", "",
   174  		"exclude paths from being scanned using a glob expression")
   175  
   176  	flags.StringArrayVarP(&cfg.Catalogers, "catalogers", "",
   177  		"enable one or more package catalogers")
   178  
   179  	if pfp, ok := flags.(fangs.PFlagSetProvider); ok {
   180  		if err := pfp.PFlagSet().MarkDeprecated("catalogers", "use: override-default-catalogers and select-catalogers"); err != nil {
   181  			panic(err)
   182  		}
   183  	} else {
   184  		panic("unable to mark flags as deprecated")
   185  	}
   186  
   187  	flags.StringArrayVarP(&cfg.DefaultCatalogers, "override-default-catalogers", "",
   188  		"set the base set of catalogers to use (defaults to 'image' or 'directory' depending on the scan source)")
   189  
   190  	flags.StringArrayVarP(&cfg.SelectCatalogers, "select-catalogers", "",
   191  		"add, remove, and filter the catalogers to be used")
   192  
   193  	flags.StringVarP(&cfg.Source.Name, "source-name", "",
   194  		"set the name of the target being analyzed")
   195  
   196  	flags.StringVarP(&cfg.Source.Version, "source-version", "",
   197  		"set the version of the target being analyzed")
   198  
   199  	flags.StringVarP(&cfg.Source.BasePath, "base-path", "",
   200  		"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
   201  }
   202  
   203  func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) {
   204  	descriptions.Add(&cfg.Parallelism, "number of cataloger workers to run in parallel")
   205  }
   206  
   207  func (cfg *Catalog) PostLoad() error {
   208  	usingLegacyCatalogers := len(cfg.Catalogers) > 0
   209  	usingNewCatalogers := len(cfg.DefaultCatalogers) > 0 || len(cfg.SelectCatalogers) > 0
   210  
   211  	if usingLegacyCatalogers && usingNewCatalogers {
   212  		return fmt.Errorf("cannot use both 'catalogers' and 'select-catalogers'/'default-catalogers' flags")
   213  	}
   214  
   215  	flatten := func(l []string) []string {
   216  		var out []string
   217  		for _, v := range l {
   218  			for _, s := range strings.Split(v, ",") {
   219  				out = append(out, strings.TrimSpace(s))
   220  			}
   221  		}
   222  		sort.Strings(out)
   223  
   224  		return out
   225  	}
   226  
   227  	cfg.From = flatten(cfg.From)
   228  
   229  	cfg.Catalogers = flatten(cfg.Catalogers)
   230  	cfg.DefaultCatalogers = flatten(cfg.DefaultCatalogers)
   231  	cfg.SelectCatalogers = flatten(cfg.SelectCatalogers)
   232  
   233  	// for backwards compatibility
   234  	cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...)
   235  
   236  	s := source.ParseScope(cfg.Scope)
   237  	if s == source.UnknownScope {
   238  		return fmt.Errorf("bad scope value %q", cfg.Scope)
   239  	}
   240  
   241  	return nil
   242  }