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 }