syft/syft/sbom/sbom.go
Brian Ebarb 4194a2cd34
feat: add relationships to ELF package discovery (#2715)
This PR adds DependencyOf relationships when ELF packages have been discovered by the binary cataloger. The discovered file.Executable type has a []ImportedLibraries that's read from the file when discovered by syft. By mapping these imported libraries back to the package collection, syft is able to create relationships showing which packages are dependencies of other packages by just reading metadata from the ELF executable.

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Brian Ebarb <ebarb.brian@sers.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-05-09 13:53:59 -04:00

126 lines
3.6 KiB
Go

package sbom
import (
"slices"
"sort"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
type SBOM struct {
Artifacts Artifacts
Relationships []artifact.Relationship
Source source.Description
Descriptor Descriptor
}
type Artifacts struct {
Packages *pkg.Collection
FileMetadata map[file.Coordinates]file.Metadata
FileDigests map[file.Coordinates][]file.Digest
FileContents map[file.Coordinates]string
FileLicenses map[file.Coordinates][]file.License
Executables map[file.Coordinates]file.Executable
LinuxDistribution *linux.Release
}
type Descriptor struct {
Name string
Version string
Configuration interface{}
}
func (s SBOM) RelationshipsSorted() []artifact.Relationship {
relationships := s.Relationships
sort.SliceStable(relationships, func(i, j int) bool {
if relationships[i].From.ID() == relationships[j].From.ID() {
if relationships[i].To.ID() == relationships[j].To.ID() {
return relationships[i].Type < relationships[j].Type
}
return relationships[i].To.ID() < relationships[j].To.ID()
}
return relationships[i].From.ID() < relationships[j].From.ID()
})
return relationships
}
func (s SBOM) AllCoordinates() []file.Coordinates {
set := file.NewCoordinateSet()
for coordinates := range s.Artifacts.FileMetadata {
set.Add(coordinates)
}
for coordinates := range s.Artifacts.FileContents {
set.Add(coordinates)
}
for coordinates := range s.Artifacts.FileDigests {
set.Add(coordinates)
}
for _, relationship := range s.Relationships {
for _, coordinates := range extractCoordinates(relationship) {
set.Add(coordinates)
}
}
return set.ToSlice()
}
// RelationshipsForPackage returns all relationships for the provided types.
// If no types are provided, all relationships for the package are returned.
func (s SBOM) RelationshipsForPackage(p pkg.Package, rt ...artifact.RelationshipType) []artifact.Relationship {
if len(rt) == 0 {
rt = artifact.AllRelationshipTypes()
}
pID := p.ID()
var relationships []artifact.Relationship
for _, relationship := range s.Relationships {
if relationship.From == nil || relationship.To == nil {
log.Debugf("relationship has nil edge, skipping: %#v", relationship)
continue
}
fromID := relationship.From.ID()
toID := relationship.To.ID()
hasPkgID := fromID == pID || toID == pID
if !hasPkgID {
continue
}
// check if the relationship is one we're searching for; rt is inclusive
if !slices.ContainsFunc(rt, func(r artifact.RelationshipType) bool { return relationship.Type == r }) {
continue
}
relationships = append(relationships, relationship)
}
return relationships
}
// CoordinatesForPackage returns all coordinates for the provided package for provided relationship types
// If no types are provided, all relationship types are considered.
func (s SBOM) CoordinatesForPackage(p pkg.Package, rt ...artifact.RelationshipType) []file.Coordinates {
var coordinates []file.Coordinates
for _, relationship := range s.RelationshipsForPackage(p, rt...) {
cords := extractCoordinates(relationship)
coordinates = append(coordinates, cords...)
}
return coordinates
}
func extractCoordinates(relationship artifact.Relationship) (results []file.Coordinates) {
if coordinates, exists := relationship.From.(file.Coordinates); exists {
results = append(results, coordinates)
}
if coordinates, exists := relationship.To.(file.Coordinates); exists {
results = append(results, coordinates)
}
return results
}