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

     1  package sbomsync
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/anchore/syft/syft/artifact"
     7  	"github.com/anchore/syft/syft/linux"
     8  	"github.com/anchore/syft/syft/pkg"
     9  	"github.com/anchore/syft/syft/sbom"
    10  )
    11  
    12  var _ interface {
    13  	Accessor
    14  	Builder
    15  } = (*sbomBuilder)(nil) // integrity check
    16  
    17  // Builder provides a simple facade for simple additions to the SBOM
    18  type Builder interface {
    19  	// nodes
    20  
    21  	AddPackages(...pkg.Package)
    22  
    23  	DeletePackages(...artifact.ID)
    24  
    25  	// edges
    26  
    27  	AddRelationships(...artifact.Relationship)
    28  
    29  	// other
    30  
    31  	SetLinuxDistribution(linux.Release)
    32  }
    33  
    34  // Accessor allows for low-level access to the SBOM
    35  type Accessor interface {
    36  	WriteToSBOM(func(*sbom.SBOM))
    37  	ReadFromSBOM(func(*sbom.SBOM))
    38  }
    39  
    40  type sbomBuilder struct {
    41  	sbom    *sbom.SBOM
    42  	lock    *sync.RWMutex
    43  	onWrite []func(*sbom.SBOM)
    44  }
    45  
    46  func NewBuilder(s *sbom.SBOM, onWrite ...func(*sbom.SBOM)) Builder {
    47  	return &sbomBuilder{
    48  		sbom:    s,
    49  		lock:    &sync.RWMutex{},
    50  		onWrite: onWrite,
    51  	}
    52  }
    53  
    54  func (b sbomBuilder) onWriteEvent() {
    55  	for _, fn := range b.onWrite {
    56  		fn(b.sbom)
    57  	}
    58  }
    59  
    60  func (b sbomBuilder) WriteToSBOM(fn func(*sbom.SBOM)) {
    61  	b.lock.Lock()
    62  	defer b.lock.Unlock()
    63  
    64  	fn(b.sbom)
    65  	b.onWriteEvent()
    66  }
    67  
    68  func (b sbomBuilder) ReadFromSBOM(fn func(*sbom.SBOM)) {
    69  	b.lock.RLock()
    70  	defer b.lock.RUnlock()
    71  
    72  	fn(b.sbom)
    73  }
    74  
    75  func (b sbomBuilder) AddPackages(p ...pkg.Package) {
    76  	b.lock.Lock()
    77  	defer b.lock.Unlock()
    78  
    79  	b.sbom.Artifacts.Packages.Add(p...)
    80  	b.onWriteEvent()
    81  }
    82  
    83  func (b sbomBuilder) DeletePackages(ids ...artifact.ID) {
    84  	b.lock.Lock()
    85  	defer b.lock.Unlock()
    86  
    87  	deleted := make(map[artifact.ID]struct{})
    88  	for _, id := range ids {
    89  		b.sbom.Artifacts.Packages.Delete(id)
    90  		deleted[id] = struct{}{}
    91  	}
    92  
    93  	// remove any relationships that reference the deleted packages
    94  	var relationships []artifact.Relationship
    95  	for _, rel := range b.sbom.Relationships {
    96  		if _, ok := deleted[rel.From.ID()]; ok {
    97  			continue
    98  		}
    99  		if _, ok := deleted[rel.To.ID()]; ok {
   100  			continue
   101  		}
   102  
   103  		// only keep relationships that don't reference the deleted packages
   104  		relationships = append(relationships, rel)
   105  	}
   106  
   107  	b.sbom.Relationships = relationships
   108  	b.onWriteEvent()
   109  }
   110  
   111  func (b sbomBuilder) AddRelationships(relationship ...artifact.Relationship) {
   112  	b.lock.Lock()
   113  	defer b.lock.Unlock()
   114  
   115  	b.sbom.Relationships = append(b.sbom.Relationships, relationship...)
   116  	b.onWriteEvent()
   117  }
   118  
   119  func (b sbomBuilder) SetLinuxDistribution(release linux.Release) {
   120  	b.lock.Lock()
   121  	defer b.lock.Unlock()
   122  
   123  	b.sbom.Artifacts.LinuxDistribution = &release
   124  	b.onWriteEvent()
   125  }