diff --git a/syft/format/common/spdxhelpers/to_syft_model.go b/syft/format/common/spdxhelpers/to_syft_model.go index 2c15111af..572e978d3 100644 --- a/syft/format/common/spdxhelpers/to_syft_model.go +++ b/syft/format/common/spdxhelpers/to_syft_model.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/scylladb/go-set/strset" "github.com/spdx/tools-golang/spdx" "github.com/spdx/tools-golang/spdx/v2/common" @@ -45,7 +46,7 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) { }, } - collectSyftPackages(s, spdxIDMap, doc.Packages) + collectSyftPackages(s, spdxIDMap, doc) collectSyftFiles(s, spdxIDMap, doc) @@ -279,8 +280,12 @@ func findLinuxReleaseByPURL(doc *spdx.Document) *linux.Release { return nil } -func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, packages []*spdx.Package) { - for _, p := range packages { +func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, doc *spdx.Document) { + skipIDs := packageIDsToSkip(doc) + for _, p := range doc.Packages { + if p == nil || skipIDs.Has(string(p.PackageSPDXIdentifier)) { + continue + } syftPkg := toSyftPackage(p) spdxIDMap[string(p.PackageSPDXIdentifier)] = syftPkg s.Artifacts.Packages.Add(syftPkg) @@ -657,3 +662,15 @@ func extractCPEs(p *spdx.Package) (cpes []cpe.CPE) { } return cpes } + +// packageIDsToSkip returns a set of packageIDs that should not be imported +func packageIDsToSkip(doc *spdx.Document) *strset.Set { + skipIDs := strset.New() + for i := 0; i < len(doc.Relationships); i++ { + r := doc.Relationships[i] + if r != nil && r.Relationship == spdx.RelationshipGeneratedFrom { + skipIDs.Add(string(r.RefB.ElementRefID)) + } + } + return skipIDs +} diff --git a/syft/format/common/spdxhelpers/to_syft_model_test.go b/syft/format/common/spdxhelpers/to_syft_model_test.go index 574675902..dd09271d1 100644 --- a/syft/format/common/spdxhelpers/to_syft_model_test.go +++ b/syft/format/common/spdxhelpers/to_syft_model_test.go @@ -759,3 +759,37 @@ func Test_useSPDXIdentifierOverDerivedSyftArtifactID(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, s.Artifacts.Packages.Package("1")) } + +func Test_skipsPackagesWithGeneratedFromRelationship(t *testing.T) { + doc := &spdx.Document{ + SPDXVersion: "SPDX-2.3", + Packages: []*spdx.Package{ + { + PackageName: "package-1", + PackageSPDXIdentifier: "1", + PackageVersion: "1.0.5", + }, + { + PackageName: "package-1-src", + PackageSPDXIdentifier: "1-src", + PackageVersion: "1.0.5-src", + }, + }, + Relationships: []*spdx.Relationship{ + { + Relationship: spdx.RelationshipGeneratedFrom, + RefA: common.DocElementID{ // package 1 + ElementRefID: spdx.ElementID("1"), + }, + RefB: common.DocElementID{ // generated from package 1-src + ElementRefID: spdx.ElementID("1-src"), + }, + }, + }, + } + s, err := ToSyftModel(doc) + + assert.Nil(t, err) + assert.NotNil(t, s.Artifacts.Packages.Package("1")) + assert.Nil(t, s.Artifacts.Packages.Package("1-src")) +}