Skip to content

Commit dadd1e1

Browse files
fix(sbom): update logic for work with files in spdx format (#4513)
Co-authored-by: Teppei Fukuda <[email protected]>
1 parent 1a65821 commit dadd1e1

File tree

8 files changed

+384
-72
lines changed

8 files changed

+384
-72
lines changed

integration/integration_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ func readSpdxJson(t *testing.T, filePath string) *spdx.Document {
178178
return bom.Relationships[i].RefB.ElementRefID < bom.Relationships[j].RefB.ElementRefID
179179
})
180180

181+
sort.Slice(bom.Files, func(i, j int) bool {
182+
return bom.Files[i].FileSPDXIdentifier < bom.Files[j].FileSPDXIdentifier
183+
})
184+
181185
// We don't compare values which change each time an SBOM is generated
182186
bom.CreationInfo.Created = ""
183187
bom.DocumentNamespace = ""

integration/testdata/conda-spdx.json.golden

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
"dataLicense": "CC0-1.0",
44
"SPDXID": "SPDXRef-DOCUMENT",
55
"name": "testdata/fixtures/fs/conda",
6-
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/testdata/fixtures/fs/conda-e854267f-30a6-497d-9183-2f45dee37b09",
6+
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/testdata/fixtures/fs/conda-8864fdf2-1c56-4e86-bc35-c89a0a4c22b9",
77
"creationInfo": {
88
"licenseListVersion": "",
99
"creators": [
1010
"Organization: aquasecurity",
1111
"Tool: trivy-dev"
1212
],
13-
"created": "2023-05-19T10:38:39Z"
13+
"created": "2023-05-31T07:16:47Z"
1414
},
1515
"packages": [
1616
{
@@ -37,20 +37,7 @@
3737
"referenceLocator": "pkg:conda/[email protected]"
3838
}
3939
],
40-
"primaryPackagePurpose": "LIBRARY",
41-
"files": [
42-
{
43-
"fileName": "miniconda3/envs/testenv/conda-meta/openssl-1.1.1q-h7f8727e_0.json",
44-
"SPDXID": "SPDXRef-File-600e5e0110a84891",
45-
"checksums": [
46-
{
47-
"algorithm": "SHA1",
48-
"checksumValue": "237db0da53131e4548cb1181337fa0f420299e1f"
49-
}
50-
],
51-
"copyrightText": ""
52-
}
53-
]
40+
"primaryPackagePurpose": "LIBRARY"
5441
},
5542
{
5643
"name": "pip",
@@ -68,20 +55,7 @@
6855
"referenceLocator": "pkg:conda/[email protected]"
6956
}
7057
],
71-
"primaryPackagePurpose": "LIBRARY",
72-
"files": [
73-
{
74-
"fileName": "miniconda3/envs/testenv/conda-meta/pip-22.2.2-py38h06a4308_0.json",
75-
"SPDXID": "SPDXRef-File-7eb62e2a3edddc0a",
76-
"checksums": [
77-
{
78-
"algorithm": "SHA1",
79-
"checksumValue": "a6a2db7668f1ad541d704369fc66c96a4415aa24"
80-
}
81-
],
82-
"copyrightText": ""
83-
}
84-
]
58+
"primaryPackagePurpose": "LIBRARY"
8559
},
8660
{
8761
"name": "testdata/fixtures/fs/conda",
@@ -94,6 +68,30 @@
9468
"primaryPackagePurpose": "SOURCE"
9569
}
9670
],
71+
"files": [
72+
{
73+
"fileName": "miniconda3/envs/testenv/conda-meta/openssl-1.1.1q-h7f8727e_0.json",
74+
"SPDXID": "SPDXRef-File-600e5e0110a84891",
75+
"checksums": [
76+
{
77+
"algorithm": "SHA1",
78+
"checksumValue": "237db0da53131e4548cb1181337fa0f420299e1f"
79+
}
80+
],
81+
"copyrightText": ""
82+
},
83+
{
84+
"fileName": "miniconda3/envs/testenv/conda-meta/pip-22.2.2-py38h06a4308_0.json",
85+
"SPDXID": "SPDXRef-File-7eb62e2a3edddc0a",
86+
"checksums": [
87+
{
88+
"algorithm": "SHA1",
89+
"checksumValue": "a6a2db7668f1ad541d704369fc66c96a4415aa24"
90+
}
91+
],
92+
"copyrightText": ""
93+
}
94+
],
9795
"relationships": [
9896
{
9997
"spdxElementId": "SPDXRef-DOCUMENT",
@@ -110,10 +108,20 @@
110108
"relatedSpdxElement": "SPDXRef-Package-b1088cb4090e3a55",
111109
"relationshipType": "CONTAINS"
112110
},
111+
{
112+
"spdxElementId": "SPDXRef-Package-b1088cb4090e3a55",
113+
"relatedSpdxElement": "SPDXRef-File-600e5e0110a84891",
114+
"relationshipType": "CONTAINS"
115+
},
113116
{
114117
"spdxElementId": "SPDXRef-Application-ee5ef1aa4ac89125",
115118
"relatedSpdxElement": "SPDXRef-Package-6b677e82217fb5bd",
116119
"relationshipType": "CONTAINS"
120+
},
121+
{
122+
"spdxElementId": "SPDXRef-Package-6b677e82217fb5bd",
123+
"relatedSpdxElement": "SPDXRef-File-7eb62e2a3edddc0a",
124+
"relationshipType": "CONTAINS"
117125
}
118126
]
119127
}

