From 076fb211ccf86b11a0a0500c0407660f98a2538e Mon Sep 17 00:00:00 2001 From: David Dashti <47575784+Dashtid@users.noreply.github.com> Date: Sat, 18 Apr 2026 02:21:21 +0200 Subject: [PATCH] fix(cyclonedx): conditionally exclude group from package name (#4791) Signed-off-by: David Dashti --- syft/format/internal/backfill.go | 4 +- .../cyclonedxutil/helpers/component.go | 5 +- .../cyclonedxutil/helpers/component_test.go | 59 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/syft/format/internal/backfill.go b/syft/format/internal/backfill.go index 230aac18a..8bb274a8b 100644 --- a/syft/format/internal/backfill.go +++ b/syft/format/internal/backfill.go @@ -151,13 +151,13 @@ var epochPrefix = regexp.MustCompile(`^\d+:`) // nameFromPurl returns the syft package name of the package from the purl. If the purl includes a namespace, // the name is prefixed as appropriate based on the PURL type func nameFromPurl(purl packageurl.PackageURL) string { - if !nameExcludesPurlNamespace(purl.Type) && purl.Namespace != "" { + if !NameExcludesPurlNamespace(purl.Type) && purl.Namespace != "" { return fmt.Sprintf("%s/%s", purl.Namespace, purl.Name) } return purl.Name } -func nameExcludesPurlNamespace(purlType string) bool { +func NameExcludesPurlNamespace(purlType string) bool { switch purlType { case packageurl.TypeAlpine, packageurl.TypeAlpm, diff --git a/syft/format/internal/cyclonedxutil/helpers/component.go b/syft/format/internal/cyclonedxutil/helpers/component.go index 588c20ee8..2e289fc84 100644 --- a/syft/format/internal/cyclonedxutil/helpers/component.go +++ b/syft/format/internal/cyclonedxutil/helpers/component.go @@ -165,6 +165,7 @@ func getPURL(c *cyclonedx.Component, ty pkg.Type) string { return purl.ToString() } +//nolint:gocognit func setPackageName(p *pkg.Package, c *cyclonedx.Component) { name := c.Name if c.Group != "" { @@ -195,7 +196,9 @@ func setPackageName(p *pkg.Package, c *cyclonedx.Component) { } } default: - name = fmt.Sprintf("%s/%s", c.Group, name) + if !internal.NameExcludesPurlNamespace(p.Type.PackageURLType()) { + name = fmt.Sprintf("%s/%s", c.Group, name) + } } } p.Name = name diff --git a/syft/format/internal/cyclonedxutil/helpers/component_test.go b/syft/format/internal/cyclonedxutil/helpers/component_test.go index 474ff47ff..341af7778 100644 --- a/syft/format/internal/cyclonedxutil/helpers/component_test.go +++ b/syft/format/internal/cyclonedxutil/helpers/component_test.go @@ -384,6 +384,65 @@ func Test_decodeComponent(t *testing.T) { } } +func Test_setPackageName(t *testing.T) { + tests := []struct { + name string + pkg pkg.Package + comp cyclonedx.Component + wantName string + }{ + { + name: "debian group excluded from name", + pkg: pkg.Package{Type: pkg.DebPkg}, + comp: cyclonedx.Component{Name: "wget", Group: "debian"}, + wantName: "wget", + }, + { + name: "rpm group excluded from name", + pkg: pkg.Package{Type: pkg.RpmPkg}, + comp: cyclonedx.Component{Name: "acl", Group: "centos"}, + wantName: "acl", + }, + { + name: "apk group excluded from name", + pkg: pkg.Package{Type: pkg.ApkPkg}, + comp: cyclonedx.Component{Name: "musl", Group: "alpine"}, + wantName: "musl", + }, + { + name: "npm group included in name", + pkg: pkg.Package{Type: pkg.NpmPkg}, + comp: cyclonedx.Component{Name: "node", Group: "@types"}, + wantName: "@types/node", + }, + { + name: "go module group included in name", + pkg: pkg.Package{Type: pkg.GoModulePkg}, + comp: cyclonedx.Component{Name: "net", Group: "golang.org/x"}, + wantName: "golang.org/x/net", + }, + { + name: "no group leaves name unchanged", + pkg: pkg.Package{Type: pkg.DebPkg}, + comp: cyclonedx.Component{Name: "wget"}, + wantName: "wget", + }, + { + name: "java group stored in metadata not name", + pkg: pkg.Package{Type: pkg.JavaPkg}, + comp: cyclonedx.Component{Name: "log4j", Group: "org.apache.logging.log4j"}, + wantName: "log4j", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := tt.pkg + setPackageName(&p, &tt.comp) + assert.Equal(t, tt.wantName, p.Name) + }) + } +} + func TestGetPURL(t *testing.T) { tests := []struct { name string