diff --git a/syft/pkg/cataloger/internal/cpegenerate/rpm.go b/syft/pkg/cataloger/internal/cpegenerate/rpm.go
index ecaea15f5..54a1e8c42 100644
--- a/syft/pkg/cataloger/internal/cpegenerate/rpm.go
+++ b/syft/pkg/cataloger/internal/cpegenerate/rpm.go
@@ -1,26 +1,49 @@
package cpegenerate
-import "github.com/anchore/syft/syft/pkg"
+import (
+ "net/url"
+ "strings"
+
+ "github.com/anchore/syft/syft/pkg"
+)
func candidateVendorsForRPM(p pkg.Package) fieldCandidateSet {
vendors := newFieldCandidateSet()
+ var vendor string
switch m := p.Metadata.(type) {
case pkg.RpmDBEntry:
- if m.Vendor != "" {
- vendors.add(fieldCandidate{
- value: normalizeName(m.Vendor),
- disallowSubSelections: true,
- })
- }
+ vendor = m.Vendor
case pkg.RpmArchive:
- if m.Vendor != "" {
- vendors.add(fieldCandidate{
- value: normalizeName(m.Vendor),
- disallowSubSelections: true,
- })
- }
+ vendor = m.Vendor
+ }
+
+ vendor = stripTrailingURL(vendor)
+ if vendor != "" {
+ vendors.add(fieldCandidate{
+ value: normalizeName(vendor),
+ disallowSubSelections: true,
+ })
}
return vendors
}
+
+func stripTrailingURL(value string) string {
+ trimmed := strings.TrimSpace(value)
+ if !strings.HasSuffix(trimmed, ">") {
+ return value
+ }
+
+ open := strings.LastIndex(trimmed, "<")
+ if open == -1 {
+ return value
+ }
+
+ parsed, err := url.Parse(trimmed[open+1 : len(trimmed)-1])
+ if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") {
+ return value
+ }
+
+ return strings.TrimSpace(trimmed[:open])
+}
diff --git a/syft/pkg/cataloger/internal/cpegenerate/rpm_test.go b/syft/pkg/cataloger/internal/cpegenerate/rpm_test.go
new file mode 100644
index 000000000..e5dc059d3
--- /dev/null
+++ b/syft/pkg/cataloger/internal/cpegenerate/rpm_test.go
@@ -0,0 +1,60 @@
+package cpegenerate
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/anchore/syft/syft/pkg"
+)
+
+func TestCandidateVendorsForRPM(t *testing.T) {
+ tests := []struct {
+ name string
+ metadata any
+ expected []string
+ }{
+ {
+ name: "database vendor with publisher URL",
+ metadata: pkg.RpmDBEntry{
+ Vendor: "SUSE LLC ",
+ },
+ expected: []string{"susellc"},
+ },
+ {
+ name: "archive vendor with publisher URL",
+ metadata: pkg.RpmArchive{
+ Vendor: "SUSE LLC ",
+ },
+ expected: []string{"susellc"},
+ },
+ {
+ name: "plain vendor",
+ metadata: pkg.RpmDBEntry{
+ Vendor: "Red Hat, Inc.",
+ },
+ expected: []string{"redhat"},
+ },
+ {
+ name: "non-URL angle-bracket suffix",
+ metadata: pkg.RpmDBEntry{
+ Vendor: "Example ",
+ },
+ expected: []string{"example"},
+ },
+ {
+ name: "non-HTTP URL",
+ metadata: pkg.RpmDBEntry{
+ Vendor: "Example ",
+ },
+ expected: []string{"example"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ p := pkg.Package{Metadata: test.metadata}
+ assert.ElementsMatch(t, test.expected, candidateVendorsForRPM(p).uniqueValues())
+ })
+ }
+}