Skip to content

Commit eb3ceb3

Browse files
bedlaDmitriyLewen
andauthored
feat(sbom): Support license detection for SBOM scan (aquasecurity#6072)
Co-authored-by: DmitriyLewen <[email protected]>
1 parent ab74caa commit eb3ceb3

File tree

9 files changed

+329
-22
lines changed

9 files changed

+329
-22
lines changed

docs/docs/references/configuration/cli/trivy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ trivy [global flags] command [flags] target
5353
* [trivy plugin](trivy_plugin.md) - Manage plugins
5454
* [trivy repository](trivy_repository.md) - Scan a repository
5555
* [trivy rootfs](trivy_rootfs.md) - Scan rootfs
56-
* [trivy sbom](trivy_sbom.md) - Scan SBOM for vulnerabilities
56+
* [trivy sbom](trivy_sbom.md) - Scan SBOM for vulnerabilities and licenses
5757
* [trivy server](trivy_server.md) - Server mode
5858
* [trivy version](trivy_version.md) - Print the version
5959
* [trivy vm](trivy_vm.md) - [EXPERIMENTAL] Scan a virtual machine image

docs/docs/references/configuration/cli/trivy_sbom.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## trivy sbom
22

3-
Scan SBOM for vulnerabilities
3+
Scan SBOM for vulnerabilities and licenses
44

55
```
66
trivy sbom [flags] SBOM_PATH
@@ -36,6 +36,7 @@ trivy sbom [flags] SBOM_PATH
3636
--ignore-policy string specify the Rego file path to evaluate each vulnerability
3737
--ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life)
3838
--ignore-unfixed display only fixed vulnerabilities
39+
--ignored-licenses strings specify a list of license to ignore
3940
--ignorefile string specify .trivyignore file (default ".trivyignore")
4041
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
4142
--list-all-pkgs enabling the option will output all packages regardless of vulnerability
@@ -50,6 +51,7 @@ trivy sbom [flags] SBOM_PATH
5051
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
5152
--reset remove all caches and database
5253
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
54+
--scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln])
5355
--server string server address in client mode
5456
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
5557
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities

docs/docs/scanner/license.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ To configure the confidence level, you can use `--license-confidence-level`. Thi
3030

3131
Currently, the standard license scanning doesn't support filesystem and repository scanning.
3232

33-
| License scanning | Image | Rootfs | Filesystem | Repository |
34-
| :-------------------: | :---: | :----: | :--------: | :--------: |
35-
| Standard ||| - | - |
36-
| Full (--license-full) |||||
33+
| License scanning | Image | Rootfs | Filesystem | Repository | SBOM |
34+
|:---------------------:|:-----:|:------:|:----------:|:----------:|:----:|
35+
| Standard ||| - | - ||
36+
| Full (--license-full) ||||| - |
3737

3838
License checking classifies the identified licenses and map the classification to severity.
3939

docs/docs/target/sbom.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SBOM scanning
22

3-
Trivy can take the following SBOM formats as an input and scan for vulnerabilities.
3+
Trivy can take the following SBOM formats as an input and scan for vulnerabilities and licenses.
44

55
- CycloneDX
66
- SPDX
@@ -17,6 +17,9 @@ $ trivy sbom /path/to/sbom_file
1717

1818
```
1919

20+
By default, vulnerability scan in SBOM is executed. You can use `--scanners vuln,license`
21+
command property to select also license scan, or `--scanners license` alone.
22+
2023
!!! note
2124
Passing SBOMs generated by tool other than Trivy may result in inaccurate detection
2225
because Trivy relies on custom properties in SBOM for accurate scanning.

integration/sbom_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import (
66
"path/filepath"
77
"testing"
88

9+
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
910
v1 "github.com/google/go-containerregistry/pkg/v1"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213

13-
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
1414
"github.com/aquasecurity/trivy/pkg/types"
1515
)
1616

@@ -19,6 +19,7 @@ func TestSBOM(t *testing.T) {
1919
input string
2020
format string
2121
artifactType string
22+
scanners string
2223
}
2324
tests := []struct {
2425
name string
@@ -150,13 +151,28 @@ func TestSBOM(t *testing.T) {
150151
},
151152
},
152153
},
154+
{
155+
name: "license check cyclonedx json",
156+
args: args{
157+
input: "testdata/fixtures/sbom/license-cyclonedx.json",
158+
format: "json",
159+
artifactType: "cyclonedx",
160+
scanners: "license",
161+
},
162+
golden: "testdata/license-cyclonedx.json.golden",
163+
},
153164
}
154165

155166
// Set up testing DB
156167
cacheDir := initDB(t)
157168