pkg/sbom/spdx/marshal.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) {
138138
relationShip(DocumentSPDXIdentifier, rootPkg.PackageSPDXIdentifier, RelationShipDescribe),
139139
)
140140

141+
var spdxFiles []*spdx.File
142+
141143
for _, result := range r.Results {
142144
if len(result.Packages) == 0 {
143145
continue
@@ -160,6 +162,16 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) {
160162
relationShips = append(relationShips,
161163
relationShip(parentPackage.PackageSPDXIdentifier, spdxPackage.PackageSPDXIdentifier, RelationShipContains),
162164
)
165+
files, err := m.pkgFiles(pkg)
166+
if err != nil {
167+
return nil, xerrors.Errorf("package file error: %w", err)
168+
}
169+
spdxFiles = append(spdxFiles, files...)
170+
for _, file := range files {
171+
relationShips = append(relationShips,
172+
relationShip(spdxPackage.PackageSPDXIdentifier, file.FileSPDXIdentifier, RelationShipContains),
173+
)
174+
}
163175
}
164176
}
165177

@@ -184,6 +196,7 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) {
184196
},
185197
Packages: toPackages(packages),
186198
Relationships: relationShips,
199+
Files: spdxFiles,
187200
}, nil
188201
}
189202

@@ -337,11 +350,6 @@ func (m *Marshaler) pkgToSpdxPackage(t, pkgDownloadLocation string, class types.
337350
attrTexts = appendAttributionText(attrTexts, PropertyLayerDigest, pkg.Layer.Digest)
338351
attrTexts = appendAttributionText(attrTexts, PropertyLayerDiffID, pkg.Layer.DiffID)
339352

340-
files, err := m.pkgFiles(pkg)
341-
if err != nil {
342-
return spdx.Package{}, xerrors.Errorf("package file error: %w", err)
343-
}
344-
345353
supplier := &spdx.Supplier{Supplier: PackageSupplierNoAssertion}
346354
if pkg.Maintainer != "" {
347355
supplier = &spdx.Supplier{
@@ -373,7 +381,6 @@ func (m *Marshaler) pkgToSpdxPackage(t, pkgDownloadLocation string, class types.
373381
PrimaryPackagePurpose: PackagePurposeLibrary,
374382
PackageSupplier: supplier,
375383
PackageChecksums: checksum,
376-
Files: files,
377384
}, nil
378385
}
379386

pkg/sbom/spdx/marshal_test.go

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -401,18 +401,6 @@ func TestMarshaler_Marshal(t *testing.T) {
401401
PackageAttributionTexts: []string{
402402
"LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488",
403403
},
404-
Files: []*spdx.File{
405-
{
406-
FileSPDXIdentifier: "File-fa42187221d0d0a8",
407-
FileName: "tools/project-doe/specifications/actionpack.gemspec",
408-
Checksums: []spdx.Checksum{
409-
{
410-
Algorithm: spdx.SHA1,
411-
Value: "413f98442c83808042b5d1d2611a346b999bdca5",
412-
},
413-
},
414-
},
415-
},
416404
PrimaryPackagePurpose: tspdx.PackagePurposeLibrary,
417405
PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion},
418406
},
@@ -433,18 +421,6 @@ func TestMarshaler_Marshal(t *testing.T) {
433421
PackageAttributionTexts: []string{
434422
"LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488",
435423
},
436-
Files: []*spdx.File{
437-
{
438-
FileSPDXIdentifier: "File-6a540784b0dc6d55",
439-
FileName: "tools/project-john/specifications/actionpack.gemspec",
440-
Checksums: []spdx.Checksum{
441-
{
442-
Algorithm: spdx.SHA1,
443-
Value: "d2f9f9aed5161f6e4116a3f9573f41cd832f137c",
444-
},
445-
},
446-
},
447-
},
448424
PrimaryPackagePurpose: tspdx.PackagePurposeLibrary,
449425
PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion},
450426
},
@@ -475,6 +451,28 @@ func TestMarshaler_Marshal(t *testing.T) {
475451
PrimaryPackagePurpose: tspdx.PackagePurposeApplication,
476452
},
477453
},
454+
Files: []*spdx.File{
455+
{
456+
FileSPDXIdentifier: "File-6a540784b0dc6d55",
457+
FileName: "tools/project-john/specifications/actionpack.gemspec",
458+
Checksums: []spdx.Checksum{
459+
{
460+
Algorithm: spdx.SHA1,
461+
Value: "d2f9f9aed5161f6e4116a3f9573f41cd832f137c",
462+
},
463+
},
464+
},
465+
{
466+
FileSPDXIdentifier: "File-fa42187221d0d0a8",
467+
FileName: "tools/project-doe/specifications/actionpack.gemspec",
468+
Checksums: []spdx.Checksum{
469+
{
470+
Algorithm: spdx.SHA1,
471+
Value: "413f98442c83808042b5d1d2611a346b999bdca5",
472+
},
473+
},
474+
},
475+
},
478476
Relationships: []*spdx.Relationship{
479477
{
480478
RefA: spdx.DocElementID{ElementRefID: "DOCUMENT"},
@@ -501,11 +499,21 @@ func TestMarshaler_Marshal(t *testing.T) {
501499
RefB: spdx.DocElementID{ElementRefID: "Package-d5443dbcbba0dbd4"},
502500
Relationship: "CONTAINS",
503501
},
502+
{
503+
RefA: spdx.DocElementID{ElementRefID: "Package-d5443dbcbba0dbd4"},
504+
RefB: spdx.DocElementID{ElementRefID: "File-6a540784b0dc6d55"},
505+
Relationship: "CONTAINS",
506+
},
504507
{
505508
RefA: spdx.DocElementID{ElementRefID: "Application-441a648f2aeeee72"},
506509
RefB: spdx.DocElementID{ElementRefID: "Package-13fe667a0805e6b7"},
507510
Relationship: "CONTAINS",
508511
},
512+
{
513+
RefA: spdx.DocElementID{ElementRefID: "Package-13fe667a0805e6b7"},
514+
RefB: spdx.DocElementID{ElementRefID: "File-fa42187221d0d0a8"},
515+
Relationship: "CONTAINS",
516+
},
509517
},
510518

511519
OtherLicenses: nil,
@@ -684,16 +692,16 @@ func TestMarshaler_Marshal(t *testing.T) {
684692
PackageAttributionTexts: []string{
685693
"LayerDiffID: sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e",
686694
},
687-
Files: []*spdx.File{
688-
{
689-
FileName: "usr/local/lib/ruby/gems/3.1.0/gems/typeprof-0.21.1/vscode/package.json",
690-
FileSPDXIdentifier: "File-a52825a3e5bc6dfe",
691-
},
692-
},
693695
PrimaryPackagePurpose: tspdx.PackagePurposeLibrary,
694696
PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion},
695697
},
696698
},
699+
Files: []*spdx.File{
700+
{
701+
FileName: "usr/local/lib/ruby/gems/3.1.0/gems/typeprof-0.21.1/vscode/package.json",
702+
FileSPDXIdentifier: "File-a52825a3e5bc6dfe",
703+
},
704+
},
697705
Relationships: []*spdx.Relationship{
698706
{
699707
RefA: spdx.DocElementID{ElementRefID: "DOCUMENT"},
@@ -710,6 +718,11 @@ func TestMarshaler_Marshal(t *testing.T) {
710718
RefB: spdx.DocElementID{ElementRefID: "Package-daedb173cfd43058"},
711719
Relationship: "CONTAINS",
712720
},
721+
{
722+
RefA: spdx.DocElementID{ElementRefID: "Package-daedb173cfd43058"},
723+
RefB: spdx.DocElementID{ElementRefID: "File-a52825a3e5bc6dfe"},
724+
Relationship: "CONTAINS",
725+
},
713726
},
714727
},
715728
},
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
{
2+
"spdxVersion": "SPDX-2.3",
3+
"dataLicense": "CC0-1.0",
4+
"SPDXID": "SPDXRef-DOCUMENT",
5+
"name": "app",
6+
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/app-8e571278-2221-4dcd-bc56-0b256210fa91",
7+
"creationInfo": {
8+
"licenseListVersion": "",
9+
"creators": [
10+
"Organization: aquasecurity",
11+
"Tool: trivy-dev"
12+
],
13+
"created": "2023-05-31T05:58:45Z"
14+
},
15+
"packages": [
16+
{
17+
"name": "app",
18+
"SPDXID": "SPDXRef-Filesystem-13b142ca391a006e",
19+
"downloadLocation": "NONE",
20+
"copyrightText": "",
21+
"attributionTexts": [
22+
"SchemaVersion: 2"
23+
],
24+
"primaryPackagePurpose": "SOURCE"
25+
},
26+
{
27+
"name": "node-pkg",
28+
"SPDXID": "SPDXRef-Application-24f8a80152e2c0fc",
29+
"downloadLocation": "NONE",
30+
"sourceInfo": "Node.js",
31+
"copyrightText": "",
32+
"primaryPackagePurpose": "APPLICATION"
33+
},
34+
{
35+
"name": "yargs-parser",
36+
"SPDXID": "SPDXRef-Package-c3508825bf3861d8",
37+
"versionInfo": "21.1.1",
38+
"supplier": "NOASSERTION",
39+
"downloadLocation": "NONE",
40+
"licenseConcluded": "ISC",
41+
"licenseDeclared": "ISC",
42+
"copyrightText": "",
43+
"externalRefs": [
44+
{
45+
"referenceCategory": "PACKAGE-MANAGER",
46+
"referenceType": "purl",
47+
"referenceLocator": "pkg:npm/[email protected]"
48+
}
49+
],
50+
"attributionTexts": [
51+
52+
],
53+
"primaryPackagePurpose": "LIBRARY"
54+
}
55+
],
56+
"files": [
57+
{
58+
"fileName": "node_modules/yargs-parser/package.json",
59+
"SPDXID": "SPDXRef-File-51bb5f929ef68877",
60+
"checksums": [
61+
{
62+
"algorithm": "SHA1",
63+
"checksumValue": "69e70ec702f9df4ff64024b5fdea4644f1ce6c97"
64+
}
65+
],
66+
"copyrightText": ""
67+
}
68+
],
69+
"relationships": [
70+
{
71+
"spdxElementId": "SPDXRef-DOCUMENT",
72+
"relatedSpdxElement": "SPDXRef-Filesystem-13b142ca391a006e",
73+
"relationshipType": "DESCRIBES"
74+
},
75+
{
76+
"spdxElementId": "SPDXRef-Filesystem-13b142ca391a006e",
77+
"relatedSpdxElement": "SPDXRef-Application-24f8a80152e2c0fc",
78+
"relationshipType": "CONTAINS"
79+
},
80+
{
81+
"spdxElementId": "SPDXRef-Application-24f8a80152e2c0fc",
82+
"relatedSpdxElement": "SPDXRef-Package-c3508825bf3861d8",
83+
"relationshipType": "CONTAINS"
84+
},
85+
{
86+
"spdxElementId": "SPDXRef-Package-c3508825bf3861d8",
87+
"relatedSpdxElement": "SPDXRef-File-51bb5f929ef68877",
88+
"relationshipType": "CONTAINS"
89+
}
90+
]
91+
}

0 commit comments

Comments
 (0)