@@ -29,6 +29,7 @@ const (
29
29
DocumentNamespace = "http://aquasecurity.github.io/trivy"
30
30
CreatorOrganization = "aquasecurity"
31
31
CreatorTool = "trivy"
32
+ noneField = "NONE"
32
33
)
33
34
34
35
const (
@@ -114,9 +115,10 @@ func NewMarshaler(version string, opts ...marshalOption) *Marshaler {
114
115
func (m * Marshaler ) Marshal (r types.Report ) (* spdx.Document2_2 , error ) {
115
116
var relationShips []* spdx.Relationship2_2
116
117
packages := make (map [spdx.ElementID ]* spdx.Package2_2 )
118
+ pkgDownloadLocation := getPackageDownloadLocation (r .ArtifactType , r .ArtifactName )
117
119
118
120
// Root package contains OS, OS packages, language-specific packages and so on.
119
- rootPkg , err := m .rootPackage (r )
121
+ rootPkg , err := m .rootPackage (r , pkgDownloadLocation )
120
122
if err != nil {
121
123
return nil , xerrors .Errorf ("failed to generate a root package: %w" , err )
122
124
}
@@ -126,7 +128,7 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document2_2, error) {
126
128
)
127
129
128
130
for _ , result := range r .Results {
129
- parentPackage , err := m .resultToSpdxPackage (result , r .Metadata .OS )
131
+ parentPackage , err := m .resultToSpdxPackage (result , r .Metadata .OS , pkgDownloadLocation )
130
132
if err != nil {
131
133
return nil , xerrors .Errorf ("failed to parse result: %w" , err )
132
134
}
@@ -136,7 +138,7 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document2_2, error) {
136
138
)
137
139
138
140
for _ , pkg := range result .Packages {
139
- spdxPackage , err := m .pkgToSpdxPackage (result .Type , result .Class , r .Metadata , pkg )
141
+ spdxPackage , err := m .pkgToSpdxPackage (result .Type , pkgDownloadLocation , result .Class , r .Metadata , pkg )
140
142
if err != nil {
141
143
return nil , xerrors .Errorf ("failed to parse package: %w" , err )
142
144
}
@@ -163,16 +165,16 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document2_2, error) {
163
165
}, nil
164
166
}
165
167
166
- func (m * Marshaler ) resultToSpdxPackage (result types.Result , os * ftypes.OS ) (spdx.Package2_2 , error ) {
168
+ func (m * Marshaler ) resultToSpdxPackage (result types.Result , os * ftypes.OS , pkgDownloadLocation string ) (spdx.Package2_2 , error ) {
167
169
switch result .Class {
168
170
case types .ClassOSPkg :
169
- osPkg , err := m .osPackage (os )
171
+ osPkg , err := m .osPackage (os , pkgDownloadLocation )
170
172
if err != nil {
171
173
return spdx.Package2_2 {}, xerrors .Errorf ("failed to parse operating system package: %w" , err )
172
174
}
173
175
return osPkg , nil
174
176
case types .ClassLangPkg :
175
- langPkg , err := m .langPackage (result .Target , result .Type )
177
+ langPkg , err := m .langPackage (result .Target , result .Type , pkgDownloadLocation )
176
178
if err != nil {
177
179
return spdx.Package2_2 {}, xerrors .Errorf ("failed to parse application package: %w" , err )
178
180
}
@@ -195,7 +197,7 @@ func (m *Marshaler) parseFile(filePath string) (spdx.File2_2, error) {
195
197
return file , nil
196
198
}
197
199
198
- func (m * Marshaler ) rootPackage (r types.Report ) (* spdx.Package2_2 , error ) {
200
+ func (m * Marshaler ) rootPackage (r types.Report , pkgDownloadLocation string ) (* spdx.Package2_2 , error ) {
199
201
var externalReferences []* spdx.PackageExternalReference2_2
200
202
attributionTexts := []string {attributionText (PropertySchemaVersion , strconv .Itoa (r .SchemaVersion ))}
201
203
@@ -231,12 +233,13 @@ func (m *Marshaler) rootPackage(r types.Report) (*spdx.Package2_2, error) {
231
233
return & spdx.Package2_2 {
232
234
PackageName : r .ArtifactName ,
233
235
PackageSPDXIdentifier : elementID (camelCase (string (r .ArtifactType )), pkgID ),
236
+ PackageDownloadLocation : pkgDownloadLocation ,
234
237
PackageAttributionTexts : attributionTexts ,
235
238
PackageExternalReferences : externalReferences ,
236
239
}, nil
237
240
}
238
241
239
- func (m * Marshaler ) osPackage (osFound * ftypes.OS ) (spdx.Package2_2 , error ) {
242
+ func (m * Marshaler ) osPackage (osFound * ftypes.OS , pkgDownloadLocation string ) (spdx.Package2_2 , error ) {
240
243
if osFound == nil {
241
244
return spdx.Package2_2 {}, nil
242
245
}
@@ -247,26 +250,28 @@ func (m *Marshaler) osPackage(osFound *ftypes.OS) (spdx.Package2_2, error) {
247
250
}
248
251
249
252
return spdx.Package2_2 {
250
- PackageName : osFound .Family ,
251
- PackageVersion : osFound .Name ,
252
- PackageSPDXIdentifier : elementID (ElementOperatingSystem , pkgID ),
253
+ PackageName : osFound .Family ,
254
+ PackageVersion : osFound .Name ,
255
+ PackageSPDXIdentifier : elementID (ElementOperatingSystem , pkgID ),
256
+ PackageDownloadLocation : pkgDownloadLocation ,
253
257
}, nil
254
258
}
255
259
256
- func (m * Marshaler ) langPackage (target , appType string ) (spdx.Package2_2 , error ) {
260
+ func (m * Marshaler ) langPackage (target , appType , pkgDownloadLocation string ) (spdx.Package2_2 , error ) {
257
261
pkgID , err := calcPkgID (m .hasher , fmt .Sprintf ("%s-%s" , target , appType ))
258
262
if err != nil {
259
263
return spdx.Package2_2 {}, xerrors .Errorf ("failed to get %s package ID: %w" , target , err )
260
264
}
261
265
262
266
return spdx.Package2_2 {
263
- PackageName : appType ,
264
- PackageSourceInfo : target , // TODO: Files seems better
265
- PackageSPDXIdentifier : elementID (ElementApplication , pkgID ),
267
+ PackageName : appType ,
268
+ PackageSourceInfo : target , // TODO: Files seems better
269
+ PackageSPDXIdentifier : elementID (ElementApplication , pkgID ),
270
+ PackageDownloadLocation : pkgDownloadLocation ,
266
271
}, nil
267
272
}
268
273
269
- func (m * Marshaler ) pkgToSpdxPackage (t string , class types.ResultClass , metadata types.Metadata , pkg ftypes.Package ) (spdx.Package2_2 , error ) {
274
+ func (m * Marshaler ) pkgToSpdxPackage (t , pkgDownloadLocation string , class types.ResultClass , metadata types.Metadata , pkg ftypes.Package ) (spdx.Package2_2 , error ) {
270
275
license := GetLicense (pkg )
271
276
272
277
pkgID , err := calcPkgID (m .hasher , pkg )
@@ -296,10 +301,11 @@ func (m *Marshaler) pkgToSpdxPackage(t string, class types.ResultClass, metadata
296
301
}
297
302
298
303
return spdx.Package2_2 {
299
- PackageName : pkg .Name ,
300
- PackageVersion : pkg .Version ,
301
- PackageSPDXIdentifier : elementID (ElementPackage , pkgID ),
302
- PackageSourceInfo : pkgSrcInfo ,
304
+ PackageName : pkg .Name ,
305
+ PackageVersion : pkg .Version ,
306
+ PackageSPDXIdentifier : elementID (ElementPackage , pkgID ),
307
+ PackageDownloadLocation : pkgDownloadLocation ,
308
+ PackageSourceInfo : pkgSrcInfo ,
303
309
304
310
// The Declared License is what the authors of a project believe govern the package
305
311
PackageLicenseConcluded : license ,
@@ -361,7 +367,7 @@ func purlExternalReference(packageURL string) *spdx.PackageExternalReference2_2
361
367
362
368
func GetLicense (p ftypes.Package ) string {
363
369
if len (p .Licenses ) == 0 {
364
- return "NONE"
370
+ return noneField
365
371
}
366
372
367
373
license := strings .Join (lo .Map (p .Licenses , func (license string , index int ) string {
@@ -384,7 +390,7 @@ func getDocumentNamespace(r types.Report, m *Marshaler) string {
384
390
return fmt .Sprintf ("%s/%s/%s-%s" ,
385
391
DocumentNamespace ,
386
392
string (r .ArtifactType ),
387
- r .ArtifactName ,
393
+ strings . ReplaceAll ( strings . ReplaceAll ( r .ArtifactName , "https://" , "" ), "http://" , "" ), // remove http(s):// prefix when scanning repos
388
394
m .newUUID ().String (),
389
395
)
390
396
}
@@ -421,3 +427,16 @@ func camelCase(inputUnderScoreStr string) (camelCase string) {
421
427
}
422
428
return
423
429
}
430
+
431
+ func getPackageDownloadLocation (t ftypes.ArtifactType , artifactName string ) string {
432
+ location := noneField
433
+ // this field is used for git/mercurial/subversion/bazaar:
434
+ // https://spdx.github.io/spdx-spec/v2.2.2/package-information/#77-package-download-location-field
435
+ if t == ftypes .ArtifactRemoteRepository {
436
+ // Trivy currently only supports git repositories. Format examples:
437
+ // git+https://git.myproject.org/MyProject.git
438
+ // git+http://git.myproject.org/MyProject
439
+ location = fmt .Sprintf ("git+%s" , artifactName )
440
+ }
441
+ return location
442
+ }
0 commit comments