syft/internal/sbomsync/builder.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

125 lines
2.4 KiB
Go

package sbomsync
import (
"sync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
var _ interface {
Accessor
Builder
} = (*sbomBuilder)(nil) // integrity check
// Builder provides a simple facade for simple additions to the SBOM
type Builder interface {
// nodes
AddPackages(...pkg.Package)
DeletePackages(...artifact.ID)
// edges
AddRelationships(...artifact.Relationship)
// other
SetLinuxDistribution(linux.Release)
}
// Accessor allows for low-level access to the SBOM
type Accessor interface {
WriteToSBOM(func(*sbom.SBOM))
ReadFromSBOM(func(*sbom.SBOM))
}
type sbomBuilder struct {
sbom *sbom.SBOM
lock *sync.RWMutex
onWrite []func(*sbom.SBOM)
}
func NewBuilder(s *sbom.SBOM, onWrite ...func(*sbom.SBOM)) Builder {
return &sbomBuilder{
sbom: s,
lock: &sync.RWMutex{},
onWrite: onWrite,
}
}
func (b sbomBuilder) onWriteEvent() {
for _, fn := range b.onWrite {
fn(b.sbom)
}
}
func (b sbomBuilder) WriteToSBOM(fn func(*sbom.SBOM)) {
b.lock.Lock()
defer b.lock.Unlock()
fn(b.sbom)
b.onWriteEvent()
}
func (b sbomBuilder) ReadFromSBOM(fn func(*sbom.SBOM)) {
b.lock.RLock()
defer b.lock.RUnlock()
fn(b.sbom)
}
func (b sbomBuilder) AddPackages(p ...pkg.Package) {
b.lock.Lock()
defer b.lock.Unlock()
b.sbom.Artifacts.Packages.Add(p...)
b.onWriteEvent()
}
func (b sbomBuilder) DeletePackages(ids ...artifact.ID) {
b.lock.Lock()
defer b.lock.Unlock()
deleted := make(map[artifact.ID]struct{})
for _, id := range ids {
b.sbom.Artifacts.Packages.Delete(id)
deleted[id] = struct{}{}
}
// remove any relationships that reference the deleted packages
var relationships []artifact.Relationship
for _, rel := range b.sbom.Relationships {
if _, ok := deleted[rel.From.ID()]; ok {
continue
}
if _, ok := deleted[rel.To.ID()]; ok {
continue
}
// only keep relationships that don't reference the deleted packages
relationships = append(relationships, rel)
}
b.sbom.Relationships = relationships
b.onWriteEvent()
}
func (b sbomBuilder) AddRelationships(relationship ...artifact.Relationship) {
b.lock.Lock()
defer b.lock.Unlock()
b.sbom.Relationships = append(b.sbom.Relationships, relationship...)
b.onWriteEvent()
}
func (b sbomBuilder) SetLinuxDistribution(release linux.Release) {
b.lock.Lock()
defer b.lock.Unlock()
b.sbom.Artifacts.LinuxDistribution = &release
b.onWriteEvent()
}