Add nested archive cataloging (#5260)

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
Co-authored-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Keith Zantow
2026-10-29 17:33:07 -04:00
committed by GitHub
co-authored by Kudryavcev Nikolay Thomas Bechtold Alex Goodman
parent 14a0beb93c
commit 4b8a3db5f3
224 changed files with 15918 additions and 1281 deletions
+37 -3
View File
@@ -2,8 +2,11 @@ package task
import (
"context"
"path"
"slices"
"strings"
"github.com/anchore/syft/internal/archive"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
@@ -36,11 +39,20 @@ func (c unknownsLabelerTask) finalize(resolver file.Resolver, s *sbom.SBOM) {
hasPackageReference := coordinateReferenceLookup(resolver, s)
if c.RemoveWhenPackagesDefined {
for coords := range s.Artifacts.Unknowns {
for coords, reasons := range s.Artifacts.Unknowns {
if !hasPackageReference(coords) {
continue
}
delete(s.Artifacts.Unknowns, coords)
// what the archive cataloger reports is about the archive's contents (left out, cut short),
// which a package describing the archive does not account for
kept := slices.DeleteFunc(reasons, func(r string) bool {
return !strings.HasPrefix(r, formatUnknown("", ArchiveCatalogerTaskName))
})
if len(kept) == 0 {
delete(s.Artifacts.Unknowns, coords)
continue
}
s.Artifacts.Unknowns[coords] = kept
}
}
@@ -59,7 +71,7 @@ func (c unknownsLabelerTask) finalize(resolver file.Resolver, s *sbom.SBOM) {
if c.IncludeUnexpandedArchives {
ctx := context.Background()
for coords := range s.Artifacts.FileMetadata {
format, _, notArchiveErr := intFile.IdentifyArchive(ctx, coords.RealPath, nil)
format, _, notArchiveErr := intFile.IdentifyArchive(ctx, coords.FullNestedPath(), nil)
if format != nil && notArchiveErr == nil && !hasPackageReference(coords) {
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], "archive not cataloged")
}
@@ -71,6 +83,7 @@ func formatUnknown(err string, task ...string) string {
return strings.Join(task, "/") + ": " + err
}
//nolint:gocognit
func coordinateReferenceLookup(resolver file.Resolver, s *sbom.SBOM) func(coords file.Coordinates) bool {
allPackageCoords := file.NewCoordinateSet()
@@ -83,7 +96,28 @@ func coordinateReferenceLookup(resolver file.Resolver, s *sbom.SBOM) func(coords
// relationships for these owned files may be disabled, but we always want to include them
for p := range s.Artifacts.Packages.Enumerate() {
if f, ok := p.Metadata.(pkg.FileOwner); ok {
// owned paths are within the containers the package was found in
var archives []file.Coordinates
atRoot := false
for _, container := range archive.OwnerContainers(p) {
if container.FullPath == "" {
atRoot = true
} else {
archives = append(archives, container)
}
}
for _, ownedFilePath := range f.OwnedFiles() {
// an archive's contents are not in this resolver to follow links in, and only an absolute owned
// path names a file in one (see packageFileOwnershipRelationships)
if path.IsAbs(ownedFilePath) {
for _, container := range archives {
allPackageCoords.Add(archive.OwnedCoordinates(container, ownedFilePath))
}
}
if !atRoot {
continue
}
// resolve these owned files, as they may have symlinks
// but coordinates we will test against are always absolute paths
locations, err := resolver.FilesByPath(ownedFilePath)