🧬 SBOM Enhanced
The sbom_enhanced module adds advanced SBOM operations on top of the core model: merging multiple SBOMs, diffing two SBOMs, sorting/filtering/deduplicating components, enriching components with pedigree and forensic evidence, and basic validation.
Type: SBOMEvidence
type SBOMEvidence struct {
Field string // evidence field (e.g. "filename", "hash", "snippet")
Value string // evidence value
Confidence float64 // detection confidence (0.0-1.0)
}Type: SBOMPedigree
type SBOMPedigree struct {
Ancestors []*SBOMComponent // ancestor components
Descendants []*SBOMComponent // descendant components
Variants []*SBOMComponent // variant components
Commits []*SBOMCommit // VCS commits
Patches []*SBOMPatch // patches applied
Notes string // additional notes
}Type: SBOMCommit
type SBOMCommit struct {
UID string // commit hash
URL string // repository URL
Author *SBOMAuthor // commit author
Message string // commit message
}Type: SBOMPatch
type SBOMPatch struct {
Type string // patch type (backport, cherry-pick, monkey, etc.)
Diff string // diff content or URL
Resolves []string // issue IDs resolved by this patch
}Type: SBOMDiff
type SBOMDiff struct {
Added []*SBOMComponent // in new SBOM but not old
Removed []*SBOMComponent // in old SBOM but not new
Changed []*SBOMComponentChange // present in both, different version
Unchanged int // unchanged component count
}Type: SBOMComponentChange
type SBOMComponentChange struct {
Component *SBOMComponent // the changed component
OldVersion string // previous version
NewVersion string // new version
ChangeType string // upgrade, downgrade, or sidegrade
}🔀 MergeSBOMs
func MergeSBOMs(sboms []*SBOM, format SBOMFormat, name string) (*SBOM, error)Merges multiple SBOMs into one. Components are deduplicated by PURL (preferred), then CPE 2.3, then bom-ref, then name; when a duplicate is found, the higher version (per CompareVersions) wins. All dependencies and metadata tools/authors from every source SBOM are preserved.
| Parameter | Type | Description |
|---|---|---|
sboms | []*SBOM | source SBOMs |
format | SBOMFormat | format of the merged SBOM |
name | string | merged document name |
| Return | Type | Description |
|---|---|---|
| #1 | *SBOM | merged SBOM |
| #2 | error | non-nil if the input slice is empty |
merged, err := cpeskills.MergeSBOMs([]*cpeskills.SBOM{sbom1, sbom2}, cpeskills.SBOMFormatCycloneDX, "merged")🔍 DiffSBOMs
func DiffSBOMs(oldSBOM, newSBOM *SBOM) *SBOMDiffComputes the difference between two SBOMs. Returns a SBOMDiff with added, removed, and changed components plus the unchanged count. A changed component's ChangeType is "upgrade" when the new version is higher and "downgrade" when lower (via CompareVersions). If either argument is nil, all components of the other side are reported as added/removed.
| Parameter | Type | Description |
|---|---|---|
oldSBOM | *SBOM | baseline SBOM |
newSBOM | *SBOM | current SBOM |
| Return | Type | Description |
|---|---|---|
| #1 | *SBOMDiff | computed diff |
diff := cpeskills.DiffSBOMs(oldSBOM, newSBOM)
fmt.Println(diff.Summary())❓ HasChanges
func (d *SBOMDiff) HasChanges() boolReturns true if the diff has any added, removed, or changed components.
| Return | Type | Description |
|---|---|---|
| #1 | bool | true if any changes exist |
if diff.HasChanges() {
fmt.Println("SBOM changed")
}🔢 TotalChanges
func (d *SBOMDiff) TotalChanges() intReturns the total number of added, removed, and changed components.
| Return | Type | Description |
|---|---|---|
| #1 | int | total change count |
fmt.Printf("%d total changes\n", diff.TotalChanges())📝 Summary
func (d *SBOMDiff) Summary() stringReturns a human-readable summary such as "3 added, 1 removed, 2 changed". When there are no changes, returns "No changes (N components unchanged)".
| Return | Type | Description |
|---|---|---|
| #1 | string | summary text |
fmt.Println(diff.Summary())↕️ SortComponentsByName
func SortComponentsByName(components []*SBOMComponent)Sorts the slice in place alphabetically by component Name (ascending).
| Parameter | Type | Description |
|---|---|---|
components | []*SBOMComponent | slice to sort in place |
cpeskills.SortComponentsByName(sbom.Components)↕️ SortComponentsByRisk
func SortComponentsByRisk(components []*SBOMComponent, nvdData *NVDCPEData) []*RiskScoreScores the components via ScoreComponents (using nvdData), sorts the scores in descending risk order via SortByRisk, and returns the sorted scores. The input slice is not reordered.
| Parameter | Type | Description |
|---|---|---|
components | []*SBOMComponent | components to score |
nvdData | *NVDCPEData | NVD data used for CVE lookup |
| Return | Type | Description |
|---|---|---|
| #1 | []*RiskScore | scores sorted by descending OverallScore |
scores := cpeskills.SortComponentsByRisk(sbom.Components, nvdData)
for _, s := range scores {
fmt.Printf("%s: %.1f\n", s.Component.Name, s.OverallScore)
}🔍 FilterComponentsByEcosystem
func FilterComponentsByEcosystem(components []*SBOMComponent, ecosystem Ecosystem) []*SBOMComponentReturns the components whose PURL maps to the given ecosystem (via PackageURL.Ecosystem()).
| Parameter | Type | Description |
|---|---|---|
components | []*SBOMComponent | input components |
ecosystem | Ecosystem | target ecosystem |
| Return | Type | Description |
|---|---|---|
| #1 | []*SBOMComponent | matching components |
maven := cpeskills.FilterComponentsByEcosystem(sbom.Components, cpeskills.EcosystemMaven)🔍 FilterComponentsByType
func FilterComponentsByType(components []*SBOMComponent, compType string) []*SBOMComponentReturns the components whose Type matches compType case-insensitively.
| Parameter | Type | Description |
|---|---|---|
components | []*SBOMComponent | input components |
compType | string | target type (e.g. "library") |
| Return | Type | Description |
|---|---|---|
| #1 | []*SBOMComponent | matching components |
libs := cpeskills.FilterComponentsByType(sbom.Components, "library")🧹 DeduplicateComponents
func DeduplicateComponents(components []*SBOMComponent) []*SBOMComponentRemoves duplicates identified by PURL (preferred), CPE 2.3, bom-ref, or name. The first occurrence is kept.
| Parameter | Type | Description |
|---|---|---|
components | []*SBOMComponent | input components |
| Return | Type | Description |
|---|---|---|
| #1 | []*SBOMComponent | deduplicated components |
deduped := cpeskills.DeduplicateComponents(allComponents)🧬 EnrichComponentWithPedigree
func EnrichComponentWithPedigree(component *SBOMComponent, pedigree *SBOMPedigree)Marks the component as having pedigree by setting Properties["cpe:hasPedigree"] = "true". (The pedigree object itself is not attached to the component by this function.)
| Parameter | Type | Description |
|---|---|---|
component | *SBOMComponent | component to enrich |
pedigree | *SBOMPedigree | pedigree information |
pedigree := cpeskills.NewSBOMPedigree()
cpeskills.EnrichComponentWithPedigree(comp, pedigree)🔬 EnrichComponentWithEvidence
func EnrichComponentWithEvidence(component *SBOMComponent, evidence []*SBOMEvidence)Writes forensic evidence into the component's Properties, keyed cpe:evidence:<i>:field and cpe:evidence:<i>:value for each evidence entry.
| Parameter | Type | Description |
|---|---|---|
component | *SBOMComponent | component to enrich |
evidence | []*SBOMEvidence | evidence entries |
ev := []*cpeskills.SBOMEvidence{
cpeskills.NewSBOMEvidence("filename", "log4j-core.jar", 0.95),
}
cpeskills.EnrichComponentWithEvidence(comp, ev)⚙️ SetComponentCopyright
func SetComponentCopyright(component *SBOMComponent, copyright string)Stores the copyright string under Properties["cpe:copyright"].
| Parameter | Type | Description |
|---|---|---|
component | *SBOMComponent | component to update |
copyright | string | copyright text |
cpeskills.SetComponentCopyright(comp, "Copyright 2026 Acme")🆕 NewSBOMEvidence
func NewSBOMEvidence(field, value string, confidence float64) *SBOMEvidenceCreates a new evidence entry.
| Parameter | Type | Description |
|---|---|---|
field | string | evidence field |
value | string | evidence value |
confidence | float64 | confidence 0.0-1.0 |
| Return | Type | Description |
|---|---|---|
| #1 | *SBOMEvidence | evidence entry |
ev := cpeskills.NewSBOMEvidence("hash", "sha256:...", 1.0)🆕 NewSBOMPedigree
func NewSBOMPedigree() *SBOMPedigreeCreates a new empty pedigree with all slice fields initialized.
| Return | Type | Description |
|---|---|---|
| #1 | *SBOMPedigree | empty pedigree |
pedigree := cpeskills.NewSBOMPedigree()➕ AddAncestor
func (p *SBOMPedigree) AddAncestor(component *SBOMComponent)Appends an ancestor component to the pedigree.
| Parameter | Type | Description |
|---|---|---|
component | *SBOMComponent | ancestor component |
pedigree.AddAncestor(ancestorComp)➕ AddCommit
func (p *SBOMPedigree) AddCommit(uid, url, message string)Appends a VCS commit to the pedigree's Commits.
| Parameter | Type | Description |
|---|---|---|
uid | string | commit hash |
url | string | repository URL |
message | string | commit message |
pedigree.AddCommit("9f1b2c4", "https://github.com/acme/lib", "fix: vuln")✅ ValidateSBOM
func ValidateSBOM(sbom *SBOM) []stringPerforms basic validation and returns a list of issue strings. Checks: nil SBOM (["SBOM is nil"]), unknown format, empty name, components with empty BomRef, and dependency refs/targets that do not match any component BomRef.
| Parameter | Type | Description |
|---|---|---|
sbom | *SBOM | SBOM to validate |
| Return | Type | Description |
|---|---|---|
| #1 | []string | list of validation issues |
issues := cpeskills.ValidateSBOM(sbom)
for _, issue := range issues {
fmt.Println(issue)
}⏱️ UpdateSBOMTimestamp
func UpdateSBOMTimestamp(sbom *SBOM)Sets sbom.CreatedAt and sbom.Metadata.Timestamp to the current time.
| Parameter | Type | Description |
|---|---|---|
sbom | *SBOM | SBOM to refresh |
cpeskills.UpdateSBOMTimestamp(sbom)