Skip to content

Commit

Permalink
CycloneDX SBOM: support nested components
Browse files Browse the repository at this point in the history
Signed-off-by: mrizzi <mrizzi@redhat.com>
  • Loading branch information
mrizzi committed Sep 30, 2024
1 parent 6ea218b commit 71f2c52
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 19 deletions.
13 changes: 11 additions & 2 deletions pkg/ingestor/parser/cyclonedx/parser_cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,12 @@ func parseContainerType(name string, version string, topLevel bool) string {
}

func (c *cyclonedxParser) getPackages() error {
if c.cdxBom.Components != nil {
for _, comp := range *c.cdxBom.Components {
return traverseComponents(*c, c.cdxBom.Components)
}

func traverseComponents(c cyclonedxParser, components *[]cdx.Component) error {
if components != nil {
for _, comp := range *components {
// skipping over the "operating-system" type as it does not contain
// the required purl for package node. Currently there is no use-case
// to capture OS for GUAC.
Expand Down Expand Up @@ -250,6 +254,11 @@ func (c *cyclonedxParser) getPackages() error {
if err := c.getLicenseInformation(comp); err != nil {
return fmt.Errorf("failed to get license information for component package with error: %w", err)
}

err = traverseComponents(c, comp.Components)
if err != nil {
return err
}
}
}
return nil
Expand Down
63 changes: 46 additions & 17 deletions pkg/ingestor/parser/cyclonedx/parser_cyclonedx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
tests := []struct {
name string
cdxBom *cdx.BOM
wantPurl string
wantPurl []string
}{{
name: "purl provided",
cdxBom: &cdx.BOM{
Expand All @@ -380,7 +380,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
PackageURL: "pkg:oci/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?repository_url=gcr.io/distroless/static&tag=nonroot",
}},
},
wantPurl: "pkg:oci/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?repository_url=gcr.io/distroless/static&tag=nonroot",
wantPurl: []string{"pkg:oci/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?repository_url=gcr.io/distroless/static&tag=nonroot"},
}, {
name: "gcr.io/distroless/static:nonroot - purl not provided",
cdxBom: &cdx.BOM{
Expand All @@ -390,7 +390,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388",
}},
},
wantPurl: "pkg:guac/pkg/gcr.io/distroless/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?tag=nonroot",
wantPurl: []string{"pkg:guac/pkg/gcr.io/distroless/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?tag=nonroot"},
}, {
name: "gcr.io/distroless/static - purl not provided, tag not specified",

Expand All @@ -401,7 +401,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388",
}},
},
wantPurl: "pkg:guac/pkg/gcr.io/distroless/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?tag=",
wantPurl: []string{"pkg:guac/pkg/gcr.io/distroless/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?tag="},
}, {
name: "gcr.io/distroless/static - purl not provided, tag not specified, version not specified",

Expand All @@ -411,7 +411,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Type: cdx.ComponentTypeContainer,
}},
},
wantPurl: "pkg:guac/pkg/gcr.io/distroless/static@?tag=",
wantPurl: []string{"pkg:guac/pkg/gcr.io/distroless/static@?tag="},
}, {
name: "library/debian:latest - purl not provided, assume docker.io",

Expand All @@ -422,7 +422,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/library/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=latest",
wantPurl: []string{"pkg:guac/pkg/library/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=latest"},
}, {
name: "library/debian - purl not provided, tag not specified",
cdxBom: &cdx.BOM{
Expand All @@ -432,7 +432,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/library/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=",
wantPurl: []string{"pkg:guac/pkg/library/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag="},
}, {
name: "library - purl not provided, tag not specified",
cdxBom: &cdx.BOM{
Expand All @@ -442,7 +442,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/library@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=",
wantPurl: []string{"pkg:guac/pkg/library@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag="},
}, {
name: "name split length too long, tag not specified",
cdxBom: &cdx.BOM{
Expand All @@ -452,7 +452,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/ghcr.io/guacsec/guac/guacsec@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
wantPurl: []string{"pkg:guac/pkg/ghcr.io/guacsec/guac/guacsec@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870"},
}, {
name: "name contains local registry, tag specified",
cdxBom: &cdx.BOM{
Expand All @@ -462,7 +462,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/foo.registry.com:4443/myapp/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=latest",
wantPurl: []string{"pkg:guac/pkg/foo.registry.com:4443/myapp/debian@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?tag=latest"},
}, {
name: "ComponentTypeLibrary",

Expand All @@ -473,7 +473,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/pkg/ghcr.io/guacsec/guac/guacsec@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
wantPurl: []string{"pkg:guac/pkg/ghcr.io/guacsec/guac/guacsec@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870"},
}, {
name: "file type - purl nor provided, version provided",
cdxBom: &cdx.BOM{
Expand All @@ -483,7 +483,7 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
},
wantPurl: "pkg:guac/files/sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?filename=/home/work/test/build/webserver",
wantPurl: []string{"pkg:guac/files/sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870?filename=/home/work/test/build/webserver"},
}, {
name: "file type - purl nor provided, version not provided",
cdxBom: &cdx.BOM{
Expand All @@ -492,7 +492,31 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
Type: cdx.ComponentTypeFile,
}},
},
wantPurl: "pkg:guac/files/home/work/test/build/webserver",
wantPurl: []string{"pkg:guac/files/home/work/test/build/webserver"},
}, {
name: "nested components",
cdxBom: &cdx.BOM{
Components: &[]cdx.Component{{
Name: "/home/work/test/build/webserver",
Type: cdx.ComponentTypeFile,
Components: &[]cdx.Component{{
Name: "gcr.io/distroless/static:nonroot",
Type: cdx.ComponentTypeContainer,
Version: "sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388",
PackageURL: "pkg:oci/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?repository_url=gcr.io/distroless/static&tag=nonroot",
Components: &[]cdx.Component{{
Name: "ghcr.io/guacsec/guac/guacsec",
Type: cdx.ComponentTypeLibrary,
Version: "sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
}},
}},
}},
},
wantPurl: []string{
"pkg:guac/files/home/work/test/build/webserver",
"pkg:oci/static@sha256:6ad5b696af3ca05a048bd29bf0f623040462638cb0b29c8d702cbb2805687388?repository_url=gcr.io/distroless/static&tag=nonroot",
"pkg:guac/pkg/ghcr.io/guacsec/guac/guacsec@sha256:1304f174557314a7ed9eddb4eab12fed12cb0cd9809e4c28f29af86979a3c870",
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -510,12 +534,17 @@ func Test_cyclonedxParser_getComponentPackages(t *testing.T) {
if err := c.getPackages(); err != nil {
t.Errorf("Failed to getTopLevelPackage %s", err)
}
wantPackage, err := asmhelpers.PurlToPkg(tt.wantPurl)
if err != nil {
t.Errorf("Failed to parse purl %v %v", tt.wantPurl, err)
wantPackage := make([]*model.PkgInputSpec, len(tt.wantPurl))
for i, item := range tt.wantPurl {
pkg, err := asmhelpers.PurlToPkg(item)
if err != nil {
t.Errorf("Failed to parse purl %v %v", tt.wantPurl, err)
}
wantPackage[i] = pkg
}

for _, comp := range *tt.cdxBom.Components {
if d := cmp.Diff(*wantPackage, *c.packagePackages[comp.BOMRef][0]); len(d) != 0 {
if d := cmp.Diff(wantPackage, c.packagePackages[comp.BOMRef]); len(d) != 0 {
t.Errorf("addRootPackage failed to produce expected package for %v", tt.name)
t.Errorf("spdx.GetPredicate mismatch values (+got, -expected): %s", d)
}
Expand Down

0 comments on commit 71f2c52

Please sign in to comment.