mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +01:00
fix: treat zero archive limits as the default
`ArchiveSearchConfig{MaxDepth: 2}` is the natural literal, but zero memory and zero disk meant "forbid both", so every archive was truncated to nothing and only unknowns came out.
- `MaxMemoryBytes` / `MaxDiskBytes` of `0` now mean the default, negative is still unbounded
- the limiter keeps its own zero-forbids semantics internally; it's just no longer reachable from config
- tests that relied on zero to force spilling now pass `archive.Limits` directly, and the two "both limits zero" tests are gone since that config no longer exists
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -27,8 +27,8 @@ note: for now this only applies to the java package cataloger`)
|
||||
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)`)
|
||||
descriptions.Add(&o.NestedArchiveMaxMemoryBytes, `maximum bytes of nested archive content held in memory at once (0 = none, always overflow to disk; -1 = unbounded)`)
|
||||
descriptions.Add(&o.NestedArchiveMaxDiskBytes, `maximum bytes of nested archive content on disk at once (0 = none, nothing overflows; -1 = unbounded)`)
|
||||
descriptions.Add(&o.NestedArchiveMaxMemoryBytes, `maximum bytes of nested archive content held in memory at once (0 = default, -1 = unbounded)`)
|
||||
descriptions.Add(&o.NestedArchiveMaxDiskBytes, `maximum bytes of nested archive content on disk at once (0 = default, -1 = unbounded)`)
|
||||
}
|
||||
|
||||
func defaultPackageConfig() packageConfig {
|
||||
|
||||
@@ -113,7 +113,7 @@ func Test_mixedFamilyNesting_leafIsCatalogedOnceWithTheFullChain(t *testing.T) {
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
s := runNesting(t, scanDir, 3, cataloging.DefaultArchiveSearchConfig(), markerTask(), probe.task())
|
||||
s := runNesting(t, scanDir, 3, defaultLimits(), markerTask(), probe.task())
|
||||
|
||||
assert.Equal(t, 1, packageCount(s, "marker-pkg"), "%s: the leaf package must be cataloged exactly once", row.why)
|
||||
|
||||
@@ -139,7 +139,7 @@ func Test_mixedFamilyNesting_containsEdgesChainAtEveryLevel(t *testing.T) {
|
||||
scanDir := t.TempDir()
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
s := runNesting(t, scanDir, 3, cataloging.DefaultArchiveSearchConfig(), markerTask(), fileMetadataTask())
|
||||
s := runNesting(t, scanDir, 3, defaultLimits(), markerTask(), fileMetadataTask())
|
||||
|
||||
// each archive's coordinates as seen in its parent; the outermost sits in the scanned
|
||||
// directory, so it carries no filesystem id of its own
|
||||
@@ -197,7 +197,7 @@ func Test_mixedFamilyNesting_javaLeafVirtualPathIsTheColonJoinedChain(t *testing
|
||||
scanDir := t.TempDir()
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
s := runNesting(t, scanDir, 3, cataloging.DefaultArchiveSearchConfig(), markerTask(), javaTask())
|
||||
s := runNesting(t, scanDir, 3, defaultLimits(), markerTask(), javaTask())
|
||||
|
||||
// the innermost archive is the jar, so its own package is the java leaf and the chain of
|
||||
// all three archives is exactly its virtual path
|
||||
@@ -228,7 +228,7 @@ func Test_mixedFamilyNesting_jarIsExercisedAsAContainer(t *testing.T) {
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
s := runNesting(t, scanDir, 3, cataloging.DefaultArchiveSearchConfig(), markerTask(), javaTask(), probe.task())
|
||||
s := runNesting(t, scanDir, 3, defaultLimits(), markerTask(), javaTask(), probe.task())
|
||||
|
||||
for _, level := range containerLevels {
|
||||
name := fmt.Sprintf("level%d", level)
|
||||
@@ -256,7 +256,7 @@ func Test_mixedFamilyNesting_depthCountsLevelsNotFamilies(t *testing.T) {
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
s := runNesting(t, scanDir, 2, cataloging.DefaultArchiveSearchConfig(), markerTask(), fileMetadataTask(), probe.task())
|
||||
s := runNesting(t, scanDir, 2, defaultLimits(), markerTask(), fileMetadataTask(), probe.task())
|
||||
|
||||
assert.Equal(t, nested.fileSystemIDs[:2], probe.archiveFileSystemIDs(),
|
||||
"at depth 2 the outer and middle archives are cataloged and the innermost is not")
|
||||
@@ -287,7 +287,7 @@ func Test_mixedFamilyNesting_defaultBoundsCatalogEveryRow(t *testing.T) {
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
s := runNesting(t, scanDir, 3, archiveLimits(bounds), markerTask(), probe.task())
|
||||
|
||||
assert.Equal(t, nested.fileSystemIDs, probe.archiveFileSystemIDs(),
|
||||
"every level of the chain must be cataloged at the default bounds")
|
||||
@@ -306,8 +306,8 @@ func Test_mixedFamilyNesting_memoryPressureOverflowsRatherThanFailing(t *testing
|
||||
|
||||
t.Run("with a generous memory limit nothing is written to disk", func(t *testing.T) {
|
||||
probe := &nestingProbe{tempDir: tempDir}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(int64(2 * (nested.sizes[0] + nested.sizes[1] + nested.sizes[2])))
|
||||
bounds := defaultLimits()
|
||||
bounds.MaxMemoryBytes = int64(2 * (nested.sizes[0] + nested.sizes[1] + nested.sizes[2]))
|
||||
runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
deepest := probe.deepestVisit()
|
||||
@@ -318,9 +318,7 @@ func Test_mixedFamilyNesting_memoryPressureOverflowsRatherThanFailing(t *testing
|
||||
t.Run("with a memory limit the outermost archive's entries do not fit in", func(t *testing.T) {
|
||||
// room for every level's index estimate, which must stay in memory, but not the outer entries
|
||||
probe := &nestingProbe{tempDir: tempDir}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(int64(nested.sizes[2] + 32*1024)).
|
||||
WithMaxDiskBytes(-1)
|
||||
bounds := archive.Limits{MaxMemoryBytes: int64(nested.sizes[2] + 32*1024), MaxDiskBytes: -1}
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
deepest := probe.deepestVisit()
|
||||
@@ -359,9 +357,7 @@ func Test_mixedFamilyNesting_aNestedArchiveIsReadWhereItLies(t *testing.T) {
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
probe := &nestingProbe{tempDir: tempDir}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(tc.memoryBytes).
|
||||
WithMaxDiskBytes(-1)
|
||||
bounds := archive.Limits{MaxMemoryBytes: tc.memoryBytes, MaxDiskBytes: -1}
|
||||
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
@@ -397,9 +393,7 @@ func Test_mixedFamilyNesting_diskLimitTruncatesTheLevelItCannotAdmit(t *testing.
|
||||
memoryLimit := int64(nested.sizes[0] + nested.sizes[1] + deepChainPayloadBytes/2)
|
||||
|
||||
probe := &nestingProbe{}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(memoryLimit).
|
||||
WithMaxDiskBytes(0)
|
||||
bounds := archive.Limits{MaxMemoryBytes: memoryLimit, MaxDiskBytes: 0}
|
||||
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
@@ -428,16 +422,14 @@ func Test_mixedFamilyNesting_diskLimitTruncatesTheLevelItCannotAdmit(t *testing.
|
||||
|
||||
func Test_mixedFamilyNesting_unboundedLimitsEnforceNothing(t *testing.T) {
|
||||
// a caller can opt out of one bound without the other, and opting out of both must still find the
|
||||
// deepest leaf. Both opt out with a negative value, since zero means "none of that resource".
|
||||
// deepest leaf. Both opt out with a negative value, since zero means the default.
|
||||
for _, row := range nestingRows() {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
scanDir := t.TempDir()
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(-1).
|
||||
WithMaxDiskBytes(-1)
|
||||
bounds := archive.Limits{MaxMemoryBytes: -1, MaxDiskBytes: -1}
|
||||
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
@@ -449,25 +441,8 @@ func Test_mixedFamilyNesting_unboundedLimitsEnforceNothing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mixedFamilyNesting_bothLimitsZeroCatalogsNothingInsideAnyArchive(t *testing.T) {
|
||||
// unlike negative (unbounded), zero on both limits admits no entry anywhere: the outermost archive is
|
||||
// cataloged from nothing, so the level inside it is never found, and the scan still succeeds
|
||||
for _, row := range nestingRows() {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
scanDir := t.TempDir()
|
||||
nested := writeNestedArchive(t, scanDir, nestPlan{families: row.families, leaf: markerLeaf()})
|
||||
|
||||
probe := &nestingProbe{}
|
||||
bounds := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(0)
|
||||
|
||||
s := runNesting(t, scanDir, 3, bounds, markerTask(), probe.task())
|
||||
|
||||
assert.Equal(t, nested.fileSystemIDs[:1], probe.archiveFileSystemIDs())
|
||||
assert.Empty(t, leafLocations(s))
|
||||
})
|
||||
}
|
||||
func defaultLimits() archive.Limits {
|
||||
return archiveLimits(cataloging.DefaultArchiveSearchConfig())
|
||||
}
|
||||
|
||||
// runNesting runs the archive cataloger task over scanDir with the given sub-pipeline and returns the
|
||||
@@ -476,9 +451,9 @@ func Test_mixedFamilyNesting_bothLimitsZeroCatalogsNothingInsideAnyArchive(t *te
|
||||
// The depth-3 limit cases need the timeout: the walk descends before it unwinds, so the outer and
|
||||
// middle archives still hold their content when the inner one is refused. An implementation that
|
||||
// waited for capacity would deadlock, and a test asserting only the result would hang rather than fail.
|
||||
func runNesting(t *testing.T, scanDir string, depth int, bounds cataloging.ArchiveSearchConfig, subPipeline ...Task) *sbom.SBOM {
|
||||
func runNesting(t *testing.T, scanDir string, depth int, bounds archive.Limits, subPipeline ...Task) *sbom.SBOM {
|
||||
t.Helper()
|
||||
tsk := newTestTask(t, bounds.WithMaxDepth(depth), subPipeline...)
|
||||
tsk := newLimitedTestTask(t, depth, bounds, subPipeline...)
|
||||
s := &sbom.SBOM{Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(),
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -36,7 +37,20 @@ const ArchiveCatalogerTaskName = "archive-cataloger"
|
||||
// exclusions are the scan's exclusion patterns, applied inside each archive. Returns nil when archive
|
||||
// cataloging is disabled (MaxDepth == 0; negative means unbounded).
|
||||
func NewArchiveCatalogerTask(cfg cataloging.ArchiveSearchConfig, subPipeline []Task, exclusions []string) Task {
|
||||
if cfg.MaxDepth == 0 {
|
||||
return newArchiveCatalogerTask(cfg.MaxDepth, archiveLimits(cfg), subPipeline, exclusions)
|
||||
}
|
||||
|
||||
// archiveLimits maps the configured limits onto the limiter's. Zero means the default, so the limiter's
|
||||
// own zero (which forbids the resource) is not reachable from configuration.
|
||||
func archiveLimits(cfg cataloging.ArchiveSearchConfig) archive.Limits {
|
||||
return archive.Limits{
|
||||
MaxMemoryBytes: cmp.Or(cfg.MaxMemoryBytes, int64(cataloging.DefaultArchiveMaxMemoryBytes)),
|
||||
MaxDiskBytes: cmp.Or(cfg.MaxDiskBytes, int64(cataloging.DefaultArchiveMaxDiskBytes)),
|
||||
}
|
||||
}
|
||||
|
||||
func newArchiveCatalogerTask(maxDepth int, limits archive.Limits, subPipeline []Task, exclusions []string) Task {
|
||||
if maxDepth == 0 {
|
||||
return nil
|
||||
}
|
||||
subPipeline = withoutArchiveCataloger(subPipeline)
|
||||
@@ -49,9 +63,9 @@ func NewArchiveCatalogerTask(cfg cataloging.ArchiveSearchConfig, subPipeline []T
|
||||
ctx = bus.WithCatalogerTaskRegistry(ctx)
|
||||
|
||||
c := &archiveCataloger{
|
||||
maxDepth: cfg.MaxDepth,
|
||||
maxDepth: maxDepth,
|
||||
subPipeline: subPipeline,
|
||||
limiter: archive.NewLimiter(archive.Limits{MaxMemoryBytes: cfg.MaxMemoryBytes, MaxDiskBytes: cfg.MaxDiskBytes}),
|
||||
limiter: archive.NewLimiter(limits),
|
||||
exclusions: archive.NewExclusions(exclusions),
|
||||
progress: bus.StartCatalogerTask(ctx, archiveCatalogerProgressInfo(), -1, ""),
|
||||
}
|
||||
|
||||
@@ -70,6 +70,14 @@ func newTestTask(t *testing.T, cfg cataloging.ArchiveSearchConfig, subPipeline .
|
||||
return tsk
|
||||
}
|
||||
|
||||
// newLimitedTestTask builds the task with the limiter's own limits, where zero forbids the resource.
|
||||
func newLimitedTestTask(t *testing.T, maxDepth int, limits archive.Limits, subPipeline ...Task) Task {
|
||||
t.Helper()
|
||||
tsk := newArchiveCatalogerTask(maxDepth, limits, subPipeline, nil)
|
||||
require.NotNil(t, tsk)
|
||||
return tsk
|
||||
}
|
||||
|
||||
func newTestSBOM() *sbom.SBOM {
|
||||
return &sbom.SBOM{Artifacts: sbom.Artifacts{Packages: pkg.NewCollection()}}
|
||||
}
|
||||
@@ -122,6 +130,23 @@ func Test_archiveCataloger_traversalThreading(t *testing.T) {
|
||||
}, seen)
|
||||
}
|
||||
|
||||
func Test_archiveCataloger_zeroLimitsMeanDefault(t *testing.T) {
|
||||
// a config literal naming only the depth must catalog archives, not forbid both memory and disk
|
||||
rootDir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "outer.zip"), makeZip(t, map[string][]byte{"a.txt": []byte("a")}), 0o600))
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cataloging.ArchiveSearchConfig{MaxDepth: 1}, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s)))
|
||||
|
||||
assert.Equal(t, []string{"/outer.zip"}, seen)
|
||||
assert.Equal(t, 1, fileCounts["/outer.zip"])
|
||||
assert.Empty(t, s.Artifacts.Unknowns)
|
||||
}
|
||||
|
||||
func Test_archiveCataloger_truncationStillCatalogs(t *testing.T) {
|
||||
// reaching a limit is a truncation, not a failure: the sub-pipeline still runs over what was stored
|
||||
big := bytes.Repeat([]byte("x"), 4096)
|
||||
@@ -134,14 +159,11 @@ func Test_archiveCataloger_truncationStillCatalogs(t *testing.T) {
|
||||
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "outer.zip"), outerZip, 0o600))
|
||||
|
||||
// nothing is held in memory, so each entry is charged to disk: small.txt lands, big.txt is refused
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(1).
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(6000)
|
||||
limits := archive.Limits{MaxMemoryBytes: 0, MaxDiskBytes: 6000}
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
tsk := newLimitedTestTask(t, 1, limits, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
err := tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s))
|
||||
@@ -462,14 +484,11 @@ func Test_archiveCataloger_manySmallArchivesAreEachCataloged(t *testing.T) {
|
||||
}
|
||||
|
||||
// room for one archive's entries and their index cost, not for two
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(1).
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(20000)
|
||||
limits := archive.Limits{MaxMemoryBytes: 0, MaxDiskBytes: 20000}
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
tsk := newLimitedTestTask(t, 1, limits, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s)))
|
||||
@@ -544,14 +563,11 @@ func Test_archiveCataloger_archiveWhoseOwnBytesExceedTheDiskLimitIsSkipped(t *te
|
||||
diskLimit := int64(len(small)) + 3000
|
||||
require.Greater(t, int64(len(oversized)), diskLimit, "the fixture must actually exceed the limit")
|
||||
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(1).
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(diskLimit)
|
||||
limits := archive.Limits{MaxMemoryBytes: 0, MaxDiskBytes: diskLimit}
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
tsk := newLimitedTestTask(t, 1, limits, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), streamingDirResolver{dirTestResolver{dir: rootDir}}, sbomsync.NewBuilder(s)),
|
||||
@@ -576,14 +592,11 @@ func Test_archiveCataloger_zeroDiskLimitTruncatesWhatWillNotFitInMemory(t *testi
|
||||
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "small.zip"), small, 0o600))
|
||||
|
||||
// room for the small archive's entry and its index cost, not for the oversized one's
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(1).
|
||||
WithMaxMemoryBytes(3000).
|
||||
WithMaxDiskBytes(0)
|
||||
limits := archive.Limits{MaxMemoryBytes: 3000, MaxDiskBytes: 0}
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
tsk := newLimitedTestTask(t, 1, limits, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s)),
|
||||
@@ -597,33 +610,6 @@ func Test_archiveCataloger_zeroDiskLimitTruncatesWhatWillNotFitInMemory(t *testi
|
||||
assert.NotContains(t, s.Artifacts.Unknowns, file.Coordinates{RealPath: "/small.zip"})
|
||||
}
|
||||
|
||||
func Test_archiveCataloger_bothLimitsZeroTruncatesEveryArchiveAndSucceeds(t *testing.T) {
|
||||
entries := map[string][]byte{"ok.txt": []byte("ok")}
|
||||
rootDir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "one.zip"), makeZip(t, entries), 0o600))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(rootDir, "two.zip"), makeZip(t, entries), 0o600))
|
||||
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(1).
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(0)
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s)),
|
||||
"both limits at zero is a real configuration, not a failure")
|
||||
|
||||
assert.ElementsMatch(t, []string{"/one.zip", "/two.zip"}, seen)
|
||||
for _, archivePath := range seen {
|
||||
assert.Equal(t, 0, fileCounts[archivePath], "no entry can be admitted anywhere")
|
||||
assert.Contains(t, s.Artifacts.Unknowns, file.Coordinates{RealPath: archivePath})
|
||||
}
|
||||
assert.Empty(t, s.Artifacts.Packages.Sorted())
|
||||
}
|
||||
|
||||
func Test_archiveCataloger_nestedArchiveExceedingALimitIsTruncatedNotBlocked(t *testing.T) {
|
||||
// the walk descends before it unwinds, so nothing is released while a child waits; a child that does
|
||||
// not fit is truncated and the scan finishes
|
||||
@@ -638,14 +624,11 @@ func Test_archiveCataloger_nestedArchiveExceedingALimitIsTruncatedNotBlocked(t *
|
||||
diskLimit := int64(len(inner)) + 2*2100 + 500
|
||||
require.Greater(t, 3000, 500, "the inner payload must not fit in the slack")
|
||||
|
||||
cfg := cataloging.DefaultArchiveSearchConfig().
|
||||
WithMaxDepth(2).
|
||||
WithMaxMemoryBytes(0).
|
||||
WithMaxDiskBytes(diskLimit)
|
||||
limits := archive.Limits{MaxMemoryBytes: 0, MaxDiskBytes: diskLimit}
|
||||
|
||||
var seen []string
|
||||
fileCounts := map[string]int{}
|
||||
tsk := newTestTask(t, cfg, capturingTask(t, &seen, &fileCounts))
|
||||
tsk := newLimitedTestTask(t, 2, limits, capturingTask(t, &seen, &fileCounts))
|
||||
|
||||
s := newTestSBOM()
|
||||
require.NoError(t, tsk.Execute(context.Background(), dirTestResolver{dir: rootDir}, sbomsync.NewBuilder(s)))
|
||||
|
||||
@@ -27,11 +27,11 @@ type ArchiveSearchConfig struct {
|
||||
MaxDepth int `yaml:"max-depth" json:"max-depth" mapstructure:"max-depth"`
|
||||
|
||||
// MaxMemoryBytes bounds the archive content held in memory at once; content that does not fit is
|
||||
// written to disk. Zero writes everything to disk, negative is unbounded.
|
||||
// written to disk. Zero means DefaultArchiveMaxMemoryBytes, negative is unbounded.
|
||||
MaxMemoryBytes int64 `yaml:"max-memory-bytes" json:"max-memory-bytes" mapstructure:"max-memory-bytes"`
|
||||
|
||||
// MaxDiskBytes bounds the archive content written to disk at once; an archive whose content does
|
||||
// not fit is skipped or truncated. Zero writes nothing to disk, negative is unbounded.
|
||||
// not fit is skipped or truncated. Zero means DefaultArchiveMaxDiskBytes, negative is unbounded.
|
||||
MaxDiskBytes int64 `yaml:"max-disk-bytes" json:"max-disk-bytes" mapstructure:"max-disk-bytes"`
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,8 @@ func (c *CreateSBOMConfig) WithFilesConfig(cfg filecataloging.Config) *CreateSBO
|
||||
}
|
||||
|
||||
// WithArchiveConfig sets how archives are cataloged: how deep to recurse into nested archives (MaxDepth
|
||||
// 0, the default, does not recurse) and how much extracted content the scan may hold at once.
|
||||
// 0, the default, does not recurse) and how much extracted content the scan may hold at once (a zero
|
||||
// limit means the default).
|
||||
func (c *CreateSBOMConfig) WithArchiveConfig(cfg cataloging.ArchiveSearchConfig) *CreateSBOMConfig {
|
||||
c.Archive = cfg
|
||||
return c
|
||||
|
||||
@@ -10,9 +10,9 @@ configs: # AUTO-GENERATED - config structs and their fields
|
||||
- key: MaxDepth
|
||||
description: 'MaxDepth is how many levels of nested archives to catalog: 0 disables it, negative is unbounded.'
|
||||
- key: MaxMemoryBytes
|
||||
description: MaxMemoryBytes bounds the archive content held in memory at once; content that does not fit is written to disk. Zero writes everything to disk, negative is unbounded.
|
||||
description: MaxMemoryBytes bounds the archive content held in memory at once; content that does not fit is written to disk. Zero means DefaultArchiveMaxMemoryBytes, negative is unbounded.
|
||||
- key: MaxDiskBytes
|
||||
description: MaxDiskBytes bounds the archive content written to disk at once; an archive whose content does not fit is skipped or truncated. Zero writes nothing to disk, negative is unbounded.
|
||||
description: MaxDiskBytes bounds the archive content written to disk at once; an archive whose content does not fit is skipped or truncated. Zero means DefaultArchiveMaxDiskBytes, negative is unbounded.
|
||||
- key: UseNetwork
|
||||
description: UseNetwork enables network operations for java package metadata enrichment, such as fetching parent POMs and license information.
|
||||
app_key: java.use-network
|
||||
|
||||
Reference in New Issue
Block a user