Skip to content

Commit aff03eb

Browse files
feat(cyclonedx): preserve SBOM structure when scanning SBOM files with vulnerability updates (aquasecurity#9439)
Signed-off-by: knqyf263 <[email protected]> Co-authored-by: DmitriyLewen <[email protected]>
1 parent 8b2575b commit aff03eb

File tree

14 files changed

+450
-169
lines changed

14 files changed

+450
-169
lines changed

integration/testdata/conda-spdx.json.golden

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"dataLicense": "CC0-1.0",
44
"SPDXID": "SPDXRef-DOCUMENT",
55
"name": "testdata/fixtures/repo/conda",
6-
"documentNamespace": "http://trivy.dev/filesystem/testdata/fixtures/repo/conda-3ff14136-e09f-4df9-80ea-000000000004",
6+
"documentNamespace": "http://trivy.dev/filesystem/testdata/fixtures/repo/conda-3ff14136-e09f-4df9-80ea-000000000005",
77
"creationInfo": {
88
"creators": [
99
"Organization: aquasecurity",
@@ -14,7 +14,7 @@
1414
"packages": [
1515
{
1616
"name": "openssl",
17-
"SPDXID": "SPDXRef-Package-22a178da112ac20a",
17+
"SPDXID": "SPDXRef-Package-cb268df467bc826c",
1818
"versionInfo": "1.1.1q",
1919
"supplier": "NOASSERTION",
2020
"downloadLocation": "NONE",
@@ -43,7 +43,7 @@
4343
},
4444
{
4545
"name": "pip",
46-
"SPDXID": "SPDXRef-Package-c22b9ee9a601ba6",
46+
"SPDXID": "SPDXRef-Package-1378bb10fcebba63",
4747
"versionInfo": "22.2.2",
4848
"supplier": "NOASSERTION",
4949
"downloadLocation": "NONE",
@@ -118,22 +118,22 @@
118118
},
119119
{
120120
"spdxElementId": "SPDXRef-Filesystem-2e2426fd0f2580ef",
121-
"relatedSpdxElement": "SPDXRef-Package-22a178da112ac20a",
121+
"relatedSpdxElement": "SPDXRef-Package-1378bb10fcebba63",
122122
"relationshipType": "CONTAINS"
123123
},
124124
{
125125
"spdxElementId": "SPDXRef-Filesystem-2e2426fd0f2580ef",
126-
"relatedSpdxElement": "SPDXRef-Package-c22b9ee9a601ba6",
126+
"relatedSpdxElement": "SPDXRef-Package-cb268df467bc826c",
127127
"relationshipType": "CONTAINS"
128128
},
129129
{
130-
"spdxElementId": "SPDXRef-Package-22a178da112ac20a",
131-
"relatedSpdxElement": "SPDXRef-File-600e5e0110a84891",
130+
"spdxElementId": "SPDXRef-Package-1378bb10fcebba63",
131+
"relatedSpdxElement": "SPDXRef-File-7eb62e2a3edddc0a",
132132
"relationshipType": "CONTAINS"
133133
},
134134
{
135-
"spdxElementId": "SPDXRef-Package-c22b9ee9a601ba6",
136-
"relatedSpdxElement": "SPDXRef-File-7eb62e2a3edddc0a",
135+
"spdxElementId": "SPDXRef-Package-cb268df467bc826c",
136+
"relatedSpdxElement": "SPDXRef-File-600e5e0110a84891",
137137
"relationshipType": "CONTAINS"
138138
}
139139
]

integration/testdata/fluentd-multiple-lockfiles-short.cdx.json.golden

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
33
"bomFormat": "CycloneDX",
44
"specVersion": "1.6",
5-
"serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000010",
5+
"serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000006",
66
"version": 1,
77
"metadata": {
88
"timestamp": "2021-08-25T12:20:30+00:00",
@@ -91,14 +91,6 @@
9191
"name": "aquasecurity:trivy:LayerDigest",
9292
"value": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c"
9393
},
94-
{
95-
"name": "aquasecurity:trivy:PkgID",
96-
"value": "[email protected]"
97-
},
98-
{
99-
"name": "aquasecurity:trivy:PkgType",
100-
"value": "debian"
101-
},
10294
{
10395
"name": "aquasecurity:trivy:SrcName",
10496
"value": "bash"
@@ -124,14 +116,6 @@
124116
"name": "aquasecurity:trivy:LayerDigest",
125117
"value": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c"
126118
},
127-
{
128-
"name": "aquasecurity:trivy:PkgID",
129-
"value": "[email protected]"
130-
},
131-
{
132-
"name": "aquasecurity:trivy:PkgType",
133-
"value": "debian"
134-
},
135119
{
136120
"name": "aquasecurity:trivy:SrcName",
137121
"value": "libidn2"
@@ -169,11 +153,7 @@
169153
"value": "sha256:a8877cad19f14a7044524a145ce33170085441a7922458017db1631dcd5f7602"
170154
},
171155
{
172-
"name": "aquasecurity:trivy:PkgID",
173-
"value": "[email protected]"
174-
},
175-
{
176-
"name": "aquasecurity:trivy:PkgType",
156+
"name": "aquasecurity:trivy:Type",
177157
"value": "gemspec"
178158
}
179159
]
@@ -193,18 +173,6 @@
193173
"353f2470-9c8b-4647-9d0d-96d893838dc8",
194174
"pkg:gem/[email protected]?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Factivesupport-6.0.2.1.gemspec"
195175
]
196-
},
197-
{
198-
"ref": "pkg:deb/debian/[email protected]?distro=debian-10.2",
199-
"dependsOn": []
200-
},
201-
{
202-
"ref": "pkg:deb/debian/[email protected]?distro=debian-10.2",
203-
"dependsOn": []
204-
},
205-
{
206-
"ref": "pkg:gem/[email protected]?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Factivesupport-6.0.2.1.gemspec",
207-
"dependsOn": []
208176
}
209177
],
210178
"vulnerabilities": [

integration/testdata/julia-spdx.json.golden

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"packages": [
1515
{
1616
"name": "Manifest.toml",
17-
"SPDXID": "SPDXRef-Application-18fc3597717a3e56",
17+
"SPDXID": "SPDXRef-Application-c39d15beb6bdf085",
1818
"downloadLocation": "NONE",
1919
"filesAnalyzed": false,
2020
"primaryPackagePurpose": "APPLICATION",
@@ -35,7 +35,7 @@
3535
},
3636
{
3737
"name": "A",
38-
"SPDXID": "SPDXRef-Package-761ce79b41d8f121",
38+
"SPDXID": "SPDXRef-Package-3aea0b160c3af98d",
3939
"versionInfo": "1.9.0",
4040
"supplier": "NOASSERTION",
4141
"downloadLocation": "NONE",
@@ -68,7 +68,7 @@
6868
},
6969
{
7070
"name": "B",
71-
"SPDXID": "SPDXRef-Package-28f04edc422602a",
71+
"SPDXID": "SPDXRef-Package-2264d5c424c073e7",
7272
"versionInfo": "1.9.0",
7373
"supplier": "NOASSERTION",
7474
"downloadLocation": "NONE",
@@ -101,7 +101,7 @@
101101
},
102102
{
103103
"name": "B",
104-
"SPDXID": "SPDXRef-Package-6e0b0d1825d8c02c",
104+
"SPDXID": "SPDXRef-Package-e29bcba688483642",
105105
"versionInfo": "1.9.0",
106106
"supplier": "NOASSERTION",
107107
"downloadLocation": "NONE",
@@ -150,13 +150,13 @@
150150
],
151151
"relationships": [
152152
{
153-
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
154-
"relatedSpdxElement": "SPDXRef-Package-6e0b0d1825d8c02c",
153+
"spdxElementId": "SPDXRef-Application-c39d15beb6bdf085",
154+
"relatedSpdxElement": "SPDXRef-Package-3aea0b160c3af98d",
155155
"relationshipType": "CONTAINS"
156156
},
157157
{
158-
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
159-
"relatedSpdxElement": "SPDXRef-Package-761ce79b41d8f121",
158+
"spdxElementId": "SPDXRef-Application-c39d15beb6bdf085",
159+
"relatedSpdxElement": "SPDXRef-Package-e29bcba688483642",
160160
"relationshipType": "CONTAINS"
161161
},
162162
{
@@ -166,12 +166,12 @@
166166
},
167167
{
168168
"spdxElementId": "SPDXRef-Filesystem-1be792dd0077c431",
169-
"relatedSpdxElement": "SPDXRef-Application-18fc3597717a3e56",
169+
"relatedSpdxElement": "SPDXRef-Application-c39d15beb6bdf085",
170170
"relationshipType": "CONTAINS"
171171
},
172172
{
173-
"spdxElementId": "SPDXRef-Package-761ce79b41d8f121",
174-
"relatedSpdxElement": "SPDXRef-Package-28f04edc422602a",
173+
"spdxElementId": "SPDXRef-Package-3aea0b160c3af98d",
174+
"relatedSpdxElement": "SPDXRef-Package-2264d5c424c073e7",
175175
"relationshipType": "DEPENDS_ON"
176176
}
177177
]

pkg/commands/artifact/run.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -374,15 +374,6 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err
374374
}
375375
}()
376376

