github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/internal/relationship/binary/shared_library_index.go (about) 1 package binary 2 3 import ( 4 "path" 5 6 "github.com/anchore/syft/internal/log" 7 "github.com/anchore/syft/internal/sbomsync" 8 "github.com/anchore/syft/syft/file" 9 "github.com/anchore/syft/syft/pkg" 10 "github.com/anchore/syft/syft/sbom" 11 ) 12 13 type sharedLibraryIndex struct { 14 libLocationsByBasename map[string]file.CoordinateSet 15 allLibLocations file.CoordinateSet 16 packagesByLibraryPath map[file.Coordinates]*pkg.Collection 17 } 18 19 func newShareLibIndex(resolver file.Resolver, accessor sbomsync.Accessor) *sharedLibraryIndex { 20 s := &sharedLibraryIndex{ 21 libLocationsByBasename: make(map[string]file.CoordinateSet), 22 allLibLocations: file.NewCoordinateSet(), 23 packagesByLibraryPath: make(map[file.Coordinates]*pkg.Collection), 24 } 25 26 s.build(resolver, accessor) 27 28 return s 29 } 30 31 func (i *sharedLibraryIndex) build(resolver file.Resolver, accessor sbomsync.Accessor) { 32 // 1. map out all locations that provide libraries (indexed by the basename) 33 i.libLocationsByBasename, i.allLibLocations = locationsThatProvideLibraries(accessor) 34 35 // 2. for each library path, find all packages that claim ownership of the library 36 i.packagesByLibraryPath = packagesWithLibraryOwnership(resolver, accessor, i.allLibLocations) 37 } 38 39 func (i *sharedLibraryIndex) owningLibraryPackage(libraryBasename string) *pkg.Collection { 40 // find all packages that own a library by its basename 41 collection := pkg.NewCollection() 42 if set, ok := i.libLocationsByBasename[libraryBasename]; ok { 43 for _, coord := range set.ToSlice() { 44 if pkgSet, ok := i.packagesByLibraryPath[coord]; ok { 45 toAdd := pkgSet.Sorted() 46 collection.Add(toAdd...) 47 } 48 } 49 } 50 51 return collection 52 } 53 54 func locationsThatProvideLibraries(accessor sbomsync.Accessor) (map[string]file.CoordinateSet, file.CoordinateSet) { 55 // map out all locations that provide libraries (indexed by the basename) 56 libLocationsByBasename := make(map[string]file.CoordinateSet) 57 allLibLocations := file.NewCoordinateSet() 58 59 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 60 // PROBLEM: this does not consider all symlinks to real paths that are libraries 61 for coord, f := range s.Artifacts.Executables { 62 if !f.HasExports { 63 continue 64 } 65 66 basename := path.Base(coord.RealPath) 67 set := libLocationsByBasename[basename] 68 set.Add(coord) 69 allLibLocations.Add(coord) 70 libLocationsByBasename[basename] = set 71 } 72 }) 73 74 return libLocationsByBasename, allLibLocations 75 } 76 func packagesWithLibraryOwnership(resolver file.Resolver, accessor sbomsync.Accessor, allLibLocations file.CoordinateSet) map[file.Coordinates]*pkg.Collection { 77 // map out all packages that claim ownership of a library at a specific path 78 packagesByLibraryPath := make(map[file.Coordinates]*pkg.Collection) 79 80 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 81 for _, p := range s.Artifacts.Packages.Sorted() { 82 var ownedFilePaths []string 83 if p.Type == pkg.BinaryPkg { 84 for _, loc := range p.Locations.ToSlice() { 85 ownedFilePaths = append(ownedFilePaths, loc.Path()) 86 } 87 } else { 88 fileOwner, ok := p.Metadata.(pkg.FileOwner) 89 if !ok { 90 continue 91 } 92 ownedFilePaths = fileOwner.OwnedFiles() 93 } 94 95 packagesByLibraryPath = populatePackagesByLibraryPath(resolver, allLibLocations, packagesByLibraryPath, p, ownedFilePaths) 96 } 97 }) 98 99 return packagesByLibraryPath 100 } 101 102 func populatePackagesByLibraryPath( 103 resolver file.Resolver, 104 allLibLocations file.CoordinateSet, 105 packagesByLibraryPath map[file.Coordinates]*pkg.Collection, 106 p pkg.Package, 107 ownedFilePaths []string, 108 ) map[file.Coordinates]*pkg.Collection { 109 for _, pth := range ownedFilePaths { 110 ownedLocation, err := resolver.FilesByPath(pth) 111 if err != nil { 112 log.WithFields("error", err, "path", pth).Trace("unable to find path for owned file") 113 continue 114 } 115 116 for _, loc := range ownedLocation { 117 // if the location is a library, add the package to the set of packages that own the library 118 if !allLibLocations.Contains(loc.Coordinates) { 119 continue 120 } 121 122 if _, ok := packagesByLibraryPath[loc.Coordinates]; !ok { 123 packagesByLibraryPath[loc.Coordinates] = pkg.NewCollection() 124 } 125 126 // we have a library path, add the package to the set of packages that own the library 127 packagesByLibraryPath[loc.Coordinates].Add(p) 128 } 129 } 130 return packagesByLibraryPath 131 }