github.com/anchore/syft@v1.38.2/internal/relationship/exclude_binaries_by_file_ownership_overlap.go (about)

     1  package relationship
     2  
     3  import (
     4  	"reflect"
     5  	"slices"
     6  
     7  	"github.com/anchore/syft/internal/sbomsync"
     8  	"github.com/anchore/syft/syft/artifact"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/anchore/syft/syft/sbom"
    11  )
    12  
    13  var (
    14  	osCatalogerTypes = []pkg.Type{
    15  		pkg.AlpmPkg,
    16  		pkg.ApkPkg,
    17  		pkg.DebPkg,
    18  		pkg.NixPkg,
    19  		pkg.PortagePkg,
    20  		pkg.RpmPkg,
    21  	}
    22  	binaryCatalogerTypes = []pkg.Type{
    23  		pkg.BinaryPkg,
    24  	}
    25  	bitnamiCatalogerTypes = []pkg.Type{
    26  		pkg.BitnamiPkg,
    27  	}
    28  	binaryMetadataTypes = []string{
    29  		reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
    30  		reflect.TypeOf(pkg.BinarySignature{}).Name(),
    31  		reflect.TypeOf(pkg.JavaVMInstallation{}).Name(),
    32  	}
    33  )
    34  
    35  func ExcludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) {
    36  	accessor.WriteToSBOM(func(s *sbom.SBOM) {
    37  		for _, r := range s.Relationships {
    38  			if idToRemove := excludeByFileOwnershipOverlap(r, s.Artifacts.Packages); idToRemove != "" {
    39  				s.Artifacts.Packages.Delete(idToRemove)
    40  				s.Relationships = RemoveRelationshipsByID(s.Relationships, idToRemove)
    41  			}
    42  		}
    43  	})
    44  }
    45  
    46  // excludeByFileOwnershipOverlap will remove packages that should be overridden by a more authoritative package,
    47  // such as an OS package or a package from a cataloger with more specific information being raised up.
    48  func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) artifact.ID {
    49  	if artifact.OwnershipByFileOverlapRelationship != r.Type {
    50  		return ""
    51  	}
    52  
    53  	parent := c.Package(r.From.ID())
    54  	if parent == nil {
    55  		return ""
    56  	}
    57  
    58  	child := c.Package(r.To.ID())
    59  	if child == nil {
    60  		return ""
    61  	}
    62  
    63  	if idToRemove := identifyOverlappingOSRelationship(parent, child); idToRemove != "" {
    64  		return idToRemove
    65  	}
    66  
    67  	if idToRemove := identifyOverlappingJVMRelationship(parent, child); idToRemove != "" {
    68  		return idToRemove
    69  	}
    70  
    71  	if idToRemove := identifyOverlappingBitnamiRelationship(parent, child); idToRemove != "" {
    72  		return idToRemove
    73  	}
    74  
    75  	return ""
    76  }
    77  
    78  // identifyOverlappingJVMRelationship indicates the package to remove if this is a binary -> binary pkg relationship
    79  // with a java binary signature package and a more authoritative JVM release package.
    80  func identifyOverlappingJVMRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
    81  	if !slices.Contains(binaryCatalogerTypes, parent.Type) {
    82  		return ""
    83  	}
    84  
    85  	if !slices.Contains(binaryCatalogerTypes, child.Type) {
    86  		return ""
    87  	}
    88  
    89  	if child.Metadata == nil {
    90  		return ""
    91  	}
    92  
    93  	var (
    94  		foundJVM   bool
    95  		idToRemove artifact.ID
    96  	)
    97  	for _, p := range []*pkg.Package{parent, child} {
    98  		switch p.Metadata.(type) {
    99  		case pkg.JavaVMInstallation:
   100  			foundJVM = true
   101  		default:
   102  			idToRemove = p.ID()
   103  		}
   104  	}
   105  
   106  	if foundJVM {
   107  		return idToRemove
   108  	}
   109  
   110  	return ""
   111  }
   112  
   113  // identifyOverlappingOSRelationship indicates the package ID to remove if this is an OS pkg -> bin pkg relationship.
   114  // This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
   115  func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
   116  	if !slices.Contains(osCatalogerTypes, parent.Type) {
   117  		return ""
   118  	}
   119  
   120  	if slices.Contains(binaryCatalogerTypes, child.Type) {
   121  		return child.ID()
   122  	}
   123  
   124  	if child.Metadata == nil {
   125  		return ""
   126  	}
   127  
   128  	if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
   129  		return ""
   130  	}
   131  
   132  	return child.ID()
   133  }
   134  
   135  // identifyOverlappingBitnamiRelationship indicates the package ID to remove if this is a Bitnami pkg -> bin pkg relationship.
   136  func identifyOverlappingBitnamiRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
   137  	if !slices.Contains(bitnamiCatalogerTypes, parent.Type) {
   138  		return ""
   139  	}
   140  
   141  	if slices.Contains(binaryCatalogerTypes, child.Type) {
   142  		return child.ID()
   143  	}
   144  
   145  	if child.Metadata == nil {
   146  		return ""
   147  	}
   148  
   149  	if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
   150  		return ""
   151  	}
   152  
   153  	return child.ID()
   154  }