377-
if opts.ServerAddr != "" && opts.Scanners.AnyEnabled(types.MisconfigScanner, types.SecretScanner) {
378-
log.WarnContext(ctx,
379-
fmt.Sprintf(
380-
"Trivy runs in client/server mode, but misconfiguration and license scanning will be done on the client side, see %s",
381-
doc.URL("/docs/references/modes/client-server", ""),
382-
),
383-
)
384-
}
385-
386377
if opts.GenerateDefaultConfig {
387378
log.Info("Writing the default config to trivy-default.yaml...")
388379

@@ -423,6 +414,9 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err
423414
}
424415

425416
func run(ctx context.Context, opts flag.Options, targetKind TargetKind) (types.Report, error) {
417+
// Perform validation checks
418+
checkOptions(ctx, opts, targetKind)
419+
426420
r, err := NewRunner(ctx, opts, targetKind)
427421
if err != nil {
428422
if errors.Is(err, SkipScan) {
@@ -466,6 +460,27 @@ func run(ctx context.Context, opts flag.Options, targetKind TargetKind) (types.R
466460
return report, nil
467461
}
468462

463+
// checkOptions performs various checks on scan options and shows warnings
464+
func checkOptions(ctx context.Context, opts flag.Options, targetKind TargetKind) {
465+
// Check client/server mode with misconfiguration and secret scanning
466+
if opts.ServerAddr != "" && opts.Scanners.AnyEnabled(types.MisconfigScanner, types.SecretScanner) {
467+
log.WarnContext(ctx,
468+
fmt.Sprintf(
469+
"Trivy runs in client/server mode, but misconfiguration and license scanning will be done on the client side, see %s",
470+
doc.URL("/docs/references/modes/client-server", ""),
471+
),
472+
)
473+
}
474+
475+
// Check SBOM to SBOM scanning with package filtering flags
476+
// For SBOM-to-SBOM scanning (for example, to add vulnerabilities to the SBOM file), we should not modify the scanned file.
477+
// cf. https://github.com/aquasecurity/trivy/pull/9439#issuecomment-3295533665
478+
if targetKind == TargetSBOM && slices.Contains(types.SupportedSBOMFormats, opts.Format) &&
479+
(!slices.Equal(opts.PkgTypes, types.PkgTypes) || !slices.Equal(opts.PkgRelationships, ftypes.Relationships)) {
480+
log.Warn("'--pkg-types' and '--pkg-relationships' options will be ignored when scanning SBOM and outputting SBOM format.")
481+
}
482+
}
483+
469484
func disabledAnalyzers(opts flag.Options) []analyzer.Type {
470485
// Specified analyzers to be disabled depending on scanning modes
471486
// e.g. The 'image' subcommand should disable the lock file scanning.

pkg/fanal/artifact/sbom/sbom_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,89 @@ func TestArtifact_Inspect(t *testing.T) {
367367
},
368368
},
369369
},
370+
{
371+
name: "components with missing BOM-REF",
372+
filePath: filepath.Join("testdata", "bom-missing-refs.json"),
373+
wantBlobs: []cachetest.WantBlob{
374+
{
375+
ID: "sha256:512b9e999c9d7b4880c63ce55c2c74ea5c22b05cdbcb486097a16ec692c746a0",
376+
BlobInfo: types.BlobInfo{
377+
SchemaVersion: types.BlobJSONSchemaVersion,
378+
OS: types.OS{
379+
Family: "alpine",
380+
Name: "3.16.0",
381+
},
382+
PackageInfos: []types.PackageInfo{
383+
{
384+
Packages: types.Packages{
385+
{
386+
387+
Name: "musl",
388+
Version: "1.2.3-r0",
389+
SrcName: "musl",
390+
SrcVersion: "1.2.3-r0",
391+
Licenses: []string{"MIT"},
392+
Layer: types.Layer{
393+
DiffID: "sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
394+
},
395+
Identifier: types.PkgIdentifier{
396+
PURL: &packageurl.PackageURL{
397+
Type: packageurl.TypeApk,
398+
Namespace: "alpine",
399+
Name: "musl",
400+
Version: "1.2.3-r0",
401+
Qualifiers: packageurl.Qualifiers{
402+
{
403+
Key: "distro",
404+
Value: "3.16.0",
405+
},
406+
},
407+
},
408+
// BOM-Ref should be auto-generated from PURL
409+
BOMRef: "pkg:apk/alpine/[email protected]?distro=3.16.0",
410+
},
411+
},
412+
},
413+
},
414+
},
415+
Applications: []types.Application{
416+
{
417+
Type: "composer",
418+
FilePath: "",
419+
Packages: types.Packages{
420+
{
421+
ID: "pear/[email protected]",
422+
Name: "pear/log",
423+
Version: "1.13.1",
424+
Layer: types.Layer{
425+
DiffID: "sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
426+
},
427+
Identifier: types.PkgIdentifier{
428+
PURL: &packageurl.PackageURL{
429+
Type: packageurl.TypeComposer,
430+
Namespace: "pear",
431+
Name: "log",
432+
Version: "1.13.1",
433+
},
434+
// BOM-Ref should be auto-generated from PURL
435+
BOMRef: "pkg:composer/pear/[email protected]",
436+
},
437+
},
438+
},
439+
},
440+
},
441+
},
442+
},
443+
},
444+
want: artifact.Reference{
445+
Name: filepath.Join("testdata", "bom-missing-refs.json"),
446+
Type: types.TypeCycloneDX,
447+
ID: "sha256:512b9e999c9d7b4880c63ce55c2c74ea5c22b05cdbcb486097a16ec692c746a0",
448+
BlobIDs: []string{
449+
"sha256:512b9e999c9d7b4880c63ce55c2c74ea5c22b05cdbcb486097a16ec692c746a0",
450+
},
451+
},
452+
},
370453
{
371454
name: "sad path with no such directory",
372455
filePath: filepath.Join("testdata", "unknown.json"),

0 commit comments

Comments
 (0)