Skip to content

Commit

Permalink
fix(sbom): attach nested packages to Application (#8144)
Browse files Browse the repository at this point in the history
Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
  • Loading branch information
DmitriyLewen and knqyf263 authored Dec 24, 2024
1 parent 9fd5cc5 commit 735335f
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 17 deletions.
172 changes: 172 additions & 0 deletions pkg/sbom/cyclonedx/testdata/happy/nested-packages-bom.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:12346a7a-5819-43bf-9411-5c146304f023",
"version": 1,
"metadata": {
"timestamp": "2024-12-20T10:57:13+00:00",
"tools": {
"components": [
{
"type": "application",
"group": "aquasecurity",
"name": "trivy",
"version": "0.38.7-764-g30c7cb137"
}
]
},
"component": {
"bom-ref": "1cb40520-a22c-481f-ad77-6bc6960430c5",
"type": "application",
"name": "/test",
"properties": [
{
"name": "aquasecurity:trivy:SchemaVersion",
"value": "2"
}
]
}
},
"components": [
{
"bom-ref": "4021d631-e242-4e69-8a93-928665810a27",
"type": "application",
"name": "foo/bar/test.elf",
"properties": [
{
"name": "aquasecurity:trivy:Class",
"value": "lang-pkgs"
},
{
"name": "aquasecurity:trivy:Type",
"value": "gobinary"
}
]
},
{
"bom-ref": "pkg:golang/github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
"type": "library",
"name": "github.com/aquasecurity/go-pep440-version",
"version": "v0.0.0-20210121094942-22b2f8951d46",
"purl": "pkg:golang/github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gobinary"
}
]
},
{
"bom-ref": "pkg:golang/github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
"type": "library",
"name": "github.com/aquasecurity/go-version",
"version": "v0.0.0-20210121072130-637058cfe492",
"purl": "pkg:golang/github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gobinary"
}
]
},
{
"bom-ref": "pkg:golang/github.com/aquasecurity/test",
"type": "library",
"name": "github.com/aquasecurity/test",
"purl": "pkg:golang/github.com/aquasecurity/test",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "github.com/aquasecurity/test"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gobinary"
}
]
},
{
"bom-ref": "pkg:golang/golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"type": "library",
"name": "golang.org/x/xerrors",
"version": "v0.0.0-20200804184101-5ec99f83aff1",
"purl": "pkg:golang/golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gobinary"
}
]
},
{
"bom-ref": "pkg:golang/stdlib@v1.15.2",
"type": "library",
"name": "stdlib",
"version": "v1.15.2",
"purl": "pkg:golang/stdlib@v1.15.2",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "stdlib@v1.15.2"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "gobinary"
}
]
}
],
"dependencies": [
{
"ref": "1cb40520-a22c-481f-ad77-6bc6960430c5",
"dependsOn": [
"4021d631-e242-4e69-8a93-928665810a27"
]
},
{
"ref": "4021d631-e242-4e69-8a93-928665810a27",
"dependsOn": [
"pkg:golang/github.com/aquasecurity/test"
]
},
{
"ref": "pkg:golang/github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
"dependsOn": []
},
{
"ref": "pkg:golang/github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
"dependsOn": []
},
{
"ref": "pkg:golang/github.com/aquasecurity/test",
"dependsOn": [
"pkg:golang/github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
"pkg:golang/github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
"pkg:golang/golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"pkg:golang/stdlib@v1.15.2"
]
},
{
"ref": "pkg:golang/golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"dependsOn": []
},
{
"ref": "pkg:golang/stdlib@v1.15.2",
"dependsOn": []
}
],
"vulnerabilities": []
}
87 changes: 87 additions & 0 deletions pkg/sbom/cyclonedx/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,93 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
},
},
},
{
name: "happy path for BOM with nested packages",
inputFile: "testdata/happy/nested-packages-bom.json",
want: types.SBOM{
Applications: []ftypes.Application{
{
Type: "gobinary",
FilePath: "foo/bar/test.elf",
Packages: ftypes.Packages{
{
ID: "github.com/aquasecurity/test",
Name: "github.com/aquasecurity/test",
Identifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "test",
},
BOMRef: "pkg:golang/github.com/aquasecurity/test",
},
DependsOn: []string{
"github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
"github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
"golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"stdlib@v1.15.2",
},
},
{
ID: "github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
Name: "github.com/aquasecurity/go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
Identifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "go-pep440-version",
Version: "v0.0.0-20210121094942-22b2f8951d46",
},
BOMRef: "pkg:golang/github.com/aquasecurity/go-pep440-version@v0.0.0-20210121094942-22b2f8951d46",
},
},
{
ID: "github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
Name: "github.com/aquasecurity/go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
Identifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "go-version",
Version: "v0.0.0-20210121072130-637058cfe492",
},
BOMRef: "pkg:golang/github.com/aquasecurity/go-version@v0.0.0-20210121072130-637058cfe492",
},
},
{
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
Name: "golang.org/x/xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
Identifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "golang.org/x",
Name: "xerrors",
Version: "v0.0.0-20200804184101-5ec99f83aff1",
},
BOMRef: "pkg:golang/golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
},
},
{
ID: "stdlib@v1.15.2",
Name: "stdlib",
Version: "v1.15.2",
Identifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Name: "stdlib",
Version: "v1.15.2",
},
BOMRef: "pkg:golang/stdlib@v1.15.2",
},
},
},
},
},
},
},
{
name: "happy path only os component",
inputFile: "testdata/happy/os-only-bom.json",
Expand Down
41 changes: 24 additions & 17 deletions pkg/sbom/io/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,15 +330,7 @@ func (m *Decoder) parseSrcVersion(ctx context.Context, pkg *ftypes.Package, ver

// addOSPkgs traverses relationships and adds OS packages
func (m *Decoder) addOSPkgs(sbom *types.SBOM) {
var pkgs []ftypes.Package
for _, rel := range m.bom.Relationships()[m.osID] {
pkg, ok := m.pkgs[rel.Dependency]
if !ok {
continue
}
pkgs = append(pkgs, *pkg)
delete(m.pkgs, rel.Dependency) // Delete the added package
}
pkgs := m.traverseDependencies(m.osID)
if len(pkgs) == 0 {
return
}
Expand All @@ -348,18 +340,33 @@ func (m *Decoder) addOSPkgs(sbom *types.SBOM) {
// addLangPkgs traverses relationships and adds language-specific packages
func (m *Decoder) addLangPkgs(sbom *types.SBOM) {
for id, app := range m.apps {
for _, rel := range m.bom.Relationships()[id] {
pkg, ok := m.pkgs[rel.Dependency]
if !ok {
continue
}
app.Packages = append(app.Packages, *pkg)
delete(m.pkgs, rel.Dependency) // Delete the added package
}
app.Packages = append(app.Packages, m.traverseDependencies(id)...)
sbom.Applications = append(sbom.Applications, *app)
}
}

// traverseDependencies recursively retrieves all packages that the specified component depends on.
// It starts from the given component ID and traverses the dependency tree, collecting all
// dependent packages. The collected packages are removed from m.pkgs to prevent duplicate
// processing. This ensures that all dependencies, including transitive ones, are properly
// captured and associated with their parent component.
func (m *Decoder) traverseDependencies(id uuid.UUID) ftypes.Packages {
var pkgs ftypes.Packages
for _, rel := range m.bom.Relationships()[id] {
pkg, ok := m.pkgs[rel.Dependency]
if !ok {
continue
}
// Add the current package
pkgs = append(pkgs, *pkg)
delete(m.pkgs, rel.Dependency) // Delete the added package

// Add the nested packages
pkgs = append(pkgs, m.traverseDependencies(rel.Dependency)...)
}
return pkgs
}

// addOrphanPkgs adds orphan packages.
// Orphan packages are packages that are not related to any components.
func (m *Decoder) addOrphanPkgs(ctx context.Context, sbom *types.SBOM) error {
Expand Down

0 comments on commit 735335f

Please sign in to comment.