mirror of
https://github.com/anchore/syft.git
synced 2026-11-02 07:26:34 +01:00
fix: make CreateSBOMConfig.Archive the one place to configure archive search
`ArchiveSearchConfig` lived in two places and each half was dead in one of them: the top-level booleans were never read (only java's copy was), and java's depth and limits did nothing. A library caller setting `Packages.JavaArchive.MaxDepth = 3` got no nested cataloging, and no error. - java's embedded `ArchiveSearchConfig` and `WithArchiveTraversal` are deprecated; `WithMaxParentRecursiveDepth` replaces the latter's other half - `CreateSBOM` now rejects `MaxDepth`, `MaxMemoryBytes` or `MaxDiskBytes` set on java's copy with an `invalid configuration` error - the top-level `IncludeIndexedArchives` / `IncludeUnindexedArchives` are overlaid onto a copy of java's config, and win when they differ from the default. Callers who only set java's copy see no change - `java.DefaultArchiveCatalogerConfig()` now carries only the booleans, so its descriptor no longer reports limits that java never used - the CLI fills only the top-level config, and the option descriptions now say the search booleans are ignored when nested cataloging is on Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -179,10 +179,6 @@ func (cfg Catalog) ToLicenseConfig() cataloging.LicenseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||||
archiveSearch := cataloging.ArchiveSearchConfig{
|
|
||||||
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
|
||||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
|
||||||
}
|
|
||||||
return pkgcataloging.Config{
|
return pkgcataloging.Config{
|
||||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||||
Cpp: cpp.DefaultCatalogerConfig().
|
Cpp: cpp.DefaultCatalogerConfig().
|
||||||
@@ -229,7 +225,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
|||||||
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
|
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
|
||||||
WithUseNetwork(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseNetwork)).
|
WithUseNetwork(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseNetwork)).
|
||||||
WithMavenBaseURL(cfg.Java.MavenURL).
|
WithMavenBaseURL(cfg.Java.MavenURL).
|
||||||
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth).
|
WithMaxParentRecursiveDepth(cfg.Java.MaxParentRecursiveDepth).
|
||||||
WithResolveTransitiveDependencies(cfg.Java.ResolveTransitiveDependencies),
|
WithResolveTransitiveDependencies(cfg.Java.ResolveTransitiveDependencies),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ var _ interface {
|
|||||||
|
|
||||||
func (o *packageConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
func (o *packageConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||||
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do contain a file index to search against (zip)
|
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do contain a file index to search against (zip)
|
||||||
note: for now this only applies to the java package cataloger`)
|
note: this only applies to the java package cataloger, and is ignored when nested-archive-max-depth is not 0 (every archive is searched)`)
|
||||||
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
|
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
|
||||||
note: enabling this may result in a performance impact since all discovered compressed tars will be decompressed
|
note: enabling this may result in a performance impact since all discovered compressed tars will be decompressed
|
||||||
note: for now this only applies to the java package cataloger`)
|
note: this only applies to the java package cataloger, and is ignored when nested-archive-max-depth is not 0 (every archive is searched)`)
|
||||||
descriptions.Add(&o.ExcludeBinaryOverlapByOwnership, `allows users to exclude synthetic binary packages from the sbom
|
descriptions.Add(&o.ExcludeBinaryOverlapByOwnership, `allows users to exclude synthetic binary packages from the sbom
|
||||||
these packages are removed if an overlap with a non-synthetic package is found`)
|
these packages are removed if an overlap with a non-synthetic package is found`)
|
||||||
descriptions.Add(&o.NestedArchiveMaxDepth, `maximum depth to recursively catalog nested archives (0 = disabled, -1 = unlimited)`)
|
descriptions.Add(&o.NestedArchiveMaxDepth, `maximum depth to recursively catalog nested archives (0 = disabled, -1 = unlimited)`)
|
||||||
|
|||||||
@@ -305,13 +305,8 @@ func TestArchiveCataloger(t *testing.T) {
|
|||||||
func TestArchiveCataloger_featureOff(t *testing.T) {
|
func TestArchiveCataloger_featureOff(t *testing.T) {
|
||||||
scanDir := writeFixture(t)
|
scanDir := writeFixture(t)
|
||||||
|
|
||||||
// java's squash-inlined MaxDepth is a config field a consumer can set: it must not enable the
|
|
||||||
// feature and must not be overwritten
|
|
||||||
supplied := java.DefaultArchiveCatalogerConfig()
|
|
||||||
supplied.ArchiveSearchConfig = supplied.ArchiveSearchConfig.WithMaxDepth(3)
|
|
||||||
cfg := DefaultCreateSBOMConfig().
|
cfg := DefaultCreateSBOMConfig().
|
||||||
WithCatalogerSelection(cataloging.NewSelectionRequest().WithDefaults("java")).
|
WithCatalogerSelection(cataloging.NewSelectionRequest().WithDefaults("java"))
|
||||||
WithPackagesConfig(pkgcataloging.Config{JavaArchive: supplied})
|
|
||||||
|
|
||||||
s := scanDirWithExclusions(t, scanDir, cfg, fixtureExclusions...)
|
s := scanDirWithExclusions(t, scanDir, cfg, fixtureExclusions...)
|
||||||
|
|
||||||
@@ -321,7 +316,41 @@ func TestArchiveCataloger_featureOff(t *testing.T) {
|
|||||||
"war-lib": {"app.war:WEB-INF/lib/war-lib-2.0.jar"},
|
"war-lib": {"app.war:WEB-INF/lib/war-lib-2.0.jar"},
|
||||||
"zip-lib": {"bundle.zip:lib/zip-lib-1.2.jar"},
|
"zip-lib": {"bundle.zip:lib/zip-lib-1.2.jar"},
|
||||||
}, javaVirtualPaths(s), "java's own recursion finds each once, under the virtual path the task also reports")
|
}, javaVirtualPaths(s), "java's own recursion finds each once, under the virtual path the task also reports")
|
||||||
assert.Equal(t, 3, cfg.Packages.JavaArchive.MaxDepth, "the consumer's value must survive")
|
|
||||||
|
t.Run("the top-level archive search booleans reach java", func(t *testing.T) {
|
||||||
|
cfg := DefaultCreateSBOMConfig().
|
||||||
|
WithCatalogerSelection(cataloging.NewSelectionRequest().WithDefaults("java")).
|
||||||
|
WithArchiveConfig(cataloging.DefaultArchiveSearchConfig().WithIncludeUnindexedArchives(true))
|
||||||
|
|
||||||
|
s := scanDirWithExclusions(t, scanDir, cfg, fixtureExclusions...)
|
||||||
|
|
||||||
|
assert.Contains(t, javaVirtualPaths(s), "tgz-lib")
|
||||||
|
assert.False(t, cfg.Packages.JavaArchive.IncludeUnindexedArchives, "the caller's config is not written to")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArchiveCataloger_javaArchiveDepthIsAConfigError(t *testing.T) {
|
||||||
|
// depth and limits are not java settings; setting them on java's deprecated copy must fail fast
|
||||||
|
// rather than be silently ignored
|
||||||
|
for name, set := range map[string]func(*cataloging.ArchiveSearchConfig){
|
||||||
|
"max depth": func(c *cataloging.ArchiveSearchConfig) { c.MaxDepth = 3 },
|
||||||
|
"max memory bytes": func(c *cataloging.ArchiveSearchConfig) { c.MaxMemoryBytes = 1 },
|
||||||
|
"max disk bytes": func(c *cataloging.ArchiveSearchConfig) { c.MaxDiskBytes = 1 },
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
supplied := java.DefaultArchiveCatalogerConfig()
|
||||||
|
supplied.ArchiveSearchConfig = cataloging.ArchiveSearchConfig{}
|
||||||
|
set(&supplied.ArchiveSearchConfig)
|
||||||
|
cfg := DefaultCreateSBOMConfig().WithPackagesConfig(pkgcataloging.Config{JavaArchive: supplied})
|
||||||
|
|
||||||
|
src, err := directorysource.New(directorysource.Config{Path: t.TempDir()})
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { _ = src.Close() })
|
||||||
|
|
||||||
|
_, err = cfg.Create(context.Background(), src)
|
||||||
|
require.ErrorContains(t, err, "set them with CreateSBOMConfig.Archive")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// packageLocations lists "archivePath|realPath" for every location of every package with the name.
|
// packageLocations lists "archivePath|realPath" for every location of every package with the name.
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ func (c *CreateSBOMConfig) selectTasks(src source.Description) ([]task.Task, []t
|
|||||||
SearchConfig: c.Search,
|
SearchConfig: c.Search,
|
||||||
RelationshipsConfig: c.Relationships,
|
RelationshipsConfig: c.Relationships,
|
||||||
DataGenerationConfig: c.DataGeneration,
|
DataGenerationConfig: c.DataGeneration,
|
||||||
PackagesConfig: c.Packages,
|
PackagesConfig: c.packagesWithArchiveSearch(),
|
||||||
LicenseConfig: c.Licenses,
|
LicenseConfig: c.Licenses,
|
||||||
ComplianceConfig: c.Compliance,
|
ComplianceConfig: c.Compliance,
|
||||||
FilesConfig: c.Files,
|
FilesConfig: c.Files,
|
||||||
@@ -480,12 +480,33 @@ func (c *CreateSBOMConfig) osFeatureDetectionTasks() []task.Task {
|
|||||||
return tasks
|
return tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packagesWithArchiveSearch returns a copy of the packages config with the top-level archive search
|
||||||
|
// booleans applied to java, which is still the only cataloger that reads them.
|
||||||
|
//
|
||||||
|
// ponytail: a top-level boolean wins only when it differs from the default, so callers who set just
|
||||||
|
// java's deprecated copy see no change. Delete this along with that field.
|
||||||
|
func (c *CreateSBOMConfig) packagesWithArchiveSearch() pkgcataloging.Config {
|
||||||
|
pkgs := c.Packages
|
||||||
|
def := cataloging.DefaultArchiveSearchConfig()
|
||||||
|
java := &pkgs.JavaArchive.ArchiveSearchConfig
|
||||||
|
if c.Archive.IncludeIndexedArchives != def.IncludeIndexedArchives {
|
||||||
|
java.IncludeIndexedArchives = c.Archive.IncludeIndexedArchives
|
||||||
|
}
|
||||||
|
if c.Archive.IncludeUnindexedArchives != def.IncludeUnindexedArchives {
|
||||||
|
java.IncludeUnindexedArchives = c.Archive.IncludeUnindexedArchives
|
||||||
|
}
|
||||||
|
return pkgs
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CreateSBOMConfig) validate() error {
|
func (c *CreateSBOMConfig) validate() error {
|
||||||
if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap {
|
if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap {
|
||||||
if !c.Relationships.PackageFileOwnershipOverlap {
|
if !c.Relationships.PackageFileOwnershipOverlap {
|
||||||
return fmt.Errorf("invalid configuration: to exclude binary packages based on file ownership overlap relationships, cataloging file ownership overlap relationships must be enabled")
|
return fmt.Errorf("invalid configuration: to exclude binary packages based on file ownership overlap relationships, cataloging file ownership overlap relationships must be enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if j := c.Packages.JavaArchive.ArchiveSearchConfig; j.MaxDepth != 0 || j.MaxMemoryBytes != 0 || j.MaxDiskBytes != 0 {
|
||||||
|
return fmt.Errorf("invalid configuration: nested archive depth and limits are not java cataloger settings, set them with CreateSBOMConfig.Archive")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,17 +114,6 @@ func Test_nestedArchiveOwnershipSwitch(t *testing.T) {
|
|||||||
require.Len(t, pkgs, 1)
|
require.Len(t, pkgs, 1)
|
||||||
assert.Equal(t, "example-app", pkgs[0].Name)
|
assert.Equal(t, "example-app", pkgs[0].Name)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("java's own archive-search depth does not hand off recursion", func(t *testing.T) {
|
|
||||||
// a library consumer setting java's squash-inlined MaxDepth must neither enable nor disable
|
|
||||||
// nested archive cataloging: that knob is Archive.MaxDepth on CreateSBOMConfig. Getting it
|
|
||||||
// wrong silently drops nested jars for every consumer that is not the CLI.
|
|
||||||
cfg := DefaultArchiveCatalogerConfig()
|
|
||||||
cfg.ArchiveSearchConfig = cfg.ArchiveSearchConfig.WithMaxDepth(2)
|
|
||||||
|
|
||||||
pkgs := parse(t, pkgtest.Context(t), cfg)
|
|
||||||
require.Len(t, pkgs, 2, "this cataloger must still recurse itself")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_NewArchiveCataloger_wrappedParserRegistration(t *testing.T) {
|
func Test_NewArchiveCataloger_wrappedParserRegistration(t *testing.T) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/bmatcuk/doublestar/v4"
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/archive"
|
"github.com/anchore/syft/internal/archive"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/internal/unknown"
|
"github.com/anchore/syft/internal/unknown"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
@@ -18,6 +19,9 @@ import (
|
|||||||
|
|
||||||
// NewArchiveCataloger returns a new Java archive cataloger object for detecting packages with archives (jar, war, ear, par, sar, jpi, hpi, and native-image formats)
|
// NewArchiveCataloger returns a new Java archive cataloger object for detecting packages with archives (jar, war, ear, par, sar, jpi, hpi, and native-image formats)
|
||||||
func NewArchiveCataloger(cfg ArchiveCatalogerConfig) pkg.Cataloger {
|
func NewArchiveCataloger(cfg ArchiveCatalogerConfig) pkg.Cataloger {
|
||||||
|
if a := cfg.ArchiveSearchConfig; a.MaxDepth != 0 || a.MaxMemoryBytes != 0 || a.MaxDiskBytes != 0 {
|
||||||
|
log.Debug("java archive cataloger ignores nested archive depth and limits, set them with syft.CreateSBOMConfig.Archive")
|
||||||
|
}
|
||||||
gap := newGenericArchiveParserAdapter(cfg)
|
gap := newGenericArchiveParserAdapter(cfg)
|
||||||
|
|
||||||
c := generic.NewCataloger("java-archive-cataloger").
|
c := generic.NewCataloger("java-archive-cataloger").
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ArchiveCatalogerConfig struct {
|
type ArchiveCatalogerConfig struct {
|
||||||
|
// Deprecated: use syft.CreateSBOMConfig.Archive. Only the two Include* booleans are read here, and
|
||||||
|
// setting MaxDepth, MaxMemoryBytes or MaxDiskBytes is a configuration error. This will be removed in a
|
||||||
|
// future release.
|
||||||
cataloging.ArchiveSearchConfig `yaml:",inline" json:"" mapstructure:",squash"`
|
cataloging.ArchiveSearchConfig `yaml:",inline" json:"" mapstructure:",squash"`
|
||||||
|
|
||||||
// UseNetwork enables network operations for java package metadata enrichment, such as fetching parent POMs and license information.
|
// UseNetwork enables network operations for java package metadata enrichment, such as fetching parent POMs and license information.
|
||||||
@@ -37,8 +40,13 @@ type ArchiveCatalogerConfig struct {
|
|||||||
|
|
||||||
func DefaultArchiveCatalogerConfig() ArchiveCatalogerConfig {
|
func DefaultArchiveCatalogerConfig() ArchiveCatalogerConfig {
|
||||||
mavenCfg := maven.DefaultConfig()
|
mavenCfg := maven.DefaultConfig()
|
||||||
|
search := cataloging.DefaultArchiveSearchConfig()
|
||||||
return ArchiveCatalogerConfig{
|
return ArchiveCatalogerConfig{
|
||||||
ArchiveSearchConfig: cataloging.DefaultArchiveSearchConfig(),
|
// only the booleans: depth and limits are not java settings
|
||||||
|
ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
|
||||||
|
IncludeIndexedArchives: search.IncludeIndexedArchives,
|
||||||
|
IncludeUnindexedArchives: search.IncludeUnindexedArchives,
|
||||||
|
},
|
||||||
UseNetwork: mavenCfg.UseNetwork,
|
UseNetwork: mavenCfg.UseNetwork,
|
||||||
UseMavenLocalRepository: mavenCfg.UseLocalRepository,
|
UseMavenLocalRepository: mavenCfg.UseLocalRepository,
|
||||||
MavenLocalRepositoryDir: mavenCfg.LocalRepositoryDir,
|
MavenLocalRepositoryDir: mavenCfg.LocalRepositoryDir,
|
||||||
@@ -75,12 +83,19 @@ func (j ArchiveCatalogerConfig) WithResolveTransitiveDependencies(resolveTransit
|
|||||||
return j
|
return j
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: set archive search with syft.CreateSBOMConfig.Archive and the parent depth with
|
||||||
|
// WithMaxParentRecursiveDepth. This will be removed in a future release.
|
||||||
func (j ArchiveCatalogerConfig) WithArchiveTraversal(search cataloging.ArchiveSearchConfig, maxDepth int) ArchiveCatalogerConfig {
|
func (j ArchiveCatalogerConfig) WithArchiveTraversal(search cataloging.ArchiveSearchConfig, maxDepth int) ArchiveCatalogerConfig {
|
||||||
j.MaxParentRecursiveDepth = maxDepth
|
j.MaxParentRecursiveDepth = maxDepth
|
||||||
j.ArchiveSearchConfig = search
|
j.ArchiveSearchConfig = search
|
||||||
return j
|
return j
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j ArchiveCatalogerConfig) WithMaxParentRecursiveDepth(depth int) ArchiveCatalogerConfig {
|
||||||
|
j.MaxParentRecursiveDepth = depth
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
|
||||||
func (j ArchiveCatalogerConfig) mavenConfig() maven.Config {
|
func (j ArchiveCatalogerConfig) mavenConfig() maven.Config {
|
||||||
return maven.Config{
|
return maven.Config{
|
||||||
UseNetwork: j.UseNetwork,
|
UseNetwork: j.UseNetwork,
|
||||||
|
|||||||
Reference in New Issue
Block a user