mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 16:06:36 +01:00
fix: SPDX output performance with many relationships (#3053)
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
"github.com/anchore/syft/internal/spdxlicense"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -45,9 +46,10 @@ const (
|
||||
func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
name, namespace := helpers.DocumentNameAndNamespace(s.Source, s.Descriptor)
|
||||
|
||||
packages := toPackages(s.Artifacts.Packages, s)
|
||||
rels := relationship.NewIndex(s.Relationships...)
|
||||
packages := toPackages(rels, s.Artifacts.Packages, s)
|
||||
|
||||
relationships := toRelationships(s.RelationshipsSorted())
|
||||
allRelationships := toRelationships(rels.All())
|
||||
|
||||
// for valid SPDX we need a document describes relationship
|
||||
describesID := spdx.ElementID("DOCUMENT")
|
||||
@@ -57,7 +59,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
describesID = rootPackage.PackageSPDXIdentifier
|
||||
|
||||
// add all relationships from the document root to all other packages
|
||||
relationships = append(relationships, toRootRelationships(rootPackage, packages)...)
|
||||
allRelationships = append(allRelationships, toRootRelationships(rootPackage, packages)...)
|
||||
|
||||
// append the root package
|
||||
packages = append(packages, rootPackage)
|
||||
@@ -75,7 +77,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
}
|
||||
|
||||
// add the root document relationship
|
||||
relationships = append(relationships, documentDescribesRelationship)
|
||||
allRelationships = append(allRelationships, documentDescribesRelationship)
|
||||
|
||||
return &spdx.Document{
|
||||
// 6.1: SPDX Version; should be in the format "SPDX-x.x"
|
||||
@@ -150,7 +152,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
},
|
||||
Packages: packages,
|
||||
Files: toFiles(s),
|
||||
Relationships: relationships,
|
||||
Relationships: allRelationships,
|
||||
OtherLicenses: toOtherLicenses(s.Artifacts.Packages),
|
||||
}
|
||||
}
|
||||
@@ -302,7 +304,7 @@ func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
//
|
||||
//nolint:funlen
|
||||
func toPackages(catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package) {
|
||||
func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package) {
|
||||
for _, p := range catalog.Sorted() {
|
||||
// name should be guaranteed to be unique, but semantically useful and stable
|
||||
id := toSPDXID(p)
|
||||
@@ -318,7 +320,7 @@ func toPackages(catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Packag
|
||||
// 2. syft has generated a sha1 digest for the package's contents
|
||||
packageChecksums, filesAnalyzed := toPackageChecksums(p)
|
||||
|
||||
packageVerificationCode := newPackageVerificationCode(p, sbom)
|
||||
packageVerificationCode := newPackageVerificationCode(rels, p, sbom)
|
||||
if packageVerificationCode != nil {
|
||||
filesAnalyzed = true
|
||||
}
|
||||
@@ -744,12 +746,12 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
|
||||
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
|
||||
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
|
||||
// the above link contains the SPDX algorithm for a package verification code
|
||||
func newPackageVerificationCode(p pkg.Package, sbom sbom.SBOM) *spdx.PackageVerificationCode {
|
||||
func newPackageVerificationCode(rels *relationship.Index, p pkg.Package, sbom sbom.SBOM) *spdx.PackageVerificationCode {
|
||||
// key off of the contains relationship;
|
||||
// spdx validator will fail if a package claims to contain a file but no sha1 provided
|
||||
// if a sha1 for a file is provided then the validator will fail if the package does not have
|
||||
// a package verification code
|
||||
coordinates := sbom.CoordinatesForPackage(p, artifact.ContainsRelationship)
|
||||
coordinates := rels.Coordinates(p, artifact.ContainsRelationship)
|
||||
var digests []file.Digest
|
||||
for _, c := range coordinates {
|
||||
digest := sbom.Artifacts.FileDigests[c]
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
|
||||
@@ -665,7 +666,7 @@ func Test_H1Digest(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
catalog := pkg.NewCollection(test.pkg)
|
||||
pkgs := toPackages(catalog, s)
|
||||
pkgs := toPackages(relationship.NewIndex(), catalog, s)
|
||||
require.Len(t, pkgs, 1)
|
||||
for _, p := range pkgs {
|
||||
if test.expectedDigest == "" {
|
||||
|
||||
@@ -167,7 +167,7 @@ func wheelEggRelationships(ctx context.Context, resolver file.Resolver, pkgs []p
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to resolve relationships for global site package %q: %w", globalSitePackage, err)
|
||||
}
|
||||
relationshipIndex.AddAll(siteRels...)
|
||||
relationshipIndex.Add(siteRels...)
|
||||
}
|
||||
|
||||
// create relationships between packages within each virtual env site package directory (that doesn't link to a global site-packages directory)
|
||||
@@ -180,7 +180,7 @@ func wheelEggRelationships(ctx context.Context, resolver file.Resolver, pkgs []p
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to resolve relationships for virtualenv site package %q: %w", venv.SitePackagesPath, err)
|
||||
}
|
||||
relationshipIndex.AddAll(siteRels...)
|
||||
relationshipIndex.Add(siteRels...)
|
||||
}
|
||||
|
||||
// create relationships between packages within each virtual env site package directory (that links to a global site package directory)
|
||||
@@ -197,10 +197,10 @@ func wheelEggRelationships(ctx context.Context, resolver file.Resolver, pkgs []p
|
||||
return nil, nil, fmt.Errorf("failed to resolve relationships for virtualenv + global site package path %q + %q: %w", venv.SitePackagesPath, globalSitePackage, err)
|
||||
}
|
||||
|
||||
relationshipIndex.AddAll(siteRels...)
|
||||
relationshipIndex.Add(siteRels...)
|
||||
}
|
||||
|
||||
return pkgs, relationshipIndex.AllUniqueRelationships(), err
|
||||
return pkgs, relationshipIndex.All(), err
|
||||
}
|
||||
|
||||
func collectPackages(pkgsBySitePackageAndName map[string]map[string]pkg.Package, sites []string) []pkg.Package {
|
||||
|
||||
@@ -35,6 +35,8 @@ type Descriptor struct {
|
||||
Configuration interface{}
|
||||
}
|
||||
|
||||
// RelationshipsSorted returns a sorted slice of all relationships
|
||||
// Deprecated -- use relationship.Index
|
||||
func (s SBOM) RelationshipsSorted() []artifact.Relationship {
|
||||
relationships := s.Relationships
|
||||
sort.SliceStable(relationships, func(i, j int) bool {
|
||||
@@ -70,6 +72,7 @@ func (s SBOM) AllCoordinates() []file.Coordinates {
|
||||
|
||||
// RelationshipsForPackage returns all relationships for the provided types.
|
||||
// If no types are provided, all relationships for the package are returned.
|
||||
// Deprecated -- use relationship.Index
|
||||
func (s SBOM) RelationshipsForPackage(p pkg.Package, rt ...artifact.RelationshipType) []artifact.Relationship {
|
||||
if len(rt) == 0 {
|
||||
rt = artifact.AllRelationshipTypes()
|
||||
@@ -103,6 +106,7 @@ func (s SBOM) RelationshipsForPackage(p pkg.Package, rt ...artifact.Relationship
|
||||
|
||||
// CoordinatesForPackage returns all coordinates for the provided package for provided relationship types
|
||||
// If no types are provided, all relationship types are considered.
|
||||
// Deprecated -- use relationship.Index
|
||||
func (s SBOM) CoordinatesForPackage(p pkg.Package, rt ...artifact.RelationshipType) []file.Coordinates {
|
||||
var coordinates []file.Coordinates
|
||||
for _, relationship := range s.RelationshipsForPackage(p, rt...) {
|
||||
|
||||
Reference in New Issue
Block a user