BREAKING: add new classes for vulnerabilities (#2541)

This commit is contained in:
Teppei Fukuda
2022-07-31 10:47:08 +03:00
committed by GitHub
parent 3cd88abec5
commit f396c677a2
51 changed files with 332 additions and 214 deletions

View File

@@ -6,8 +6,16 @@ Note that XML format is not supported at the moment.
You can use the regular subcommands (like `image`, `fs` and `rootfs`) and specify `cyclonedx` with the `--format` option. You can use the regular subcommands (like `image`, `fs` and `rootfs`) and specify `cyclonedx` with the `--format` option.
CycloneDX can represent either or both SBOM or BOV.
- [Software Bill of Materials (SBOM)][sbom]
- [Bill of Vulnerabilities (BOV)][bov]
By default, `--format cyclonedx` represents SBOM and doesn't include vulnerabilities in the CycloneDX output.
``` ```
$ trivy image --format cyclonedx --output result.json alpine:3.15 $ trivy image --format cyclonedx --output result.json alpine:3.15
2022-07-19T07:47:27.624Z INFO "--format cyclonedx" disables security checks. Specify "--security-checks vuln" explicitly if you want to include vulnerabilities in the CycloneDX report.
``` ```
<details> <details>
@@ -231,6 +239,12 @@ $ cat result.json | jq .
</details> </details>
If you want to include vulnerabilities, you can enable vulnerability scanning via `--security-checks vuln`.
```
$ trivy image --security-checks vuln --format cyclonedx --output result.json alpine:3.15
```
## Scanning ## Scanning
Trivy can take CycloneDX as an input and scan for vulnerabilities. Trivy can take CycloneDX as an input and scan for vulnerabilities.
To scan SBOM, you can use the `sbom` subcommand and pass the path to your CycloneDX report. To scan SBOM, you can use the `sbom` subcommand and pass the path to your CycloneDX report.
@@ -258,5 +272,8 @@ Total: 3 (CRITICAL: 3)
!!! note !!! note
If you want to generate a CycloneDX report from a CycloneDX input, please be aware that the output stores references to your original CycloneDX report and contains only detected vulnerabilities, not components. If you want to generate a CycloneDX report from a CycloneDX input, please be aware that the output stores references to your original CycloneDX report and contains only detected vulnerabilities, not components.
The report is called [BOV][bov].
[cyclonedx]: https://cyclonedx.org/ [cyclonedx]: https://cyclonedx.org/
[sbom]: https://cyclonedx.org/capabilities/sbom/
[bov]: https://cyclonedx.org/capabilities/bov/

View File

@@ -9,9 +9,10 @@ Trivy can generate the following SBOM formats.
To generate SBOM, you can use the `--format` option for each subcommand such as `image` and `fs`. To generate SBOM, you can use the `--format` option for each subcommand such as `image` and `fs`.
``` ```
$ trivy image --format cyclonedx --output result.json alpine:3.15 $ trivy image --format spdx-json --output result.json alpine:3.15
``` ```
``` ```
$ trivy fs --format cyclonedx --output result.json /app/myproject $ trivy fs --format cyclonedx --output result.json /app/myproject
``` ```

View File

@@ -48,7 +48,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/almalinux-8.tar.gz (alma 8.5)", "Target": "testdata/fixtures/images/almalinux-8.tar.gz (alma 8.5)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alma", "Type": "alma",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -56,7 +56,7 @@
"Results": [ "Results": [
{ {
"Target": "localhost:63577/alpine:3.10 (alpine 3.10.2)", "Target": "localhost:63577/alpine:3.10 (alpine 3.10.2)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -50,7 +50,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/alpine-310.tar.gz (alpine 3.10.2)", "Target": "testdata/fixtures/images/alpine-310.tar.gz (alpine 3.10.2)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -50,7 +50,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -50,7 +50,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -50,7 +50,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -45,7 +45,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/alpine-distroless.tar.gz (alpine 3.16)", "Target": "testdata/fixtures/images/alpine-distroless.tar.gz (alpine 3.16)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "alpine", "Type": "alpine",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -49,7 +49,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/amazon-1.tar.gz (amazon AMI release 2018.03)", "Target": "testdata/fixtures/images/amazon-1.tar.gz (amazon AMI release 2018.03)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "amazon", "Type": "amazon",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -49,7 +49,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/amazon-2.tar.gz (amazon 2 (Karoo))", "Target": "testdata/fixtures/images/amazon-2.tar.gz (amazon 2 (Karoo))",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "amazon", "Type": "amazon",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -49,7 +49,7 @@
"Results": [ "Results": [
{ {
"Target": "Cargo.lock", "Target": "Cargo.lock",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "cargo", "Type": "cargo",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -71,7 +71,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/centos-6.tar.gz (centos 6.10)", "Target": "testdata/fixtures/images/centos-6.tar.gz (centos 6.10)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "centos", "Type": "centos",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -61,7 +61,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "centos", "Type": "centos",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -61,7 +61,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "centos", "Type": "centos",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -61,7 +61,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "centos", "Type": "centos",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -49,7 +49,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)", "Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -49,7 +49,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)", "Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -6,7 +6,7 @@
"OS": { "OS": {
"Family": "debian", "Family": "debian",
"Name": "9.9", "Name": "9.9",
"Eosl": true "EOSL": true
}, },
"ImageID": "sha256:f26939cc87ef44a6fc554eedd0a976ab30b5bc2769d65d2e986b6c5f1fd4053d", "ImageID": "sha256:f26939cc87ef44a6fc554eedd0a976ab30b5bc2769d65d2e986b6c5f1fd4053d",
"DiffIDs": [ "DiffIDs": [
@@ -50,7 +50,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/debian-stretch.tar.gz (debian 9.9)", "Target": "testdata/fixtures/images/debian-stretch.tar.gz (debian 9.9)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -6,7 +6,7 @@
"OS": { "OS": {
"Family": "debian", "Family": "debian",
"Name": "9.9", "Name": "9.9",
"Eosl": true "EOSL": true
}, },
"ImageID": "sha256:7f04a8d247173b1f2546d22913af637bbab4e7411e00ae6207da8d94c445750d", "ImageID": "sha256:7f04a8d247173b1f2546d22913af637bbab4e7411e00ae6207da8d94c445750d",
"DiffIDs": [ "DiffIDs": [
@@ -48,7 +48,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/distroless-base.tar.gz (debian 9.9)", "Target": "testdata/fixtures/images/distroless-base.tar.gz (debian 9.9)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -6,7 +6,7 @@
"OS": { "OS": {
"Family": "debian", "Family": "debian",
"Name": "9.9", "Name": "9.9",
"Eosl": true "EOSL": true
}, },
"ImageID": "sha256:6fcac2cc8a710f21577b5bbd534e0bfc841c0cca569b57182ba19054696cddda", "ImageID": "sha256:6fcac2cc8a710f21577b5bbd534e0bfc841c0cca569b57182ba19054696cddda",
"DiffIDs": [ "DiffIDs": [
@@ -65,7 +65,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/distroless-python27.tar.gz (debian 9.9)", "Target": "testdata/fixtures/images/distroless-python27.tar.gz (debian 9.9)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -102,7 +102,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/fluentd-multiple-lockfiles.tar.gz (debian 10.2)", "Target": "testdata/fixtures/images/fluentd-multiple-lockfiles.tar.gz (debian 10.2)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian", "Type": "debian",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
@@ -165,7 +165,7 @@
}, },
{ {
"Target": "Ruby", "Target": "Ruby",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "gemspec", "Type": "gemspec",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -17,7 +17,7 @@
"Results": [ "Results": [
{ {
"Target": "go.mod", "Target": "go.mod",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "gomod", "Type": "gomod",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
@@ -103,7 +103,7 @@
}, },
{ {
"Target": "submod/go.mod", "Target": "submod/go.mod",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "gomod", "Type": "gomod",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
@@ -131,7 +131,7 @@
}, },
{ {
"Target": "submod2/go.mod", "Target": "submod2/go.mod",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "gomod", "Type": "gomod",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -34,7 +34,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/mariner-1.0.tar.gz (cbl-mariner 1.0.20220122)", "Target": "testdata/fixtures/images/mariner-1.0.tar.gz (cbl-mariner 1.0.20220122)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "cbl-mariner", "Type": "cbl-mariner",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -17,7 +17,7 @@
"Results": [ "Results": [
{ {
"Target": "package-lock.json", "Target": "package-lock.json",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "npm", "Type": "npm",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -57,7 +57,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/opensuse-leap-151.tar.gz (opensuse.leap 15.1)", "Target": "testdata/fixtures/images/opensuse-leap-151.tar.gz (opensuse.leap 15.1)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "opensuse.leap", "Type": "opensuse.leap",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -58,7 +58,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/oraclelinux-8.tar.gz (oracle 8.0)", "Target": "testdata/fixtures/images/oraclelinux-8.tar.gz (oracle 8.0)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "oracle", "Type": "oracle",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -59,7 +59,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/photon-30.tar.gz (photon 3.0)", "Target": "testdata/fixtures/images/photon-30.tar.gz (photon 3.0)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "photon", "Type": "photon",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -55,7 +55,12 @@
"Version": "2.0.0", "Version": "2.0.0",
"Layer": {} "Layer": {}
} }
], ]
},
{
"Target": "requirements.txt",
"Class": "vuln-lang-pkgs",
"Type": "pip",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
"VulnerabilityID": "CVE-2019-14806", "VulnerabilityID": "CVE-2019-14806",

View File

@@ -5,7 +5,7 @@
"Results": [ "Results": [
{ {
"Target": "pnpm-lock.yaml", "Target": "pnpm-lock.yaml",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "pnpm", "Type": "pnpm",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -17,7 +17,7 @@
"Results": [ "Results": [
{ {
"Target": "pom.xml", "Target": "pom.xml",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "pom", "Type": "pom",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -48,7 +48,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/rockylinux-8.tar.gz (rocky 8.5)", "Target": "testdata/fixtures/images/rockylinux-8.tar.gz (rocky 8.5)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "rocky", "Type": "rocky",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
@@ -118,11 +118,6 @@
"LastModifiedDate": "2022-01-06T09:15:00Z" "LastModifiedDate": "2022-01-06T09:15:00Z"
} }
] ]
},
{
"Target": "Python",
"Class": "lang-pkgs",
"Type": "python-pkg"
} }
] ]
} }

View File

@@ -26,8 +26,7 @@
"Title": "AWS Access Key ID", "Title": "AWS Access Key ID",
"StartLine": 3, "StartLine": 3,
"EndLine": 3, "EndLine": 3,
"Match": "export AWS_ACCESS_KEY_ID=********************", "Code": {
"Code" : {
"Lines": [ "Lines": [
{ {
"Number": 1, "Number": 1,
@@ -45,7 +44,6 @@
"IsCause": false, "IsCause": false,
"Annotation": "", "Annotation": "",
"Truncated": false, "Truncated": false,
"Highlighted": "",
"FirstCause": false, "FirstCause": false,
"LastCause": false "LastCause": false
}, },
@@ -65,12 +63,12 @@
"IsCause": false, "IsCause": false,
"Annotation": "", "Annotation": "",
"Truncated": false, "Truncated": false,
"Highlighted": "",
"FirstCause": false, "FirstCause": false,
"LastCause": false "LastCause": false
} }
] ]
} },
"Match": "export AWS_ACCESS_KEY_ID=********************"
}, },
{ {
"RuleID": "mysecret", "RuleID": "mysecret",
@@ -79,8 +77,7 @@
"Title": "My Secret", "Title": "My Secret",
"StartLine": 7, "StartLine": 7,
"EndLine": 7, "EndLine": 7,
"Match": "echo ********", "Code": {
"Code" : {
"Lines": [ "Lines": [
{ {
"Number": 5, "Number": 5,
@@ -98,7 +95,6 @@
"IsCause": false, "IsCause": false,
"Annotation": "", "Annotation": "",
"Truncated": false, "Truncated": false,
"Highlighted": "",
"FirstCause": false, "FirstCause": false,
"LastCause": false "LastCause": false
}, },
@@ -113,7 +109,8 @@
"LastCause": true "LastCause": true
} }
] ]
} },
"Match": "echo ********"
} }
] ]
} }

View File

@@ -185,12 +185,12 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/spring4shell-jre11.tar.gz (debian 11.3)", "Target": "testdata/fixtures/images/spring4shell-jre11.tar.gz (debian 11.3)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian" "Type": "debian"
}, },
{ {
"Target": "Java", "Target": "Java",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "jar", "Type": "jar",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -185,12 +185,12 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/spring4shell-jre8.tar.gz (debian 11.3)", "Target": "testdata/fixtures/images/spring4shell-jre8.tar.gz (debian 11.3)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "debian" "Type": "debian"
}, },
{ {
"Target": "Java", "Target": "Java",
"Class": "lang-pkgs", "Class": "vuln-lang-pkgs",
"Type": "jar", "Type": "jar",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -72,7 +72,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/ubi-7.tar.gz (redhat 7.7)", "Target": "testdata/fixtures/images/ubi-7.tar.gz (redhat 7.7)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "redhat", "Type": "redhat",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -67,7 +67,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)", "Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "ubuntu", "Type": "ubuntu",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -67,7 +67,7 @@
"Results": [ "Results": [
{ {
"Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)", "Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)",
"Class": "os-pkgs", "Class": "vuln-os-pkgs",
"Type": "ubuntu", "Type": "ubuntu",
"Vulnerabilities": [ "Vulnerabilities": [
{ {

View File

@@ -240,7 +240,7 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
$ trivy image --format json --output result.json alpine:3.15 $ trivy image --format json --output result.json alpine:3.15
# Generate a report in the CycloneDX format # Generate a report in the CycloneDX format
$ trivy image --format cyclonedx --output result.cdx alpine:3.15`, $ trivy image --format cyclonedx --output result.cdx --security-checks none alpine:3.15`,
// 'Args' cannot be used since it is called before PreRunE and viper is not configured yet. // 'Args' cannot be used since it is called before PreRunE and viper is not configured yet.
// cmd.Args -> cannot validate args here // cmd.Args -> cannot validate args here

View File

@@ -12,6 +12,8 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
) )
type Flag struct { type Flag struct {
@@ -83,6 +85,20 @@ type Options struct {
DisabledAnalyzers []analyzer.Type DisabledAnalyzers []analyzer.Type
} }
// Align takes consistency of options
func (o *Options) Align() {
if o.Format == report.FormatSPDX || o.Format == report.FormatSPDXJSON {
log.Logger.Info(`"--format spdx" and "--format spdx-json" disable security checks`)
o.SecurityChecks = nil
}
// Vulnerability scanning is disabled by default for CycloneDX.
if o.Format == report.FormatCycloneDX && !viper.IsSet(SecurityChecksFlag.ConfigName) {
log.Logger.Info(`"--format cyclonedx" disables security checks. Specify "--security-checks vuln" explicitly if you want to include vulnerabilities in the CycloneDX report.`)
o.SecurityChecks = nil
}
}
func addFlag(cmd *cobra.Command, flag *Flag) { func addFlag(cmd *cobra.Command, flag *Flag) {
if flag == nil || flag.Name == "" { if flag == nil || flag.Name == "" {
return return
@@ -337,6 +353,8 @@ func (f *Flags) ToOptions(appVersion string, args []string, globalFlags *GlobalF
opts.VulnerabilityOptions = f.VulnerabilityFlagGroup.ToOptions() opts.VulnerabilityOptions = f.VulnerabilityFlagGroup.ToOptions()
} }
opts.Align()
return opts, nil return opts, nil
} }

View File

@@ -102,7 +102,7 @@ func (w Writer) Write(report types.Report) error {
manifest := Manifest{} manifest := Manifest{}
manifest.Name = result.Type manifest.Name = result.Type
//show path for languages only // show path for language-specific packages only
if result.Class == types.ClassLangPkg { if result.Class == types.ClassLangPkg {
manifest.File = &File{ manifest.File = &File{
SrcLocation: result.Target, SrcLocation: result.Target,

View File

@@ -187,9 +187,9 @@ func (sw SarifWriter) Write(report types.Report) error {
func toSarifRuleName(class string) string { func toSarifRuleName(class string) string {
switch class { switch class {
case types.ClassOSPkg: case types.ClassVulnOSPkg:
return sarifOsPackageVulnerability return sarifOsPackageVulnerability
case types.ClassLangPkg: case types.ClassVulnLangPkg:
return sarifLanguageSpecificVulnerability return sarifLanguageSpecificVulnerability
case types.ClassConfig: case types.ClassConfig:
return sarifConfigFiles return sarifConfigFiles

View File

@@ -30,7 +30,7 @@ func TestReportWriter_Sarif(t *testing.T) {
input: types.Results{ input: types.Results{
{ {
Target: "library/test", Target: "library/test",
Class: types.ClassOSPkg, Class: types.ClassVulnOSPkg,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2020-0001",

View File

@@ -13,6 +13,7 @@ import (
"github.com/aquasecurity/table" "github.com/aquasecurity/table"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types" dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
) )
@@ -52,17 +53,25 @@ type Renderer interface {
// Write writes the result on standard output // Write writes the result on standard output
func (tw Writer) Write(report types.Report) error { func (tw Writer) Write(report types.Report) error {
// Iterate results to extract packages first, then write tables for each result
pkgs := map[string][]ftypes.Package{}
for _, result := range report.Results {
if result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg {
pkgs[result.Target] = result.Packages
}
}
for _, result := range report.Results { for _, result := range report.Results {
// Not display a table of custom resources // Not display a table of custom resources
if result.Class == types.ClassCustom { if result.Class == types.ClassCustom {
continue continue
} }
tw.write(result) tw.write(result, pkgs)
} }
return nil return nil
} }
func (tw Writer) write(result types.Result) { func (tw Writer) write(result types.Result, pkgs map[string][]ftypes.Package) {
if result.IsEmpty() && result.Class != types.ClassOSPkg { if result.IsEmpty() && result.Class != types.ClassOSPkg {
return return
} }
@@ -70,8 +79,8 @@ func (tw Writer) write(result types.Result) {
var renderer Renderer var renderer Renderer
switch { switch {
// vulnerability // vulnerability
case result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg: case result.Class == types.ClassVulnOSPkg || result.Class == types.ClassVulnLangPkg:
renderer = NewVulnerabilityRenderer(result, tw.isOutputToTerminal(), tw.Tree, tw.Severities) renderer = NewVulnerabilityRenderer(result, pkgs, tw.isOutputToTerminal(), tw.Tree, tw.Severities)
// misconfiguration // misconfiguration
case result.Class == types.ClassConfig: case result.Class == types.ClassConfig:
renderer = NewMisconfigRenderer(result, tw.Severities, tw.Trace, tw.IncludeNonFailures, tw.isOutputToTerminal()) renderer = NewMisconfigRenderer(result, tw.Severities, tw.Trace, tw.IncludeNonFailures, tw.isOutputToTerminal())

View File

@@ -24,7 +24,7 @@ func TestReportWriter_Table(t *testing.T) {
results: types.Results{ results: types.Results{
{ {
Target: "test", Target: "test",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2020-0001",
@@ -59,7 +59,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
results: types.Results{ results: types.Results{
{ {
Target: "test", Target: "test",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2020-0001",
@@ -95,7 +95,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
results: types.Results{ results: types.Results{
{ {
Target: "test", Target: "test",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2020-0001",
@@ -127,7 +127,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
results: types.Results{ results: types.Results{
{ {
Target: "test", Target: "test",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-1234", VulnerabilityID: "CVE-2020-1234",
@@ -166,7 +166,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
results: types.Results{ results: types.Results{
{ {
Target: "package-lock.json", Target: "package-lock.json",
Class: "lang-pkgs", Class: types.ClassLangPkg,
Type: "npm", Type: "npm",
Packages: []ftypes.Package{ Packages: []ftypes.Package{
{ {
@@ -199,6 +199,11 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
}, },
}, },
}, },
},
{
Target: "package-lock.json",
Class: types.ClassVulnLangPkg,
Type: "npm",
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2022-0235", VulnerabilityID: "CVE-2022-0235",
@@ -256,7 +261,7 @@ package-lock.json
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
tableWritten := bytes.Buffer{} tableWritten := bytes.Buffer{}
err := report.Write(types.Report{Results: tc.results}, report.Option{ err := report.Write(types.Report{Results: tc.results}, report.Option{
Format: "table", Format: report.FormatTable,
Output: &tableWritten, Output: &tableWritten,
Tree: true, Tree: true,
IncludeNonFailures: tc.includeNonFailures, IncludeNonFailures: tc.includeNonFailures,

View File

@@ -23,13 +23,14 @@ type vulnerabilityRenderer struct {
w *bytes.Buffer w *bytes.Buffer
tableWriter *table.Table tableWriter *table.Table
result types.Result result types.Result
pkgs map[string][]ftypes.Package
isTerminal bool isTerminal bool
tree bool tree bool
severities []dbTypes.Severity severities []dbTypes.Severity
once *sync.Once once *sync.Once
} }
func NewVulnerabilityRenderer(result types.Result, isTerminal, tree bool, severities []dbTypes.Severity) vulnerabilityRenderer { func NewVulnerabilityRenderer(result types.Result, pkgs map[string][]ftypes.Package, isTerminal, tree bool, severities []dbTypes.Severity) vulnerabilityRenderer {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
if !isTerminal { if !isTerminal {
tml.DisableFormatting() tml.DisableFormatting()
@@ -38,6 +39,7 @@ func NewVulnerabilityRenderer(result types.Result, isTerminal, tree bool, severi
w: buf, w: buf,
tableWriter: newTableWriter(buf, isTerminal), tableWriter: newTableWriter(buf, isTerminal),
result: result, result: result,
pkgs: pkgs,
isTerminal: isTerminal, isTerminal: isTerminal,
tree: tree, tree: tree,
severities: severities, severities: severities,
@@ -53,7 +55,7 @@ func (r vulnerabilityRenderer) Render() string {
total, summaries := summarize(r.severities, severityCount) total, summaries := summarize(r.severities, severityCount)
target := r.result.Target target := r.result.Target
if r.result.Class != types.ClassOSPkg { if r.result.Class == types.ClassVulnLangPkg {
target += fmt.Sprintf(" (%s)", r.result.Type) target += fmt.Sprintf(" (%s)", r.result.Type)
} }
renderTarget(r.w, target, r.isTerminal) renderTarget(r.w, target, r.isTerminal)
@@ -128,8 +130,14 @@ func (r vulnerabilityRenderer) countSeverities(vulns []types.DetectedVulnerabili
} }
func (r vulnerabilityRenderer) renderDependencyTree() { func (r vulnerabilityRenderer) renderDependencyTree() {
// Take packages
pkgs, ok := r.pkgs[r.result.Target]
if !ok {
return
}
// Get parents of each dependency // Get parents of each dependency
parents := reverseDeps(r.result.Packages) parents := reverseDeps(pkgs)
if len(parents) == 0 { if len(parents) == 0 {
return return
} }

View File

@@ -187,16 +187,17 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com
var metadataDependencies []cdx.Dependency var metadataDependencies []cdx.Dependency
libraryUniqMap := map[string]struct{}{} libraryUniqMap := map[string]struct{}{}
vulnMap := map[string]cdx.Vulnerability{} vulnMap := map[string]cdx.Vulnerability{}
bomRefMap := map[string]string{}
for _, result := range r.Results { for _, result := range r.Results {
var componentDependencies []cdx.Dependency var componentDependencies []cdx.Dependency
bomRefMap := map[string]string{}
for _, pkg := range result.Packages { for _, pkg := range result.Packages {
pkgComponent, err := pkgToCdxComponent(result.Type, r.Metadata, pkg) pkgComponent, err := pkgToCdxComponent(result.Type, r.Metadata, pkg)
if err != nil { if err != nil {
return nil, nil, nil, xerrors.Errorf("failed to parse pkg: %w", err) return nil, nil, nil, xerrors.Errorf("failed to parse pkg: %w", err)
} }
if _, ok := bomRefMap[pkg.Name+utils.FormatVersion(pkg)+pkg.FilePath]; !ok { pkgID := packageID(result.Target, pkg.Name, utils.FormatVersion(pkg), pkg.FilePath)
bomRefMap[pkg.Name+utils.FormatVersion(pkg)+pkg.FilePath] = pkgComponent.BOMRef if _, ok := bomRefMap[pkgID]; !ok {
bomRefMap[pkgID] = pkgComponent.BOMRef
} }
// When multiple lock files have the same dependency with the same name and version, // When multiple lock files have the same dependency with the same name and version,
@@ -222,9 +223,11 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com
componentDependencies = append(componentDependencies, cdx.Dependency{Ref: pkgComponent.BOMRef}) componentDependencies = append(componentDependencies, cdx.Dependency{Ref: pkgComponent.BOMRef})
} }
for _, vuln := range result.Vulnerabilities { for _, vuln := range result.Vulnerabilities {
// Take a bom-ref // Take a bom-ref
ref := bomRefMap[vuln.PkgName+vuln.InstalledVersion+vuln.PkgPath] pkgID := packageID(result.Target, vuln.PkgName, vuln.InstalledVersion, vuln.PkgPath)
ref := bomRefMap[pkgID]
if v, ok := vulnMap[vuln.VulnerabilityID]; ok { if v, ok := vulnMap[vuln.VulnerabilityID]; ok {
// If a vulnerability depends on multiple packages, // If a vulnerability depends on multiple packages,
// it will be commonised into a single vulnerability. // it will be commonised into a single vulnerability.
@@ -250,7 +253,7 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com
// Dependency graph from #1 to #2 // Dependency graph from #1 to #2
metadataDependencies = append(metadataDependencies, componentDependencies...) metadataDependencies = append(metadataDependencies, componentDependencies...)
} else { } else if result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg {
// If a package is OS package, it will be a dependency of "Operating System" component. // If a package is OS package, it will be a dependency of "Operating System" component.
// e.g. // e.g.
// Container component (alpine:3.15) --------------------- #1 // Container component (alpine:3.15) --------------------- #1
@@ -291,6 +294,10 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com
return &components, &dependencies, &vulns, nil return &components, &dependencies, &vulns, nil
} }
func packageID(target, pkgName, pkgVersion, pkgFilePath string) string {
return fmt.Sprintf("%s/%s/%s/%s", target, pkgName, pkgVersion, pkgFilePath)
}
func toCdxVulnerability(bomRef string, vuln types.DetectedVulnerability) cdx.Vulnerability { func toCdxVulnerability(bomRef string, vuln types.DetectedVulnerability) cdx.Vulnerability {
v := cdx.Vulnerability{ v := cdx.Vulnerability{
ID: vuln.VulnerabilityID, ID: vuln.VulnerabilityID,

View File

@@ -70,6 +70,11 @@ func TestMarshaler_Marshal(t *testing.T) {
Licenses: []string{"GPLv3+"}, Licenses: []string{"GPLv3+"},
}, },
}, },
},
{
Target: "rails:latest (centos 8.3.2011)",
Class: types.ClassVulnOSPkg,
Type: fos.CentOS,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2018-20623", VulnerabilityID: "CVE-2018-20623",
@@ -470,6 +475,11 @@ func TestMarshaler_Marshal(t *testing.T) {
FilePath: "tools/project-doe/specifications/actionpack.gemspec", FilePath: "tools/project-doe/specifications/actionpack.gemspec",
}, },
}, },
},
{
Target: "Ruby",
Class: types.ClassVulnOSPkg,
Type: ftypes.GemSpec,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2022-23633", VulnerabilityID: "CVE-2022-23633",
@@ -1105,6 +1115,11 @@ func TestMarshaler_MarshalVulnerabilities(t *testing.T) {
Licenses: []string{"GPLv3+"}, Licenses: []string{"GPLv3+"},
}, },
}, },
},
{
Target: "rails:latest (centos 8.3.2011)",
Class: types.ClassVulnOSPkg,
Type: fos.CentOS,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2018-20623", VulnerabilityID: "CVE-2018-20623",

View File

@@ -98,10 +98,18 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
var eosl bool var eosl bool
var results types.Results var results types.Results
// Scan OS packages and language-specific dependencies // Fill OS packages and language-specific packages
if options.ListAllPackages {
if res := s.osPkgsToResult(target, artifactDetail, options); res != nil {
results = append(results, *res)
}
results = append(results, s.langPkgsToResult(artifactDetail)...)
}
// Scan packages for vulnerabilities
if slices.Contains(options.SecurityChecks, types.SecurityCheckVulnerability) { if slices.Contains(options.SecurityChecks, types.SecurityCheckVulnerability) {
var vulnResults types.Results var vulnResults types.Results
vulnResults, eosl, err = s.checkVulnerabilities(target, artifactDetail, options) vulnResults, eosl, err = s.scanVulnerabilities(target, artifactDetail, options)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err) return nil, nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
} }
@@ -123,6 +131,12 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
results = append(results, secretResults...) results = append(results, secretResults...)
} }
// Scan licenses
if slices.Contains(options.SecurityChecks, types.SecurityCheckLicense) {
licenseResults := s.scanLicenses(artifactDetail, options.LicenseCategories)
results = append(results, licenseResults...)
}
// For WASM plugins and custom analyzers // For WASM plugins and custom analyzers
if len(artifactDetail.CustomResources) != 0 { if len(artifactDetail.CustomResources) != 0 {
results = append(results, types.Result{ results = append(results, types.Result{
@@ -131,12 +145,6 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
}) })
} }
// Scan licenses
if slices.Contains(options.SecurityChecks, types.SecurityCheckLicense) {
licenseResults := s.scanLicenses(artifactDetail, options.LicenseCategories)
results = append(results, licenseResults...)
}
for i := range results { for i := range results {
// Fill vulnerability details // Fill vulnerability details
s.vulnClient.FillInfo(results[i].Vulnerabilities) s.vulnClient.FillInfo(results[i].Vulnerabilities)
@@ -151,11 +159,51 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys
return results, artifactDetail.OS, nil return results, artifactDetail.OS, nil
} }
func shouldScanMisconfig(securityChecks []string) bool { func (s Scanner) osPkgsToResult(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) *types.Result {
return slices.Contains(securityChecks, types.SecurityCheckConfig) || slices.Contains(securityChecks, types.SecurityCheckRbac) if len(detail.Packages) == 0 {
return nil
}
if detail.OS != nil {
target = fmt.Sprintf("%s (%s %s)", target, detail.OS.Family, detail.OS.Name)
}
pkgs := detail.Packages
if options.ScanRemovedPackages {
pkgs = mergePkgs(pkgs, detail.HistoryPackages)
}
sort.Slice(pkgs, func(i, j int) bool {
return strings.Compare(pkgs[i].Name, pkgs[j].Name) <= 0
})
return &types.Result{
Target: target,
Class: types.ClassOSPkg,
Type: detail.OS.Family,
Packages: pkgs,
}
} }
func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) ( func (s Scanner) langPkgsToResult(detail ftypes.ArtifactDetail) types.Results {
var results types.Results
for _, app := range detail.Applications {
if len(app.Libraries) == 0 {
continue
}
target := app.FilePath
if t, ok := pkgTargets[app.Type]; ok && target == "" {
// When the file path is empty, we will overwrite it with the pre-defined value.
target = t
}
results = append(results, types.Result{
Target: target,
Class: types.ClassLangPkg,
Type: app.Type,
Packages: app.Libraries,
})
}
return results
}
func (s Scanner) scanVulnerabilities(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
types.Results, bool, error) { types.Results, bool, error) {
var eosl bool var eosl bool
var results types.Results var results types.Results
@@ -171,7 +219,7 @@ func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetai
} }
if slices.Contains(options.VulnType, types.VulnTypeLibrary) { if slices.Contains(options.VulnType, types.VulnTypeLibrary) {
libResults, err := s.scanLibrary(detail.Applications, options) libResults, err := s.scanLangPkgs(detail.Applications)
if err != nil { if err != nil {
return nil, false, xerrors.Errorf("failed to scan application libraries: %w", err) return nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
} }
@@ -183,7 +231,7 @@ func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetai
func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) ( func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
*types.Result, bool, error) { *types.Result, bool, error) {
if detail.OS == nil { if detail.OS == nil || detail.OS.Family == "" {
log.Logger.Debug("Detected OS: unknown") log.Logger.Debug("Detected OS: unknown")
return nil, false, nil return nil, false, nil
} }
@@ -194,45 +242,24 @@ func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options
pkgs = mergePkgs(pkgs, detail.HistoryPackages) pkgs = mergePkgs(pkgs, detail.HistoryPackages)
} }
result, eosl, err := s.detectVulnsInOSPkgs(target, detail.OS.Family, detail.OS.Name, detail.Repository, pkgs) vulns, eosl, err := s.ospkgDetector.Detect("", detail.OS.Family, detail.OS.Name, detail.Repository, time.Time{}, pkgs)
if err != nil {
return nil, false, xerrors.Errorf("failed to scan OS packages: %w", err)
} else if result == nil {
return nil, eosl, nil
}
if options.ListAllPackages {
sort.Slice(pkgs, func(i, j int) bool {
return strings.Compare(pkgs[i].Name, pkgs[j].Name) <= 0
})
result.Packages = pkgs
}
return result, eosl, nil
}
func (s Scanner) detectVulnsInOSPkgs(target, osFamily, osName string, repo *ftypes.Repository, pkgs []ftypes.Package) (*types.Result, bool, error) {
if osFamily == "" {
return nil, false, nil
}
vulns, eosl, err := s.ospkgDetector.Detect("", osFamily, osName, repo, time.Time{}, pkgs)
if err == ospkgDetector.ErrUnsupportedOS { if err == ospkgDetector.ErrUnsupportedOS {
return nil, false, nil return nil, false, nil
} else if err != nil { } else if err != nil {
return nil, false, xerrors.Errorf("failed vulnerability detection of OS packages: %w", err) return nil, false, xerrors.Errorf("failed vulnerability detection of OS packages: %w", err)
} }
artifactDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osName) artifactDetail := fmt.Sprintf("%s (%s %s)", target, detail.OS.Family, detail.OS.Name)
result := &types.Result{ result := &types.Result{
Target: artifactDetail, Target: artifactDetail,
Vulnerabilities: vulns, Vulnerabilities: vulns,
Class: types.ClassOSPkg, Class: types.ClassVulnOSPkg,
Type: osFamily, Type: detail.OS.Family,
} }
return result, eosl, nil return result, eosl, nil
} }
func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOptions) (types.Results, error) { func (s Scanner) scanLangPkgs(apps []ftypes.Application) (types.Results, error) {
log.Logger.Infof("Number of language-specific files: %d", len(apps)) log.Logger.Infof("Number of language-specific files: %d", len(apps))
if len(apps) == 0 { if len(apps) == 0 {
return nil, nil return nil, nil
@@ -255,6 +282,8 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
vulns, err := library.Detect(app.Type, app.Libraries) vulns, err := library.Detect(app.Type, app.Libraries)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err) return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err)
} else if len(vulns) == 0 {
return nil, nil
} }
target := app.FilePath target := app.FilePath
@@ -263,16 +292,12 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
target = t target = t
} }
libReport := types.Result{ results = append(results, types.Result{
Target: target, Target: target,
Vulnerabilities: vulns, Vulnerabilities: vulns,
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Type: app.Type, Type: app.Type,
} })
if options.ListAllPackages {
libReport.Packages = app.Libraries
}
results = append(results, libReport)
} }
sort.Slice(results, func(i, j int) bool { sort.Slice(results, func(i, j int) bool {
return results[i].Target < results[j].Target return results[i].Target < results[j].Target
@@ -475,3 +500,7 @@ func mergePkgs(pkgs, pkgsFromCommands []ftypes.Package) []ftypes.Package {
} }
return pkgs return pkgs
} }
func shouldScanMisconfig(securityChecks []string) bool {
return slices.Contains(securityChecks, types.SecurityCheckConfig) || slices.Contains(securityChecks, types.SecurityCheckRbac)
}

View File

@@ -89,7 +89,7 @@ func TestScanner_Scan(t *testing.T) {
wantResults: types.Results{ wantResults: types.Results{
{ {
Target: "alpine:latest (alpine 3.11)", Target: "alpine:latest (alpine 3.11)",
Class: types.ClassOSPkg, Class: types.ClassVulnOSPkg,
Type: fos.Alpine, Type: fos.Alpine,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
@@ -111,7 +111,7 @@ func TestScanner_Scan(t *testing.T) {
}, },
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Type: ftypes.Bundler, Type: ftypes.Bundler,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
@@ -228,6 +228,25 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
},
{
Target: "/app/Gemfile.lock",
Class: types.ClassLangPkg,
Type: ftypes.Bundler,
Packages: []ftypes.Package{
{
Name: "rails",
Version: "4.0.2",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
},
},
},
{
Target: "alpine:latest (alpine 3.11)",
Class: types.ClassVulnOSPkg,
Type: fos.Alpine,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-9999", VulnerabilityID: "CVE-2020-9999",
@@ -248,17 +267,8 @@ func TestScanner_Scan(t *testing.T) {
}, },
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassLangPkg, Class: types.ClassVulnLangPkg,
Type: ftypes.Bundler, Type: ftypes.Bundler,
Packages: []ftypes.Package{
{
Name: "rails",
Version: "4.0.2",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
},
},
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2014-0081", VulnerabilityID: "CVE-2014-0081",
@@ -328,6 +338,8 @@ func TestScanner_Scan(t *testing.T) {
wantResults: types.Results{ wantResults: types.Results{
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassVulnLangPkg,
Type: "bundler",
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2014-0081", VulnerabilityID: "CVE-2014-0081",
@@ -350,8 +362,6 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
Class: types.ClassLangPkg,
Type: "bundler",
}, },
}, },
wantOS: &ftypes.OS{}, wantOS: &ftypes.OS{},
@@ -399,11 +409,13 @@ func TestScanner_Scan(t *testing.T) {
wantResults: types.Results{ wantResults: types.Results{
{ {
Target: "alpine:latest (alpine 3.11)", Target: "alpine:latest (alpine 3.11)",
Class: types.ClassOSPkg, Class: types.ClassVulnOSPkg,
Type: fos.Alpine, Type: fos.Alpine,
}, },
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassVulnLangPkg,
Type: ftypes.Bundler,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2014-0081", VulnerabilityID: "CVE-2014-0081",
@@ -426,8 +438,6 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
Class: types.ClassLangPkg,
Type: "bundler",
}, },
}, },
wantOS: &ftypes.OS{ wantOS: &ftypes.OS{
@@ -478,6 +488,8 @@ func TestScanner_Scan(t *testing.T) {
wantResults: types.Results{ wantResults: types.Results{
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassVulnLangPkg,
Type: ftypes.Bundler,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2014-0081", VulnerabilityID: "CVE-2014-0081",
@@ -500,8 +512,6 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
Class: types.ClassLangPkg,
Type: ftypes.Bundler,
}, },
}, },
wantOS: &ftypes.OS{ wantOS: &ftypes.OS{
@@ -535,7 +545,7 @@ func TestScanner_Scan(t *testing.T) {
wantOS: nil, wantOS: nil,
}, },
{ {
name: "happy path with only library detection", name: "happy path with only language-specific package detection",
args: args{ args: args{
target: "alpine:latest", target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"}, layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -597,6 +607,8 @@ func TestScanner_Scan(t *testing.T) {
wantResults: types.Results{ wantResults: types.Results{
{ {
Target: "/app/Gemfile.lock", Target: "/app/Gemfile.lock",
Class: types.ClassVulnLangPkg,
Type: ftypes.Bundler,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2014-0081", VulnerabilityID: "CVE-2014-0081",
@@ -619,11 +631,11 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
Class: types.ClassLangPkg,
Type: ftypes.Bundler,
}, },
{ {
Target: "/app/composer-lock.json", Target: "/app/composer-lock.json",
Class: types.ClassVulnLangPkg,
Type: ftypes.Composer,
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2021-21263", VulnerabilityID: "CVE-2021-21263",
@@ -635,8 +647,6 @@ func TestScanner_Scan(t *testing.T) {
}, },
}, },
}, },
Class: types.ClassLangPkg,
Type: ftypes.Composer,
}, },
}, },
wantOS: &ftypes.OS{ wantOS: &ftypes.OS{

View File

@@ -39,12 +39,14 @@ type Results []Result
type ResultClass string type ResultClass string
const ( const (
ClassOSPkg = "os-pkgs" ClassOSPkg = "os-pkgs" // For OS packages
ClassLangPkg = "lang-pkgs" ClassLangPkg = "lang-pkgs" // For language-specific packages
ClassConfig = "config" ClassVulnOSPkg = "vuln-os-pkgs" // For detected vulnerabilities in OS packages
ClassSecret = "secret" ClassVulnLangPkg = "vuln-lang-pkgs" // For detected vulnerabilities in language-specific packages
ClassLicense = "license" ClassConfig = "config" // For detected misconfigurations
ClassLicenseFile = "license-file" ClassSecret = "secret" // For detected secrets
ClassLicense = "license" // For detected package licenses
ClassLicenseFile = "license-file" // For detected licenses in files
ClassCustom = "custom" ClassCustom = "custom"
) )