diff --git a/syft/format/internal/backfill.go b/syft/format/internal/backfill.go index f40aa9215..6e5544b95 100644 --- a/syft/format/internal/backfill.go +++ b/syft/format/internal/backfill.go @@ -29,6 +29,7 @@ func Backfill(p *pkg.Package) { var cpes []cpe.CPE epoch := "" + rpmmod := "" for _, qualifier := range purl.Qualifiers { switch qualifier.Key { @@ -44,6 +45,8 @@ func Backfill(p *pkg.Package) { } case pkg.PURLQualifierEpoch: epoch = qualifier.Value + case pkg.PURLQualifierRpmModularity: + rpmmod = qualifier.Value } } @@ -63,6 +66,10 @@ func Backfill(p *pkg.Package) { setJavaMetadataFromPurl(p, purl) } + if p.Type == pkg.RpmPkg { + setRpmMetadataFromPurl(p, rpmmod) + } + for _, c := range cpes { if slices.Contains(p.CPEs, c) { continue @@ -82,6 +89,35 @@ func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) { } } +func setRpmMetadataFromPurl(p *pkg.Package, rpmmod string) { + if p.Type != pkg.RpmPkg { + return + } + if rpmmod == "" { + return + } + + if p.Metadata == nil { + p.Metadata = pkg.RpmDBEntry{ + ModularityLabel: &rpmmod, + } + return + } + + switch m := p.Metadata.(type) { + case pkg.RpmDBEntry: + if m.ModularityLabel == nil { + m.ModularityLabel = &rpmmod + p.Metadata = m + } + case pkg.RpmArchive: + if m.ModularityLabel == nil { + m.ModularityLabel = &rpmmod + p.Metadata = m + } + } +} + func setVersionFromPurl(p *pkg.Package, purl packageurl.PackageURL, epoch string) { if p.Version == "" { p.Version = purl.Version diff --git a/syft/format/internal/backfill_test.go b/syft/format/internal/backfill_test.go index 0f88fa604..7e396e2b5 100644 --- a/syft/format/internal/backfill_test.go +++ b/syft/format/internal/backfill_test.go @@ -53,6 +53,21 @@ func Test_Backfill(t *testing.T) { Version: "1:1.12.8-26.el8", }, }, + { + name: "rpm with rpmmod", + in: pkg.Package{ + PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7&rpmmod=httpd:2.4", + }, + expected: pkg.Package{ + PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7&rpmmod=httpd:2.4", + Type: pkg.RpmPkg, + Name: "httpd", + Version: "2.4.37-51", + Metadata: pkg.RpmDBEntry{ + ModularityLabel: strRef("httpd:2.4"), + }, + }, + }, { name: "bad cpe", in: pkg.Package{ @@ -171,3 +186,7 @@ func Test_nameFromPurl(t *testing.T) { }) } } + +func strRef(s string) *string { + return &s +} diff --git a/syft/internal/fileresolver/metadata_test.go b/syft/internal/fileresolver/metadata_test.go index 936cd227c..ccb68d65f 100644 --- a/syft/internal/fileresolver/metadata_test.go +++ b/syft/internal/fileresolver/metadata_test.go @@ -4,9 +4,10 @@ import ( "os" "testing" - "github.com/anchore/stereoscope/pkg/file" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/anchore/stereoscope/pkg/file" ) func TestFileMetadataFromPath(t *testing.T) { diff --git a/syft/pkg/url.go b/syft/pkg/url.go index ef995f918..0c9335523 100644 --- a/syft/pkg/url.go +++ b/syft/pkg/url.go @@ -18,6 +18,9 @@ const ( // PURLQualifierUpstream this qualifier is not in the pURL spec, but is used by grype to perform indirect matching based on source information PURLQualifierUpstream = "upstream" + // PURLQualifierRpmModularity this qualifier is not in the pURL spec, but is used to specify RPM modularity information + PURLQualifierRpmModularity = "rpmmod" + purlCargoPkgType = "cargo" purlGradlePkgType = "gradle" )