Skip to content

Commit c75143f

Browse files
authored
fix(sbom): use group field for pom.xml and nodejs files for CycloneDX reports (aquasecurity#5922)
1 parent a3fac90 commit c75143f

File tree

6 files changed

+96
-15
lines changed

6 files changed

+96
-15
lines changed

integration/testdata/pom-cyclonedx.json.golden

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
{
4545
"bom-ref": "pkg:maven/com.example/[email protected]",
4646
"type": "library",
47-
"name": "com.example:log4shell",
47+
"group": "com.example",
48+
"name": "log4shell",
4849
"version": "1.0-SNAPSHOT",
4950
"purl": "pkg:maven/com.example/[email protected]",
5051
"properties": [
@@ -61,7 +62,8 @@
6162
{
6263
"bom-ref": "pkg:maven/com.fasterxml.jackson.core/[email protected]",
6364
"type": "library",
64-
"name": "com.fasterxml.jackson.core:jackson-databind",
65+
"group": "com.fasterxml.jackson.core",
66+
"name": "jackson-databind",
6567
"version": "2.9.1",
6668
"purl": "pkg:maven/com.fasterxml.jackson.core/[email protected]",
6769
"properties": [

pkg/sbom/cyclonedx/marshal.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
cdx "github.com/CycloneDX/cyclonedx-go"
10+
"github.com/package-url/packageurl-go"
1011
"github.com/samber/lo"
1112
"golang.org/x/xerrors"
1213

@@ -323,8 +324,11 @@ func pkgComponent(pkg Package) (*core.Component, error) {
323324
// e.g. local Go packages
324325
if pu := pkg.Identifier.PURL; pu != nil {
325326
version = pu.Version
326-
// use `group` field for GroupID and `name` for ArtifactID for jar files
327-
if pkg.Type == ftypes.Jar {
327+
// Use `group` field for GroupID and `name` for ArtifactID for java files
328+
// https://github.com/aquasecurity/trivy/issues/4675
329+
// Use `group` field for npm scopes
330+
// https://github.com/aquasecurity/trivy/issues/5908
331+
if pu.Type == packageurl.TypeMaven || pu.Type == packageurl.TypeNPM {
328332
name = pu.Name
329333
group = pu.Namespace
330334
}

pkg/sbom/cyclonedx/marshal_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,26 @@ func TestMarshaler_Marshal(t *testing.T) {
12241224
},
12251225
},
12261226
},
1227+
{
1228+
Target: "yarn.lock",
1229+
Class: types.ClassLangPkg,
1230+
Type: ftypes.Yarn,
1231+
Packages: []ftypes.Package{
1232+
{
1233+
ID: "@babel/[email protected]",
1234+
Name: "@babel/helper-string-parser",
1235+
Version: "7.23.4",
1236+
Identifier: ftypes.PkgIdentifier{
1237+
PURL: &packageurl.PackageURL{
1238+
Type: packageurl.TypeNPM,
1239+
Namespace: "@babel",
1240+
Name: "helper-string-parser",
1241+
Version: "7.23.4",
1242+
},
1243+
},
1244+
},
1245+
},
1246+
},
12271247
},
12281248
},
12291249
want: &cdx.BOM{
@@ -1270,6 +1290,21 @@ func TestMarshaler_Marshal(t *testing.T) {
12701290
},
12711291
},
12721292
},
1293+
{
1294+
BOMRef: "3ff14136-e09f-4df9-80ea-000000000004",
1295+
Type: cdx.ComponentTypeApplication,
1296+
Name: "yarn.lock",
1297+
Properties: &[]cdx.Property{
1298+
{
1299+
Name: "aquasecurity:trivy:Class",
1300+
Value: "lang-pkgs",
1301+
},
1302+
{
1303+
Name: "aquasecurity:trivy:Type",
1304+
Value: "yarn",
1305+
},
1306+
},
1307+
},
12731308
{
12741309
BOMRef: "pkg:gem/[email protected]",
12751310
Type: "library",
@@ -1301,13 +1336,32 @@ func TestMarshaler_Marshal(t *testing.T) {
13011336
},
13021337
},
13031338
},
1339+
{
1340+
BOMRef: "pkg:npm/%40babel/[email protected]",
1341+
Type: "library",
1342+
Name: "helper-string-parser",
1343+
Group: "@babel",
1344+
Version: "7.23.4",
1345+
PackageURL: "pkg:npm/%40babel/[email protected]",
1346+
Properties: &[]cdx.Property{
1347+
{
1348+
Name: "aquasecurity:trivy:PkgID",
1349+
Value: "@babel/[email protected]",
1350+
},
1351+
{
1352+
Name: "aquasecurity:trivy:PkgType",
1353+
Value: "yarn",
1354+
},
1355+
},
1356+
},
13041357
},
13051358
Vulnerabilities: &[]cdx.Vulnerability{},
13061359
Dependencies: &[]cdx.Dependency{
13071360
{
13081361
Ref: "3ff14136-e09f-4df9-80ea-000000000002",
13091362
Dependencies: &[]string{
13101363
"3ff14136-e09f-4df9-80ea-000000000003",
1364+
"3ff14136-e09f-4df9-80ea-000000000004",
13111365
"pkg:maven/org.springframework/[email protected]?file_path=spring-web-5.3.22.jar",
13121366
},
13131367
},
@@ -1317,6 +1371,12 @@ func TestMarshaler_Marshal(t *testing.T) {
13171371
"pkg:gem/[email protected]",
13181372
},
13191373
},
1374+
{
1375+
Ref: "3ff14136-e09f-4df9-80ea-000000000004",
1376+
Dependencies: &[]string{
1377+
"pkg:npm/%40babel/[email protected]",
1378+
},
1379+
},
13201380
{
13211381
Ref: "pkg:gem/[email protected]",
13221382
Dependencies: lo.ToPtr([]string{}),
@@ -1325,6 +1385,10 @@ func TestMarshaler_Marshal(t *testing.T) {
13251385
Ref: "pkg:maven/org.springframework/[email protected]?file_path=spring-web-5.3.22.jar",
13261386
Dependencies: lo.ToPtr([]string{}),
13271387
},
1388+
{
1389+
Ref: "pkg:npm/%40babel/[email protected]",
1390+
Dependencies: lo.ToPtr([]string{}),
1391+
},
13281392
},
13291393
},
13301394
},

