Skip to content

Commit

Permalink
fix(sbom): fix infinite loop for cyclonedx (#3998)
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitriyLewen committed Apr 9, 2023
1 parent 6c8b042 commit c8283ce
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 4 deletions.
173 changes: 173 additions & 0 deletions pkg/sbom/cyclonedx/testdata/happy/infinite-loop-bom.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"serialNumber": "urn:uuid:a085f5e7-f5c1-4bc0-96be-ffa4d235ebc8",
"version": 1,
"metadata": {
"timestamp": "2023-04-06T05:41:44+00:00",
"tools": [
{
"vendor": "aquasecurity",
"name": "trivy",
"version": "dev"
}
],
"component": {
"bom-ref": "pkg:oci/ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21?repository_url=index.docker.io%2Flibrary%2Fubuntu\u0026arch=amd64",
"type": "container",
"name": "ubuntu",
"purl": "pkg:oci/ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21?repository_url=index.docker.io%2Flibrary%2Fubuntu\u0026arch=amd64",
"properties": [
{
"name": "aquasecurity:trivy:SchemaVersion",
"value": "2"
},
{
"name": "aquasecurity:trivy:ImageID",
"value": "sha256:08d22c0ceb150ddeb2237c5fa3129c0183f3cc6f5eeb2e7aa4016da3ad02140a"
},
{
"name": "aquasecurity:trivy:RepoDigest",
"value": "ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21"
},
{
"name": "aquasecurity:trivy:DiffID",
"value": "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466"
},
{
"name": "aquasecurity:trivy:RepoTag",
"value": "ubuntu:latest"
}
]
}
},
"components": [
{
"bom-ref": "pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
"type": "library",
"name": "libc6",
"version": "2.35-0ubuntu3.1",
"licenses": [
{
"expression": "LGPL-2.1"
},
{
"expression": "GPL-2.0"
},
{
"expression": "GFDL-1.3"
}
],
"purl": "pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "libc6@2.35-0ubuntu3.1"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "ubuntu"
},
{
"name": "aquasecurity:trivy:SrcName",
"value": "glibc"
},
{
"name": "aquasecurity:trivy:SrcVersion",
"value": "2.35"
},
{
"name": "aquasecurity:trivy:SrcRelease",
"value": "0ubuntu3.1"
},
{
"name": "aquasecurity:trivy:LayerDigest",
"value": "sha256:74ac377868f863e123f24c409f79709f7563fa464557c36a09cf6f85c8b92b7f"
},
{
"name": "aquasecurity:trivy:LayerDiffID",
"value": "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466"
}
]
},
{
"bom-ref": "pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1\u0026distro=ubuntu-22.04",
"type": "library",
"name": "libcrypt1",
"version": "4.4.27-1",
"purl": "pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1\u0026distro=ubuntu-22.04",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "libcrypt1@1:4.4.27-1"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "ubuntu"
},
{
"name": "aquasecurity:trivy:SrcName",
"value": "libxcrypt"
},
{
"name": "aquasecurity:trivy:SrcVersion",
"value": "4.4.27"
},
{
"name": "aquasecurity:trivy:SrcRelease",
"value": "1"
},
{
"name": "aquasecurity:trivy:SrcEpoch",
"value": "1"
},
{
"name": "aquasecurity:trivy:LayerDigest",
"value": "sha256:74ac377868f863e123f24c409f79709f7563fa464557c36a09cf6f85c8b92b7f"
},
{
"name": "aquasecurity:trivy:LayerDiffID",
"value": "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466"
}
]
},
{
"bom-ref": "71577c3e-dbc6-41be-ba9e-c5268bc2e5d4",
"type": "operating-system",
"name": "ubuntu",
"version": "22.04",
"properties": [
{
"name": "aquasecurity:trivy:Type",
"value": "ubuntu"
},
{
"name": "aquasecurity:trivy:Class",
"value": "os-pkgs"
}
]
}
],
"dependencies": [
{
"ref": "71577c3e-dbc6-41be-ba9e-c5268bc2e5d4",
"dependsOn": [
"pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
"pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1\u0026distro=ubuntu-22.04"
]
},
{
"ref": "pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
"dependsOn": [
"pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1\u0026distro=ubuntu-22.04"
]
},
{
"ref": "pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1\u0026distro=ubuntu-22.04",
"dependsOn": [
"pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04"
]
}
],
"vulnerabilities": []
}
20 changes: 16 additions & 4 deletions pkg/sbom/cyclonedx/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func (c *CycloneDX) parseSBOM(bom *cdx.BOM) error {
}

