Files
syft/internal/relationship/binary/shared_library_index.go
T
4b8a3db5f3 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>
2026-10-29 17:33:07 -04:00

139 lines
4.6 KiB
Go

package binary
import (
"path"
"github.com/anchore/syft/internal/archive"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
type sharedLibraryIndex struct {
libLocationsByBasename map[string]file.CoordinateSet
allLibLocations file.CoordinateSet
packagesByLibraryPath map[file.Coordinates]*pkg.Collection
}
func newShareLibIndex(resolver file.Resolver, accessor sbomsync.Accessor) *sharedLibraryIndex {
s := &sharedLibraryIndex{
libLocationsByBasename: make(map[string]file.CoordinateSet),
allLibLocations: file.NewCoordinateSet(),
packagesByLibraryPath: make(map[file.Coordinates]*pkg.Collection),
}
s.build(resolver, accessor)
return s
}
func (i *sharedLibraryIndex) build(resolver file.Resolver, accessor sbomsync.Accessor) {
// 1. map out all locations that provide libraries (indexed by the basename)
i.libLocationsByBasename, i.allLibLocations = locationsThatProvideLibraries(accessor)
// 2. for each library path, find all packages that claim ownership of the library
i.packagesByLibraryPath = packagesWithLibraryOwnership(resolver, accessor, i.allLibLocations)
}
func (i *sharedLibraryIndex) owningLibraryPackage(libraryBasename string) *pkg.Collection {
// find all packages that own a library by its basename
collection := pkg.NewCollection()
if set, ok := i.libLocationsByBasename[libraryBasename]; ok {
for _, coord := range set.ToSlice() {
if pkgSet, ok := i.packagesByLibraryPath[coord]; ok {
toAdd := pkgSet.Sorted()
collection.Add(toAdd...)
}
}
}
return collection
}
func locationsThatProvideLibraries(accessor sbomsync.Accessor) (map[string]file.CoordinateSet, file.CoordinateSet) {
// map out all locations that provide libraries (indexed by the basename)
libLocationsByBasename := make(map[string]file.CoordinateSet)
allLibLocations := file.NewCoordinateSet()
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
// PROBLEM: this does not consider all symlinks to real paths that are libraries
for coord, f := range s.Artifacts.Executables {
if !f.HasExports {
continue
}
basename := path.Base(coord.FullNestedPath())
set := libLocationsByBasename[basename]
set.Add(coord)
allLibLocations.Add(coord)
libLocationsByBasename[basename] = set
}
})
return libLocationsByBasename, allLibLocations
}
func packagesWithLibraryOwnership(resolver file.Resolver, accessor sbomsync.Accessor, allLibLocations file.CoordinateSet) map[file.Coordinates]*pkg.Collection {
// map out all packages that claim ownership of a library at a specific path
packagesByLibraryPath := make(map[file.Coordinates]*pkg.Collection)
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
for _, p := range s.Artifacts.Packages.Sorted() {
var ownedFilePaths []string
if p.Type == pkg.BinaryPkg {
for _, loc := range p.Locations.ToSlice() {
if archive.ContainerPath(loc.Coordinates) != "" {
// a path within an archive cannot be resolved from the scan root; the location is the library
addLibraryOwner(packagesByLibraryPath, allLibLocations, loc.Coordinates, p)
continue
}
ownedFilePaths = append(ownedFilePaths, loc.Path())
}
} else {
fileOwner, ok := p.Metadata.(pkg.FileOwner)
if !ok {
continue
}
ownedFilePaths = fileOwner.OwnedFiles()
}
packagesByLibraryPath = populatePackagesByLibraryPath(resolver, allLibLocations, packagesByLibraryPath, p, ownedFilePaths)
}
})
return packagesByLibraryPath
}
func populatePackagesByLibraryPath(
resolver file.Resolver,
allLibLocations file.CoordinateSet,
packagesByLibraryPath map[file.Coordinates]*pkg.Collection,
p pkg.Package,
ownedFilePaths []string,
) map[file.Coordinates]*pkg.Collection {
for _, pth := range ownedFilePaths {
ownedLocation, err := resolver.FilesByPath(pth)
if err != nil {
log.WithFields("error", err, "path", pth).Trace("unable to find path for owned file")
continue
}
for _, loc := range ownedLocation {
addLibraryOwner(packagesByLibraryPath, allLibLocations, loc.Coordinates, p)
}
}
return packagesByLibraryPath
}
// addLibraryOwner records p as an owner of the file at coordinates when that file is a library.
func addLibraryOwner(packagesByLibraryPath map[file.Coordinates]*pkg.Collection, allLibLocations file.CoordinateSet, coordinates file.Coordinates, p pkg.Package) {
if !allLibLocations.Contains(coordinates) {
return
}
if _, ok := packagesByLibraryPath[coordinates]; !ok {
packagesByLibraryPath[coordinates] = pkg.NewCollection()
}
packagesByLibraryPath[coordinates].Add(p)
}