github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/sbom/sbom.go (about)

     1  package sbom
     2  
     3  import (
     4  	"slices"
     5  	"sort"
     6  
     7  	"github.com/anchore/syft/internal/log"
     8  	"github.com/anchore/syft/syft/artifact"
     9  	"github.com/anchore/syft/syft/file"
    10  	"github.com/anchore/syft/syft/linux"
    11  	"github.com/anchore/syft/syft/pkg"
    12  	"github.com/anchore/syft/syft/source"
    13  )
    14  
    15  type SBOM struct {
    16  	Artifacts     Artifacts
    17  	Relationships []artifact.Relationship
    18  	Source        source.Description
    19  	Descriptor    Descriptor
    20  }
    21  
    22  type Artifacts struct {
    23  	Packages          *pkg.Collection
    24  	FileMetadata      map[file.Coordinates]file.Metadata
    25  	FileDigests       map[file.Coordinates][]file.Digest
    26  	FileContents      map[file.Coordinates]string
    27  	FileLicenses      map[file.Coordinates][]file.License
    28  	Executables       map[file.Coordinates]file.Executable
    29  	LinuxDistribution *linux.Release
    30  }
    31  
    32  type Descriptor struct {
    33  	Name          string
    34  	Version       string
    35  	Configuration interface{}
    36  }
    37  
    38  func (s SBOM) RelationshipsSorted() []artifact.Relationship {
    39  	relationships := s.Relationships
    40  	sort.SliceStable(relationships, func(i, j int) bool {
    41  		if relationships[i].From.ID() == relationships[j].From.ID() {
    42  			if relationships[i].To.ID() == relationships[j].To.ID() {
    43  				return relationships[i].Type < relationships[j].Type
    44  			}
    45  			return relationships[i].To.ID() < relationships[j].To.ID()
    46  		}
    47  		return relationships[i].From.ID() < relationships[j].From.ID()
    48  	})
    49  	return relationships
    50  }
    51  
    52  func (s SBOM) AllCoordinates() []file.Coordinates {
    53  	set := file.NewCoordinateSet()
    54  	for coordinates := range s.Artifacts.FileMetadata {
    55  		set.Add(coordinates)
    56  	}
    57  	for coordinates := range s.Artifacts.FileContents {
    58  		set.Add(coordinates)
    59  	}
    60  	for coordinates := range s.Artifacts.FileDigests {
    61  		set.Add(coordinates)
    62  	}
    63  	for _, relationship := range s.Relationships {
    64  		for _, coordinates := range extractCoordinates(relationship) {
    65  			set.Add(coordinates)
    66  		}
    67  	}
    68  	return set.ToSlice()
    69  }
    70  
    71  // RelationshipsForPackage returns all relationships for the provided types.
    72  // If no types are provided, all relationships for the package are returned.
    73  func (s SBOM) RelationshipsForPackage(p pkg.Package, rt ...artifact.RelationshipType) []artifact.Relationship {
    74  	if len(rt) == 0 {
    75  		rt = artifact.AllRelationshipTypes()
    76  	}
    77  
    78  	pID := p.ID()
    79  
    80  	var relationships []artifact.Relationship
    81  	for _, relationship := range s.Relationships {
    82  		if relationship.From == nil || relationship.To == nil {
    83  			log.Debugf("relationship has nil edge, skipping: %#v", relationship)
    84  			continue
    85  		}
    86  		fromID := relationship.From.ID()
    87  		toID := relationship.To.ID()
    88  		hasPkgID := fromID == pID || toID == pID
    89  
    90  		if !hasPkgID {
    91  			continue
    92  		}
    93  
    94  		// check if the relationship is one we're searching for; rt is inclusive
    95  		if !slices.ContainsFunc(rt, func(r artifact.RelationshipType) bool { return relationship.Type == r }) {
    96  			continue
    97  		}
    98  		relationships = append(relationships, relationship)
    99  	}
   100  
   101  	return relationships
   102  }
   103  
   104  // CoordinatesForPackage returns all coordinates for the provided package for provided relationship types
   105  // If no types are provided, all relationship types are considered.
   106  func (s SBOM) CoordinatesForPackage(p pkg.Package, rt ...artifact.RelationshipType) []file.Coordinates {
   107  	var coordinates []file.Coordinates
   108  	for _, relationship := range s.RelationshipsForPackage(p, rt...) {
   109  		cords := extractCoordinates(relationship)
   110  		coordinates = append(coordinates, cords...)
   111  	}
   112  	return coordinates
   113  }
   114  
   115  func extractCoordinates(relationship artifact.Relationship) (results []file.Coordinates) {
   116  	if coordinates, exists := relationship.From.(file.Coordinates); exists {
   117  		results = append(results, coordinates)
   118  	}
   119  
   120  	if coordinates, exists := relationship.To.(file.Coordinates); exists {
   121  		results = append(results, coordinates)
   122  	}
   123  
   124  	return results
   125  }