mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +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 {
|
||||
archiveSearch := cataloging.ArchiveSearchConfig{
|
||||
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
||||
}
|
||||
return pkgcataloging.Config{
|
||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||
Cpp: cpp.DefaultCatalogerConfig().
|
||||
@@ -229,7 +225,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
|
||||
WithUseNetwork(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseNetwork)).
|
||||
WithMavenBaseURL(cfg.Java.MavenURL).
|
||||
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth).
|
||||
WithMaxParentRecursiveDepth(cfg.Java.MaxParentRecursiveDepth).
|
||||
WithResolveTransitiveDependencies(cfg.Java.ResolveTransitiveDependencies),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ var _ interface {
|
||||
|
||||
func (o *packageConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
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)
|
||||
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
|
||||
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)`)
|
||||
|
||||
@@ -305,13 +305,8 @@ func TestArchiveCataloger(t *testing.T) {
|
||||
func TestArchiveCataloger_featureOff(t *testing.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().
|
||||
WithCatalogerSelection(cataloging.NewSelectionRequest().WithDefaults("java")).
|
||||
WithPackagesConfig(pkgcataloging.Config{JavaArchive: supplied})
|
||||
WithCatalogerSelection(cataloging.NewSelectionRequest().WithDefaults("java"))
|
||||
|
||||
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"},
|
||||
"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")
|
||||
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.
|
||||
|
||||
@@ -279,7 +279,7 @@ func (c *CreateSBOMConfig) selectTasks(src source.Description) ([]task.Task, []t
|
||||
SearchConfig: c.Search,
|
||||
RelationshipsConfig: c.Relationships,
|
||||
DataGenerationConfig: c.DataGeneration,
|
||||
PackagesConfig: c.Packages,
|
||||
PackagesConfig: c.packagesWithArchiveSearch(),
|
||||
LicenseConfig: c.Licenses,
|
||||
ComplianceConfig: c.Compliance,
|
||||
FilesConfig: c.Files,
|
||||
@@ -480,12 +480,33 @@ func (c *CreateSBOMConfig) osFeatureDetectionTasks() []task.Task {
|
||||
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 {
|
||||
if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap {
|
||||
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")
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -114,17 +114,6 @@ func Test_nestedArchiveOwnershipSwitch(t *testing.T) {
|
||||
require.Len(t, pkgs, 1)
|
||||
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) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
|
||||
"github.com/anchore/syft/internal/archive"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"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)
|
||||
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)
|
||||
|
||||
c := generic.NewCataloger("java-archive-cataloger").
|
||||
|
||||
@@ -8,6 +8,9 @@ import (
|
||||
)
|
||||
|
||||
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"`
|
||||
|
||||
// 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 {
|
||||
mavenCfg := maven.DefaultConfig()
|
||||
search := cataloging.DefaultArchiveSearchConfig()
|
||||
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,
|
||||
UseMavenLocalRepository: mavenCfg.UseLocalRepository,
|
||||
MavenLocalRepositoryDir: mavenCfg.LocalRepositoryDir,
|
||||
@@ -75,12 +83,19 @@ func (j ArchiveCatalogerConfig) WithResolveTransitiveDependencies(resolveTransit
|
||||
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 {
|
||||
j.MaxParentRecursiveDepth = maxDepth
|
||||
j.ArchiveSearchConfig = search
|
||||
return j
|
||||
}
|
||||
|
||||
func (j ArchiveCatalogerConfig) WithMaxParentRecursiveDepth(depth int) ArchiveCatalogerConfig {
|
||||
j.MaxParentRecursiveDepth = depth
|
||||
return j
|
||||
}
|
||||
|
||||
func (j ArchiveCatalogerConfig) mavenConfig() maven.Config {
|
||||
return maven.Config{
|
||||
UseNetwork: j.UseNetwork,
|
||||
|
||||
Reference in New Issue
Block a user