158169
for _, tt := range tests {
159170
t.Run(tt.name, func(t *testing.T) {
171+
scanners := "vuln"
172+
if tt.args.scanners != "" {
173+
scanners = tt.args.scanners
174+
}
175+
160176
osArgs := []string{
161177
"--cache-dir",
162178
cacheDir,
@@ -165,6 +181,8 @@ func TestSBOM(t *testing.T) {
165181
"--skip-db-update",
166182
"--format",
167183
tt.args.format,
184+
"--scanners",
185+
scanners,
168186
}
169187

170188
// Set up the output file
@@ -223,5 +241,10 @@ func compareSBOMReports(t *testing.T, wantFile, gotFile string, overrideWant typ
223241
}
224242

225243
got := readReport(t, gotFile)
244+
// when running on Windows FS
245+
got.ArtifactName = filepath.ToSlash(filepath.Clean(got.ArtifactName))
246+
for i, result := range got.Results {
247+
got.Results[i].Target = filepath.ToSlash(filepath.Clean(result.Target))
248+
}
226249
assert.Equal(t, want, got)
227250
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.5",
5+
"serialNumber": "urn:uuid:c09512e3-47e7-4eff-8f76-5d7ae72b26a5",
6+
"version": 1,
7+
"metadata": {
8+
"timestamp": "2024-03-10T14:57:31+00:00",
9+
"tools": {
10+
"components": [
11+
{
12+
"type": "application",
13+
"group": "aquasecurity",
14+
"name": "trivy",
15+
"version": "dev"
16+
}
17+
]
18+
},
19+
"component": {
20+
"bom-ref": "acc9d4aa-4158-4969-a497-637e114fde0c",
21+
"type": "application",
22+
"name": "C:/Users/bedla.czech/IdeaProjects/sbom-demo",
23+
"properties": [
24+
{
25+
"name": "aquasecurity:trivy:SchemaVersion",
26+
"value": "2"
27+
}
28+
]
29+
}
30+
},
31+
"components": [
32+
{
33+
"bom-ref": "eb56cd49-da98-4b08-bfc8-9880fb063cf1",
34+
"type": "application",
35+
"name": "pom.xml",
36+
"properties": [
37+
{
38+
"name": "aquasecurity:trivy:Class",
39+
"value": "lang-pkgs"
40+
},
41+
{
42+
"name": "aquasecurity:trivy:Type",
43+
"value": "pom"
44+
}
45+
]
46+
},
47+
{
48+
"bom-ref": "pkg:maven/org.eclipse.sisu/[email protected]",
49+
"type": "library",
50+
"group": "org.eclipse.sisu",
51+
"name": "org.eclipse.sisu.plexus",
52+
"version": "0.3.0.M1",
53+
"licenses": [
54+
{
55+
"license": {
56+
"name": "EPL-1.0"
57+
}
58+
}
59+
],
60+
"purl": "pkg:maven/org.eclipse.sisu/[email protected]",
61+
"properties": [
62+
{
63+
"name": "aquasecurity:trivy:PkgID",
64+
"value": "org.eclipse.sisu:org.eclipse.sisu.plexus:0.3.0.M1"
65+
},
66+
{
67+
"name": "aquasecurity:trivy:PkgType",
68+
"value": "pom"
69+
}
70+
]
71+
},
72+
{
73+
"bom-ref": "pkg:maven/org.ow2.asm/[email protected]",
74+
"type": "library",
75+
"group": "org.ow2.asm",
76+
"name": "asm",
77+
"version": "9.5",
78+
"licenses": [
79+
{
80+
"license": {
81+
"name": "BSD-3-Clause"
82+
}
83+
}
84+
],
85+
"purl": "pkg:maven/org.ow2.asm/[email protected]",
86+
"properties": [
87+
{
88+
"name": "aquasecurity:trivy:PkgID",
89+
"value": "org.ow2.asm:asm:9.5"
90+
},
91+
{
92+
"name": "aquasecurity:trivy:PkgType",
93+
"value": "pom"
94+
}
95+
]
96+
},
97+
{
98+
"bom-ref": "pkg:maven/org.slf4j/[email protected]",
99+
"type": "library",
100+
"group": "org.slf4j",
101+
"name": "slf4j-api",
102+
"version": "2.0.11",
103+
"licenses": [
104+
{
105+
"license": {
106+
"name": "MIT License"
107+
}
108+
}
109+
],
110+
"purl": "pkg:maven/org.slf4j/[email protected]",
111+
"properties": [
112+
{
113+
"name": "aquasecurity:trivy:PkgID",
114+
"value": "org.slf4j:slf4j-api:2.0.11"
115+
},
116+
{
117+
"name": "aquasecurity:trivy:PkgType",
118+
"value": "pom"
119+
}
120+
]
121+
}
122+
],
123+
"dependencies": [],
124+
"vulnerabilities": []
125+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"SchemaVersion": 2,
3+
"CreatedAt": "2021-08-25T12:20:30.000000005Z",
4+
"ArtifactName": "testdata/fixtures/sbom/license-cyclonedx.json",
5+
"ArtifactType": "cyclonedx",
6+
"Metadata": {
7+
"ImageConfig": {
8+
"architecture": "",
9+
"created": "0001-01-01T00:00:00Z",
10+
"os": "",
11+
"rootfs": {
12+
"type": "",
13+
"diff_ids": null
14+
},
15+
"config": {}
16+
}
17+
},
18+
"Results": [
19+
{
20+
"Target": "OS Packages",
21+
"Class": "license"
22+
},
23+
{
24+
"Target": "pom.xml",
25+
"Class": "license"
26+
},
27+
{
28+
"Target": "Java",
29+
"Class": "license",
30+
"Licenses": [
31+
{
32+
"Severity": "MEDIUM",
33+
"Category": "reciprocal",
34+
"PkgName": "org.eclipse.sisu:org.eclipse.sisu.plexus",
35+
"FilePath": "",
36+
"Name": "EPL-1.0",
37+
"Confidence": 1,
38+
"Link": ""
39+
},
40+
{
41+
"Severity": "LOW",
42+
"Category": "notice",
43+
"PkgName": "org.ow2.asm:asm",
44+
"FilePath": "",
45+
"Name": "BSD-3-Clause",
46+
"Confidence": 1,
47+
"Link": ""
48+
},
49+
{
50+
"Severity": "UNKNOWN",
51+
"Category": "unknown",
52+
"PkgName": "org.slf4j:slf4j-api",
53+
"FilePath": "",
54+
"Name": "MIT License",
55+
"Confidence": 1,
56+
"Link": ""
57+
}
58+
]
59+
},
60+
{
61+
"Target": "Loose File License(s)",
62+
"Class": "license-file"
63+
}
64+
]
65+
}

pkg/commands/app.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,11 +1125,24 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
11251125
reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
11261126
reportFlagGroup.ReportFormat = nil // TODO: support --report summary
11271127

1128+
scanners := flag.ScannersFlag.Clone()
1129+
scanners.Values = xstrings.ToStringSlice(types.Scanners{
1130+
types.VulnerabilityScanner,
1131+
types.LicenseScanner,
1132+
})
1133+
scanners.Default = xstrings.ToStringSlice(types.Scanners{
1134+
types.VulnerabilityScanner,
1135+
})
11281136
scanFlagGroup := flag.NewScanFlagGroup()
1129-
scanFlagGroup.Scanners = nil // disable '--scanners' as it always scans for vulnerabilities
1137+
scanFlagGroup.Scanners = scanners // allow only 'vuln' and 'license' options for '--scanners'
11301138
scanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
11311139
scanFlagGroup.Parallel = nil // disable '--parallel'
11321140

1141+
licenseFlagGroup := flag.NewLicenseFlagGroup()
1142+
// License full-scan and confidence-level are for file content only
1143+
licenseFlagGroup.LicenseFull = nil
1144+
licenseFlagGroup.LicenseConfidenceLevel = nil
1145+
11331146
sbomFlags := &flag.Flags{
11341147
GlobalFlagGroup: globalFlags,
11351148
CacheFlagGroup: flag.NewCacheFlagGroup(),
@@ -1139,11 +1152,12 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
11391152
ScanFlagGroup: scanFlagGroup,
11401153
SBOMFlagGroup: flag.NewSBOMFlagGroup(),
11411154
VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
1155+
LicenseFlagGroup: licenseFlagGroup,
11421156
}
11431157

11441158
cmd := &cobra.Command{
11451159
Use: "sbom [flags] SBOM_PATH",
1146-
Short: "Scan SBOM for vulnerabilities",
1160+
Short: "Scan SBOM for vulnerabilities and licenses",
11471161
GroupID: groupScanning,
11481162
Example: ` # Scan CycloneDX and show the result in tables
11491163
$ trivy sbom /path/to/report.cdx
@@ -1166,9 +1180,6 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
11661180
return xerrors.Errorf("flag error: %w", err)
11671181
}
11681182

1169-
// Scan vulnerabilities
1170-
options.Scanners = types.Scanners{types.VulnerabilityScanner}
1171-
11721183
return artifact.Run(cmd.Context(), options, artifact.TargetSBOM)
11731184
},
11741185
SilenceErrors: true,

0 commit comments

Comments
 (0)