Respect "rpmmod" PURL qualifier (#4314)

Red Hat purls the RPM modularity info in a query param in the PURLs in
their vulnerability data. It would be nice if Syft respected this
qualifier so that Grype can use it when a Red Hat purl is passed.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
This commit is contained in:
Will Murphy 2025-10-28 09:35:11 -04:00 committed by GitHub
parent bee78c0b16
commit 0d9ea69a66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 1 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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) {

View File

@ -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"
)