From 295454945ca6b725d73b44da9b1dc263a91b878b Mon Sep 17 00:00:00 2001 From: Eljees <57435526+Eljees@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:18:27 +0300 Subject: [PATCH] fix: strip publisher URL from RPM CPE vendor (#5081) Signed-off-by: Eljees --- .../pkg/cataloger/internal/cpegenerate/rpm.go | 49 +++++++++++---- .../internal/cpegenerate/rpm_test.go | 60 +++++++++++++++++++ 2 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 syft/pkg/cataloger/internal/cpegenerate/rpm_test.go 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()) + }) + } +}