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  }