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 }