fix: duplicate entries in cyclonedx dependency list (#2063)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
Keith Zantow 2023-08-25 12:19:01 -04:00 committed by GitHub
parent d08e2be768
commit 4ae94c37eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 36 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/CycloneDX/cyclonedx-go" "github.com/CycloneDX/cyclonedx-go"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/exp/slices"
"github.com/anchore/syft/internal" "github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
@ -139,7 +140,7 @@ func isExpressiblePackageRelationship(ty artifact.RelationshipType) bool {
} }
func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependency { func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependency {
result := make([]cyclonedx.Dependency, 0) dependencies := map[string]*cyclonedx.Dependency{}
for _, r := range relationships { for _, r := range relationships {
exists := isExpressiblePackageRelationship(r.Type) exists := isExpressiblePackageRelationship(r.Type)
if !exists { if !exists {
@ -160,15 +161,32 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
continue continue
} }
// ind dep toRef := deriveBomRef(toPkg)
dep := dependencies[toRef]
innerDeps := []string{} if dep == nil {
innerDeps = append(innerDeps, deriveBomRef(fromPkg)) dep = &cyclonedx.Dependency{
result = append(result, cyclonedx.Dependency{ Ref: toRef,
Ref: deriveBomRef(toPkg), Dependencies: &[]string{},
Dependencies: &innerDeps,
})
} }
dependencies[toRef] = dep
}
fromRef := deriveBomRef(fromPkg)
if !slices.Contains(*dep.Dependencies, fromRef) {
*dep.Dependencies = append(*dep.Dependencies, fromRef)
}
}
result := make([]cyclonedx.Dependency, 0, len(dependencies))
for _, dep := range dependencies {
slices.Sort(*dep.Dependencies)
result = append(result, *dep)
}
slices.SortFunc(result, func(a, b cyclonedx.Dependency) bool {
return a.Ref < b.Ref
})
return result return result
} }

View File

@ -1,6 +1,7 @@
package cyclonedxhelpers package cyclonedxhelpers
import ( import (
"fmt"
"testing" "testing"
"github.com/CycloneDX/cyclonedx-go" "github.com/CycloneDX/cyclonedx-go"
@ -43,28 +44,34 @@ func Test_relationships(t *testing.T) {
p1 := pkg.Package{ p1 := pkg.Package{
Name: "p1", Name: "p1",
} }
p1.SetID()
p2 := pkg.Package{ p2 := pkg.Package{
Name: "p2", Name: "p2",
} }
p2.SetID()
p3 := pkg.Package{ p3 := pkg.Package{
Name: "p3", Name: "p3",
} }
p3.SetID()
p4 := pkg.Package{
Name: "p4",
}
for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
p.PURL = fmt.Sprintf("pkg:generic/%s@%s", p.Name, p.Name)
p.SetID()
}
tests := []struct { tests := []struct {
name string name string
sbom sbom.SBOM sbom sbom.SBOM
expected []string expected *[]cyclonedx.Dependency
}{ }{
{ {
name: "package dependencyOf relationships output as dependencies", name: "package dependencyOf relationships output as dependencies",
sbom: sbom.SBOM{ sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{ Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1, p2, p3), Packages: pkg.NewCollection(p1, p2, p3, p4),
}, },
Relationships: []artifact.Relationship{ Relationships: []artifact.Relationship{
{ {
@ -77,9 +84,28 @@ func Test_relationships(t *testing.T) {
To: p1, To: p1,
Type: artifact.DependencyOfRelationship, Type: artifact.DependencyOfRelationship,
}, },
{
From: p4,
To: p2,
Type: artifact.DependencyOfRelationship,
},
},
},
expected: &[]cyclonedx.Dependency{
{
Ref: deriveBomRef(p1),
Dependencies: &[]string{
deriveBomRef(p2),
deriveBomRef(p3),
},
},
{
Ref: deriveBomRef(p2),
Dependencies: &[]string{
deriveBomRef(p4),
},
}, },
}, },
expected: []string{p2.Name, p3.Name},
}, },
{ {
name: "package contains relationships not output", name: "package contains relationships not output",
@ -108,28 +134,7 @@ func Test_relationships(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
cdx := ToFormatModel(test.sbom) cdx := ToFormatModel(test.sbom)
got := cdx.Dependencies got := cdx.Dependencies
require.Equal(t, test.expected, got)
var deps []string
if got != nil {
for _, r := range *got {
for _, d := range *r.Dependencies {
c := findComponent(cdx, d)
require.NotNil(t, c)
deps = append(deps, c.Name)
}
}
}
require.Equal(t, test.expected, deps)
}) })
} }
} }
func findComponent(cdx *cyclonedx.BOM, bomRef string) *cyclonedx.Component {
for _, c := range *cdx.Components {
if c.BOMRef == bomRef {
return &c
}
}
return nil
}