pkg/sbom/cyclonedx/testdata/happy/bom.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,9 @@
121121
]
122122
},
123123
{
124-
"bom-ref": "pkg:npm/[email protected]?file_path=app%2Fapp%2Fpackage.json",
124+
"bom-ref": "pkg:npm/@example/[email protected]?file_path=app%2Fapp%2Fpackage.json",
125125
"type": "library",
126+
"group": "@example",
126127
"name": "bootstrap",
127128
"version": "5.0.2",
128129
"licenses": [
@@ -132,7 +133,7 @@
132133
}
133134
}
134135
],
135-
"purl": "pkg:npm/[email protected]",
136+
"purl": "pkg:npm/@example/[email protected]",
136137
"properties": [
137138
{
138139
"name": "aquasecurity:trivy:FilePath",
@@ -265,7 +266,7 @@
265266
"60e9f57b-d4a6-4f71-ad14-0893ac609182",
266267
"pkg:maven/org.codehaus.mojo/[email protected]?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
267268
"pkg:maven/com.example/[email protected]",
268-
"pkg:npm/[email protected]?file_path=app%2Fapp%2Fpackage.json",
269+
"pkg:npm/@example/[email protected]?file_path=app%2Fapp%2Fpackage.json",
269270
"100925ff-7c0a-470f-a725-8fb973b40e7b",
270271
"1a111e6b-a682-470e-8b0e-aaa49d93cd39"
271272
]

pkg/sbom/cyclonedx/unmarshal.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,11 @@ func toTrivyCdxComponent(component cdx.Component) ftypes.Component {
419419
}
420420

421421
func packageName(typ, pkgNameFromPurl string, component cdx.Component) string {
422-
if typ == packageurl.TypeMaven {
423-
// Jar uses `Group` field for `GroupID`
422+
if typ == packageurl.TypeMaven || typ == packageurl.TypeNPM {
423+
// Maven uses `Group` field for `GroupID`
424+
// Npm uses `Group` field for `Scope`
424425
if component.Group != "" {
425-
return fmt.Sprintf("%s:%s", component.Group, component.Name)
426+
return fmt.Sprintf("%s%s%s", component.Group, packageNameSeparator(typ), component.Name)
426427
} else {
427428
// use name derived from purl if `Group` doesn't exist
428429
return pkgNameFromPurl
@@ -431,6 +432,14 @@ func packageName(typ, pkgNameFromPurl string, component cdx.Component) string {
431432
return component.Name
432433
}
433434

435+
// packageNameSeparator selects separator to join `group` and `name` fields of the component
436+
func packageNameSeparator(typ string) string {
437+
if typ == packageurl.TypeMaven {
438+
return ":"
439+
}
440+
return "/"
441+
}
442+
434443
// parsePackageLicenses checks all supported license fields and returns a list of licenses.
435444
// https://cyclonedx.org/docs/1.5/json/#components_items_licenses
436445
func parsePackageLicenses(l *cdx.Licenses) []string {

pkg/sbom/cyclonedx/unmarshal_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,16 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
172172
FilePath: "",
173173
Libraries: ftypes.Packages{
174174
{
175-
Name: "bootstrap",
175+
Name: "@example/bootstrap",
176176
Version: "5.0.2",
177177
Identifier: ftypes.PkgIdentifier{
178178
PURL: &packageurl.PackageURL{
179-
Type: packageurl.TypeNPM,
180-
Name: "bootstrap",
181-
Version: "5.0.2",
179+
Type: packageurl.TypeNPM,
180+
Namespace: "@example",
181+
Name: "bootstrap",
182+
Version: "5.0.2",
182183
},
183-
BOMRef: "pkg:npm/[email protected]?file_path=app%2Fapp%2Fpackage.json",
184+
BOMRef: "pkg:npm/@example/[email protected]?file_path=app%2Fapp%2Fpackage.json",
184185
},
185186
Licenses: []string{"MIT"},
186187
Layer: ftypes.Layer{

0 commit comments

Comments
 (0)