fix: bound archive recursion with a cycle guard and depth ceiling

a negative `MaxDepth` really was unbounded, and nothing noticed an archive nested inside a copy of itself. A zip quine at `-1` recursed until memory, disk or file descriptors ran out, running the full sub-pipeline at every level.

- the walk keeps a stack of the digests of the archives it's inside, and an archive identical to one of them is recorded as an unknown instead of being entered
- negative depth now stops at 16 levels (docs and CLI description updated)
- cancellation is checked between candidates, so a cancelled scan stops walking instead of finishing the archive tree

the three unknown-recording blocks in `processArchive` became a one-line helper to stay under `funlen`.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2026-09-24 11:32:15 -04:00
parent 73397f1e8b
commit 27655c2be0
5 changed files with 99 additions and 13 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ note: enabling this may result in a performance impact since all discovered comp
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)`)
descriptions.Add(&o.NestedArchiveMaxDepth, `maximum depth to recursively catalog nested archives (0 = disabled, -1 = as deep as 16 levels)`)
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)`)
}