fix: recover from panics while extracting an archive

sub-pipeline catalogers were already run under `runTaskSafely`, but extraction wasn't: the zip, tar and compressor decoders run on untrusted bytes, and a panic in one unwound the whole walk and failed `CreateSBOM` with "failed to run tasks".

- `processArchive` recovers and returns the panic (with its stack) as an error at the archive's coordinates, so it becomes an unknown and the sibling archives are still cataloged
- `Extract` now defers `Cleanup` on failure instead of calling it on each error path, so a panic no longer leaks the limiter charge or the spill file

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2026-09-24 11:28:26 -04:00
parent f5bac4a026
commit c9b4f9c085
4 changed files with 85 additions and 4 deletions
+9 -1
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"maps"
"runtime/debug"
"time"
"github.com/dustin/go-humanize"
@@ -142,7 +143,14 @@ func (c *archiveCataloger) catalog(ctx context.Context, resolver file.Resolver,
return errs
}
func (c *archiveCataloger) processArchive(ctx context.Context, parentResolver file.Resolver, candidate archiveCandidate, depth int, builder sbomsync.Builder) error {
func (c *archiveCataloger) processArchive(ctx context.Context, parentResolver file.Resolver, candidate archiveCandidate, depth int, builder sbomsync.Builder) (err error) {
// the decoders run on untrusted bytes; a panic in one costs this archive, not the scan
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic while cataloging archive: %v at:\n%s", r, debug.Stack())
}
}()
location := candidate.location
content, err := parentResolver.FileContentsByLocation(location)
if err != nil {