github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/internal/relationship/binary/binary_dependencies.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/artifact" 9 "github.com/anchore/syft/syft/file" 10 "github.com/anchore/syft/syft/pkg" 11 "github.com/anchore/syft/syft/sbom" 12 ) 13 14 func NewDependencyRelationships(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.Relationship { 15 // TODO: consider library format (e.g. ELF, Mach-O, PE) for the meantime assume all binaries are homogeneous format 16 // start with building new package-to-package relationships for executables-to-executables 17 // each relationship must be unique, store in a map[id]map[id]relationship to avoid duplicates 18 // 1 & 2... build an index of all shared libraries and their owning packages to search against 19 index := newShareLibIndex(resolver, accessor) 20 21 // 3. craft package-to-package relationships for each binary that represent shared library dependencies 22 //note: we only care about package-to-package relationships 23 var relIndex *relationshipIndex 24 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 25 relIndex = newRelationshipIndex(s.Relationships...) 26 }) 27 28 return generateRelationships(resolver, accessor, index, relIndex) 29 } 30 31 func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, index *sharedLibraryIndex, relIndex *relationshipIndex) []artifact.Relationship { 32 // read all existing dependencyOf relationships 33 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 34 for _, r := range s.Relationships { 35 if r.Type != artifact.DependencyOfRelationship { 36 continue 37 } 38 relIndex.track(r) 39 } 40 }) 41 42 // find all package-to-package relationships for shared library dependencies 43 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 44 for _, parentPkg := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) { 45 for _, evidentLocation := range parentPkg.Locations.ToSlice() { 46 if evidentLocation.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation { 47 continue 48 } 49 50 // find all libraries that this package depends on 51 exec, ok := s.Artifacts.Executables[evidentLocation.Coordinates] 52 if !ok { 53 continue 54 } 55 56 populateRelationships(exec, parentPkg, resolver, relIndex, index) 57 } 58 } 59 }) 60 61 return relIndex.newRelationships() 62 } 63 64 // PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM 65 // These packages are removed because they are already represented by a higher order packages in the SBOM. 66 func PackagesToRemove(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.ID { 67 pkgsToDelete := make([]artifact.ID, 0) 68 accessor.ReadFromSBOM(func(s *sbom.SBOM) { 69 // OTHER package type > ELF package type > Binary package type 70 pkgsToDelete = append(pkgsToDelete, getBinaryPackagesToDelete(resolver, s)...) 71 pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(s)...) 72 }) 73 return pkgsToDelete 74 } 75 76 func compareElfBinaryPackages(s *sbom.SBOM) []artifact.ID { 77 pkgsToDelete := make([]artifact.ID, 0) 78 for _, elfPkg := range allElfPackages(s) { 79 for _, loc := range onlyPrimaryEvidenceLocations(elfPkg) { 80 for _, otherPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) { 81 // we only care about comparing binary packages to each other (not other types) 82 if otherPkg.Type != pkg.BinaryPkg { 83 continue 84 } 85 if !isElfPackage(otherPkg) { 86 pkgsToDelete = append(pkgsToDelete, otherPkg.ID()) 87 } 88 } 89 } 90 } 91 return pkgsToDelete 92 } 93 94 func onlyPrimaryEvidenceLocations(p pkg.Package) []file.Location { 95 var locs []file.Location 96 for _, loc := range p.Locations.ToSlice() { 97 if loc.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation { 98 continue 99 } 100 locs = append(locs, loc) 101 } 102 103 return locs 104 } 105 106 func allElfPackages(s *sbom.SBOM) []pkg.Package { 107 var elfPkgs []pkg.Package 108 for _, p := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) { 109 if !isElfPackage(p) { 110 continue 111 } 112 elfPkgs = append(elfPkgs, p) 113 } 114 return elfPkgs 115 } 116 117 func isElfPackage(p pkg.Package) bool { 118 _, ok := p.Metadata.(pkg.ELFBinaryPackageNoteJSONPayload) 119 return ok 120 } 121 122 func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.ID { 123 pkgsToDelete := make([]artifact.ID, 0) 124 for p := range s.Artifacts.Packages.Enumerate() { 125 if p.Type == pkg.BinaryPkg { 126 continue 127 } 128 fileOwner, ok := p.Metadata.(pkg.FileOwner) 129 if !ok { 130 continue 131 } 132 ownedFiles := fileOwner.OwnedFiles() 133 locations, err := resolver.FilesByPath(ownedFiles...) 134 if err != nil { 135 log.WithFields("error", err).Trace("unable to find path for owned file") 136 continue 137 } 138 for _, loc := range locations { 139 for _, pathPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) { 140 if pathPkg.Type == pkg.BinaryPkg { 141 pkgsToDelete = append(pkgsToDelete, pathPkg.ID()) 142 } 143 } 144 } 145 } 146 return pkgsToDelete 147 } 148 149 func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, relIndex *relationshipIndex, index *sharedLibraryIndex) { 150 for _, libReference := range exec.ImportedLibraries { 151 // for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library 152 // if found, create a relationship between the parent package and the library package 153 // if not found do nothing. 154 // note: we only care about package-to-package relationships 155 156 // find the basename of the library 157 libBasename := path.Base(libReference) 158 libLocations, err := resolver.FilesByGlob("**/" + libBasename) 159 if err != nil { 160 log.WithFields("lib", libReference, "error", err).Trace("unable to resolve library basename") 161 continue 162 } 163 164 for _, loc := range libLocations { 165 // are you in our index? 166 realBaseName := path.Base(loc.RealPath) 167 pkgCollection := index.owningLibraryPackage(realBaseName) 168 if pkgCollection.PackageCount() < 1 { 169 relIndex.add( 170 artifact.Relationship{ 171 From: loc.Coordinates, 172 To: parentPkg, 173 Type: artifact.DependencyOfRelationship, 174 }, 175 ) 176 } 177 for _, p := range pkgCollection.Sorted() { 178 relIndex.add( 179 artifact.Relationship{ 180 From: p, 181 To: parentPkg, 182 Type: artifact.DependencyOfRelationship, 183 }, 184 ) 185 } 186 } 187 } 188 }