Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package sbom

import (
"strings"
"testing"
)

func TestFilterProperties(t *testing.T) {
s := New(TypeCycloneDX)
s.AddPackage(Package{
Name: "a",
Version: "1.0",
Properties: []Property{
{Name: "tool:scratch", Value: "x"},
{Name: "cdx:type", Value: "library"},
{Name: "tool:size", Value: "100"},
},
})
s.AddPackage(Package{
Name: "b",
Version: "2.0",
Properties: []Property{
{Name: "tool:scratch", Value: "y"},
},
})

s.FilterProperties(func(name string) bool {
return !strings.HasPrefix(name, "tool:")
})

if got := len(s.Packages[0].Properties); got != 1 {
t.Errorf("package a: properties = %d, want 1", got)
}
if s.Packages[0].Properties[0].Name != "cdx:type" {
t.Errorf("package a kept the wrong property: %+v", s.Packages[0].Properties)
}
if got := len(s.Packages[1].Properties); got != 0 {
t.Errorf("package b: properties = %d, want 0", got)
}
}

func TestFilterProperties_NilPredicate(t *testing.T) {
s := New(TypeCycloneDX)
s.AddPackage(Package{Name: "a", Version: "1.0", Properties: []Property{{Name: "x"}}})
s.FilterProperties(nil)
if len(s.Packages[0].Properties) != 1 {
t.Error("nil predicate should be a no-op")
}
}

func TestFilterProperties_EmptyPackages(t *testing.T) {
s := New(TypeCycloneDX)
s.FilterProperties(func(string) bool { return true })
// No-op; just shouldn't panic.
}
26 changes: 26 additions & 0 deletions sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,32 @@ func newSBOM(t Type) *SBOM { return New(t) }
// (Name, Version) pair.
func (s *SBOM) AddPackage(p Package) { s.addPackage(p) }

// FilterProperties walks every package and removes properties whose
// names keep returns false for. Useful when handing a document to a
// downstream consumer that doesn't recognise a particular property
// namespace — for example, strip a tool-specific "mytool:" prefix
// before sharing outside the team that produced it. The filter
// mutates the SBOM in place; callers wanting a copy should Encode
// + Parse around the call.
//
// Document- and Component-level metadata is untouched; only the
// per-package Properties slice is filtered.
func (s *SBOM) FilterProperties(keep func(name string) bool) {
if keep == nil {
return
}
for i := range s.Packages {
props := s.Packages[i].Properties
filtered := props[:0]
for _, p := range props {
if keep(p.Name) {
filtered = append(filtered, p)
}
}
s.Packages[i].Properties = filtered
}
}

func (s *SBOM) addPackage(p Package) {
if s.pkgIndex == nil {
s.pkgIndex = map[[2]string]int{}
Expand Down