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