func (c *CycloneDX) parseOSPkgs(component cdx.Component, seen map[string]struct{}) (ftypes.PackageInfo, error) {
components := c.walkDependencies(component.BOMRef)
components := c.walkDependencies(component.BOMRef, map[string]struct{}{})
pkgs, err := parsePkgs(components, seen)
if err != nil {
return ftypes.PackageInfo{}, xerrors.Errorf("failed to parse os package: %w", err)
Expand All @@ -164,7 +164,7 @@ func (c *CycloneDX) parseOSPkgs(component cdx.Component, seen map[string]struct{
}

func (c *CycloneDX) parseLangPkgs(component cdx.Component, seen map[string]struct{}) (*ftypes.Application, error) {
components := c.walkDependencies(component.BOMRef)
components := c.walkDependencies(component.BOMRef, map[string]struct{}{})
components = lo.UniqBy(components, func(c cdx.Component) string {
return c.BOMRef
})
Expand Down Expand Up @@ -196,7 +196,7 @@ func parsePkgs(components []cdx.Component, seen map[string]struct{}) ([]ftypes.P
}

// walkDependencies takes all nested dependencies of the root component.
func (c *CycloneDX) walkDependencies(rootRef string) []cdx.Component {
func (c *CycloneDX) walkDependencies(rootRef string, uniqComponents map[string]struct{}) []cdx.Component {
// e.g. Library A, B, C, D and E will be returned as dependencies of Application 1.
// type: Application 1
// - type: Library A
Expand All @@ -213,12 +213,24 @@ func (c *CycloneDX) walkDependencies(rootRef string) []cdx.Component {
continue
}

// there are cases of looped components:
// type: Application 1
// - type: Library A
// - type: Library B
// - type: Library A
// ...
// use uniqComponents to fix infinite loop
if _, ok = uniqComponents[dep]; ok {
continue
}
uniqComponents[dep] = struct{}{}

// Take only 'Libraries'
if component.Type == cdx.ComponentTypeLibrary {
components = append(components, component)
}

components = append(components, c.walkDependencies(dep)...)
components = append(components, c.walkDependencies(dep, uniqComponents)...)
}
return components
}
Expand Down
43 changes: 43 additions & 0 deletions pkg/sbom/cyclonedx/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,49 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
},
},
},
{
name: "happy path with infinity loop",
inputFile: "testdata/happy/infinite-loop-bom.json",
want: types.SBOM{
OS: ftypes.OS{
Family: "ubuntu",
Name: "22.04",
},
Packages: []ftypes.PackageInfo{
{
Packages: []ftypes.Package{
{
ID: "libc6@2.35-0ubuntu3.1",
Name: "libc6",
Version: "2.35-0ubuntu3.1",
SrcName: "glibc",
SrcVersion: "2.35",
SrcRelease: "0ubuntu3.1",
Licenses: []string{"LGPL-2.1", "GPL-2.0", "GFDL-1.3"},
Ref: "pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
Layer: ftypes.Layer{
DiffID: "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466",
},
},
{
ID: "libcrypt1@1:4.4.27-1",
Name: "libcrypt1",
Version: "4.4.27-1",
Epoch: 1,
SrcName: "libxcrypt",
SrcVersion: "4.4.27",
SrcRelease: "1",
SrcEpoch: 1,
Ref: "pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1&distro=ubuntu-22.04",
Layer: ftypes.Layer{
DiffID: "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466",
},
},
},
},
},
},
},
{
name: "happy path for third party sbom",
inputFile: "testdata/happy/third-party-bom.json",
Expand Down

0 comments on commit c8283ce

Please sign in to comment.