diff --git a/docs/docs/sbom/cyclonedx.md b/docs/docs/sbom/cyclonedx.md index e703b602d6..18f3dfcfe2 100644 --- a/docs/docs/sbom/cyclonedx.md +++ b/docs/docs/sbom/cyclonedx.md @@ -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. +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 +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. ```
@@ -231,6 +239,12 @@ $ cat result.json | jq .
+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 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. @@ -258,5 +272,8 @@ Total: 3 (CRITICAL: 3) !!! 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. + The report is called [BOV][bov]. -[cyclonedx]: https://cyclonedx.org/ \ No newline at end of file +[cyclonedx]: https://cyclonedx.org/ +[sbom]: https://cyclonedx.org/capabilities/sbom/ +[bov]: https://cyclonedx.org/capabilities/bov/ diff --git a/docs/docs/sbom/index.md b/docs/docs/sbom/index.md index cf5a696574..caa51538aa 100644 --- a/docs/docs/sbom/index.md +++ b/docs/docs/sbom/index.md @@ -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`. ``` -$ 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 ``` diff --git a/integration/testdata/almalinux-8.json.golden b/integration/testdata/almalinux-8.json.golden index 22fd2acee9..301616ef7c 100644 --- a/integration/testdata/almalinux-8.json.golden +++ b/integration/testdata/almalinux-8.json.golden @@ -48,7 +48,7 @@ "Results": [ { "Target": "testdata/fixtures/images/almalinux-8.tar.gz (alma 8.5)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alma", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-310-registry.json.golden b/integration/testdata/alpine-310-registry.json.golden index ea9d38cebb..3c0b1b4e41 100644 --- a/integration/testdata/alpine-310-registry.json.golden +++ b/integration/testdata/alpine-310-registry.json.golden @@ -56,7 +56,7 @@ "Results": [ { "Target": "localhost:63577/alpine:3.10 (alpine 3.10.2)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-310.json.golden b/integration/testdata/alpine-310.json.golden index 3be3ef5228..929cd22344 100644 --- a/integration/testdata/alpine-310.json.golden +++ b/integration/testdata/alpine-310.json.golden @@ -50,7 +50,7 @@ "Results": [ { "Target": "testdata/fixtures/images/alpine-310.tar.gz (alpine 3.10.2)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-39-high-critical.json.golden b/integration/testdata/alpine-39-high-critical.json.golden index 9d201ded77..0d37ac28de 100644 --- a/integration/testdata/alpine-39-high-critical.json.golden +++ b/integration/testdata/alpine-39-high-critical.json.golden @@ -50,7 +50,7 @@ "Results": [ { "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-39-ignore-cveids.json.golden b/integration/testdata/alpine-39-ignore-cveids.json.golden index 0c827332a6..adafbd06cb 100644 --- a/integration/testdata/alpine-39-ignore-cveids.json.golden +++ b/integration/testdata/alpine-39-ignore-cveids.json.golden @@ -50,7 +50,7 @@ "Results": [ { "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-39.json.golden b/integration/testdata/alpine-39.json.golden index 2a79fe6ec8..a65b8a1c15 100644 --- a/integration/testdata/alpine-39.json.golden +++ b/integration/testdata/alpine-39.json.golden @@ -50,7 +50,7 @@ "Results": [ { "Target": "testdata/fixtures/images/alpine-39.tar.gz (alpine 3.9.4)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/alpine-distroless.json.golden b/integration/testdata/alpine-distroless.json.golden index 7e2b5826d6..32b908cca8 100644 --- a/integration/testdata/alpine-distroless.json.golden +++ b/integration/testdata/alpine-distroless.json.golden @@ -45,7 +45,7 @@ "Results": [ { "Target": "testdata/fixtures/images/alpine-distroless.tar.gz (alpine 3.16)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "alpine", "Vulnerabilities": [ { diff --git a/integration/testdata/amazon-1.json.golden b/integration/testdata/amazon-1.json.golden index 3dbc73fed4..3f818a84f6 100644 --- a/integration/testdata/amazon-1.json.golden +++ b/integration/testdata/amazon-1.json.golden @@ -49,7 +49,7 @@ "Results": [ { "Target": "testdata/fixtures/images/amazon-1.tar.gz (amazon AMI release 2018.03)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "amazon", "Vulnerabilities": [ { diff --git a/integration/testdata/amazon-2.json.golden b/integration/testdata/amazon-2.json.golden index 8a809c9daf..7bde5affcd 100644 --- a/integration/testdata/amazon-2.json.golden +++ b/integration/testdata/amazon-2.json.golden @@ -49,7 +49,7 @@ "Results": [ { "Target": "testdata/fixtures/images/amazon-2.tar.gz (amazon 2 (Karoo))", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "amazon", "Vulnerabilities": [ { diff --git a/integration/testdata/busybox-with-lockfile.json.golden b/integration/testdata/busybox-with-lockfile.json.golden index 6c07a60bae..e189d61e02 100644 --- a/integration/testdata/busybox-with-lockfile.json.golden +++ b/integration/testdata/busybox-with-lockfile.json.golden @@ -49,7 +49,7 @@ "Results": [ { "Target": "Cargo.lock", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "cargo", "Vulnerabilities": [ { diff --git a/integration/testdata/centos-6.json.golden b/integration/testdata/centos-6.json.golden index 287a56247f..4f8879b624 100644 --- a/integration/testdata/centos-6.json.golden +++ b/integration/testdata/centos-6.json.golden @@ -71,7 +71,7 @@ "Results": [ { "Target": "testdata/fixtures/images/centos-6.tar.gz (centos 6.10)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "centos", "Vulnerabilities": [ { diff --git a/integration/testdata/centos-7-ignore-unfixed.json.golden b/integration/testdata/centos-7-ignore-unfixed.json.golden index 75eb95d4ef..2449617198 100644 --- a/integration/testdata/centos-7-ignore-unfixed.json.golden +++ b/integration/testdata/centos-7-ignore-unfixed.json.golden @@ -61,7 +61,7 @@ "Results": [ { "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "centos", "Vulnerabilities": [ { diff --git a/integration/testdata/centos-7-medium.json.golden b/integration/testdata/centos-7-medium.json.golden index 06666c40a7..3e4aa85186 100644 --- a/integration/testdata/centos-7-medium.json.golden +++ b/integration/testdata/centos-7-medium.json.golden @@ -61,7 +61,7 @@ "Results": [ { "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "centos", "Vulnerabilities": [ { diff --git a/integration/testdata/centos-7.json.golden b/integration/testdata/centos-7.json.golden index f705fe7814..6de25677b9 100644 --- a/integration/testdata/centos-7.json.golden +++ b/integration/testdata/centos-7.json.golden @@ -61,7 +61,7 @@ "Results": [ { "Target": "testdata/fixtures/images/centos-7.tar.gz (centos 7.6.1810)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "centos", "Vulnerabilities": [ { diff --git a/integration/testdata/debian-buster-ignore-unfixed.json.golden b/integration/testdata/debian-buster-ignore-unfixed.json.golden index 20cba9ed33..04741eba06 100644 --- a/integration/testdata/debian-buster-ignore-unfixed.json.golden +++ b/integration/testdata/debian-buster-ignore-unfixed.json.golden @@ -49,7 +49,7 @@ "Results": [ { "Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { diff --git a/integration/testdata/debian-buster.json.golden b/integration/testdata/debian-buster.json.golden index bf33931bd5..31c3064a08 100644 --- a/integration/testdata/debian-buster.json.golden +++ b/integration/testdata/debian-buster.json.golden @@ -49,7 +49,7 @@ "Results": [ { "Target": "testdata/fixtures/images/debian-buster.tar.gz (debian 10.1)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { diff --git a/integration/testdata/debian-stretch.json.golden b/integration/testdata/debian-stretch.json.golden index 479ab069a9..e6f8cc383a 100644 --- a/integration/testdata/debian-stretch.json.golden +++ b/integration/testdata/debian-stretch.json.golden @@ -6,7 +6,7 @@ "OS": { "Family": "debian", "Name": "9.9", - "Eosl": true + "EOSL": true }, "ImageID": "sha256:f26939cc87ef44a6fc554eedd0a976ab30b5bc2769d65d2e986b6c5f1fd4053d", "DiffIDs": [ @@ -50,7 +50,7 @@ "Results": [ { "Target": "testdata/fixtures/images/debian-stretch.tar.gz (debian 9.9)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { diff --git a/integration/testdata/distroless-base.json.golden b/integration/testdata/distroless-base.json.golden index 8ded829529..a9493efa49 100644 --- a/integration/testdata/distroless-base.json.golden +++ b/integration/testdata/distroless-base.json.golden @@ -6,7 +6,7 @@ "OS": { "Family": "debian", "Name": "9.9", - "Eosl": true + "EOSL": true }, "ImageID": "sha256:7f04a8d247173b1f2546d22913af637bbab4e7411e00ae6207da8d94c445750d", "DiffIDs": [ @@ -48,7 +48,7 @@ "Results": [ { "Target": "testdata/fixtures/images/distroless-base.tar.gz (debian 9.9)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { diff --git a/integration/testdata/distroless-python27.json.golden b/integration/testdata/distroless-python27.json.golden index a2a26f26d3..322b6c1c1c 100644 --- a/integration/testdata/distroless-python27.json.golden +++ b/integration/testdata/distroless-python27.json.golden @@ -6,7 +6,7 @@ "OS": { "Family": "debian", "Name": "9.9", - "Eosl": true + "EOSL": true }, "ImageID": "sha256:6fcac2cc8a710f21577b5bbd534e0bfc841c0cca569b57182ba19054696cddda", "DiffIDs": [ @@ -65,7 +65,7 @@ "Results": [ { "Target": "testdata/fixtures/images/distroless-python27.tar.gz (debian 9.9)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { diff --git a/integration/testdata/fluentd-gems.json.golden b/integration/testdata/fluentd-gems.json.golden index 8ac3ecee59..bb08a1cf12 100644 --- a/integration/testdata/fluentd-gems.json.golden +++ b/integration/testdata/fluentd-gems.json.golden @@ -102,7 +102,7 @@ "Results": [ { "Target": "testdata/fixtures/images/fluentd-multiple-lockfiles.tar.gz (debian 10.2)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian", "Vulnerabilities": [ { @@ -165,7 +165,7 @@ }, { "Target": "Ruby", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "gemspec", "Vulnerabilities": [ { diff --git a/integration/testdata/gomod.json.golden b/integration/testdata/gomod.json.golden index d9ca71adc5..d5bb0d8ee1 100644 --- a/integration/testdata/gomod.json.golden +++ b/integration/testdata/gomod.json.golden @@ -17,7 +17,7 @@ "Results": [ { "Target": "go.mod", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "gomod", "Vulnerabilities": [ { @@ -103,7 +103,7 @@ }, { "Target": "submod/go.mod", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "gomod", "Vulnerabilities": [ { @@ -131,7 +131,7 @@ }, { "Target": "submod2/go.mod", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "gomod", "Vulnerabilities": [ { diff --git a/integration/testdata/mariner-1.0.json.golden b/integration/testdata/mariner-1.0.json.golden index 78c0488649..c8edd7b8ab 100644 --- a/integration/testdata/mariner-1.0.json.golden +++ b/integration/testdata/mariner-1.0.json.golden @@ -34,7 +34,7 @@ "Results": [ { "Target": "testdata/fixtures/images/mariner-1.0.tar.gz (cbl-mariner 1.0.20220122)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "cbl-mariner", "Vulnerabilities": [ { diff --git a/integration/testdata/nodejs.json.golden b/integration/testdata/nodejs.json.golden index 62989eeb0d..20c76b6741 100644 --- a/integration/testdata/nodejs.json.golden +++ b/integration/testdata/nodejs.json.golden @@ -17,7 +17,7 @@ "Results": [ { "Target": "package-lock.json", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "npm", "Vulnerabilities": [ { diff --git a/integration/testdata/opensuse-leap-151.json.golden b/integration/testdata/opensuse-leap-151.json.golden index a0d1146351..25564b7fa6 100644 --- a/integration/testdata/opensuse-leap-151.json.golden +++ b/integration/testdata/opensuse-leap-151.json.golden @@ -57,7 +57,7 @@ "Results": [ { "Target": "testdata/fixtures/images/opensuse-leap-151.tar.gz (opensuse.leap 15.1)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "opensuse.leap", "Vulnerabilities": [ { diff --git a/integration/testdata/oraclelinux-8.json.golden b/integration/testdata/oraclelinux-8.json.golden index 80c9057bb6..cfa2cbb3e8 100644 --- a/integration/testdata/oraclelinux-8.json.golden +++ b/integration/testdata/oraclelinux-8.json.golden @@ -58,7 +58,7 @@ "Results": [ { "Target": "testdata/fixtures/images/oraclelinux-8.tar.gz (oracle 8.0)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "oracle", "Vulnerabilities": [ { diff --git a/integration/testdata/photon-30.json.golden b/integration/testdata/photon-30.json.golden index 2747bd6317..b409fbb2d6 100644 --- a/integration/testdata/photon-30.json.golden +++ b/integration/testdata/photon-30.json.golden @@ -59,7 +59,7 @@ "Results": [ { "Target": "testdata/fixtures/images/photon-30.tar.gz (photon 3.0)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "photon", "Vulnerabilities": [ { diff --git a/integration/testdata/pip.json.golden b/integration/testdata/pip.json.golden index 38ff825487..3244e5fde1 100644 --- a/integration/testdata/pip.json.golden +++ b/integration/testdata/pip.json.golden @@ -55,7 +55,12 @@ "Version": "2.0.0", "Layer": {} } - ], + ] + }, + { + "Target": "requirements.txt", + "Class": "vuln-lang-pkgs", + "Type": "pip", "Vulnerabilities": [ { "VulnerabilityID": "CVE-2019-14806", diff --git a/integration/testdata/pnpm.json.golden b/integration/testdata/pnpm.json.golden index 5c83c2aa48..56e98fd454 100644 --- a/integration/testdata/pnpm.json.golden +++ b/integration/testdata/pnpm.json.golden @@ -5,7 +5,7 @@ "Results": [ { "Target": "pnpm-lock.yaml", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "pnpm", "Vulnerabilities": [ { diff --git a/integration/testdata/pom.json.golden b/integration/testdata/pom.json.golden index 724d51263f..4330219c2c 100644 --- a/integration/testdata/pom.json.golden +++ b/integration/testdata/pom.json.golden @@ -17,7 +17,7 @@ "Results": [ { "Target": "pom.xml", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "pom", "Vulnerabilities": [ { diff --git a/integration/testdata/rockylinux-8.json.golden b/integration/testdata/rockylinux-8.json.golden index 1e491a5336..72785aa710 100644 --- a/integration/testdata/rockylinux-8.json.golden +++ b/integration/testdata/rockylinux-8.json.golden @@ -48,7 +48,7 @@ "Results": [ { "Target": "testdata/fixtures/images/rockylinux-8.tar.gz (rocky 8.5)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "rocky", "Vulnerabilities": [ { @@ -118,11 +118,6 @@ "LastModifiedDate": "2022-01-06T09:15:00Z" } ] - }, - { - "Target": "Python", - "Class": "lang-pkgs", - "Type": "python-pkg" } ] } diff --git a/integration/testdata/secrets.json.golden b/integration/testdata/secrets.json.golden index 52302fb4f9..eed11e72d9 100644 --- a/integration/testdata/secrets.json.golden +++ b/integration/testdata/secrets.json.golden @@ -26,51 +26,49 @@ "Title": "AWS Access Key ID", "StartLine": 3, "EndLine": 3, - "Match": "export AWS_ACCESS_KEY_ID=********************", - "Code" : { - "Lines": [ - { - "Number": 1, - "Content": "#!/bin/sh", - "IsCause": false, - "Annotation": "", - "Truncated": false, - "Highlighted": "#!/bin/sh", - "FirstCause": false, - "LastCause": false - }, - { - "Number": 2, - "Content": "", - "IsCause": false, - "Annotation": "", - "Truncated": false, - "Highlighted": "", - "FirstCause": false, - "LastCause": false - }, - { - "Number": 3, - "Content": "export AWS_ACCESS_KEY_ID=********************", - "IsCause": true, - "Annotation": "", - "Truncated": false, - "Highlighted": "export AWS_ACCESS_KEY_ID=********************", - "FirstCause": true, - "LastCause": true - }, - { - "Number": 4, - "Content": "", - "IsCause": false, - "Annotation": "", - "Truncated": false, - "Highlighted": "", - "FirstCause": false, - "LastCause": false - } - ] - } + "Code": { + "Lines": [ + { + "Number": 1, + "Content": "#!/bin/sh", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "Highlighted": "#!/bin/sh", + "FirstCause": false, + "LastCause": false + }, + { + "Number": 2, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 3, + "Content": "export AWS_ACCESS_KEY_ID=********************", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "Highlighted": "export AWS_ACCESS_KEY_ID=********************", + "FirstCause": true, + "LastCause": true + }, + { + "Number": 4, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + } + ] + }, + "Match": "export AWS_ACCESS_KEY_ID=********************" }, { "RuleID": "mysecret", @@ -79,10 +77,9 @@ "Title": "My Secret", "StartLine": 7, "EndLine": 7, - "Match": "echo ********", - "Code" : { - "Lines": [ - { + "Code": { + "Lines": [ + { "Number": 5, "Content": "export GITHUB_PAT=ghp_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", "IsCause": false, @@ -91,29 +88,29 @@ "Highlighted": "export GITHUB_PAT=ghp_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", "FirstCause": false, "LastCause": false - }, - { + }, + { "Number": 6, "Content": "", "IsCause": false, "Annotation": "", "Truncated": false, - "Highlighted": "", "FirstCause": false, "LastCause": false - }, - { - "Number": 7, - "Content": "echo ********", - "IsCause": true, - "Annotation": "", - "Truncated": false, - "Highlighted": "echo ********", - "FirstCause": true, - "LastCause": true - } - ] - } + }, + { + "Number": 7, + "Content": "echo ********", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "Highlighted": "echo ********", + "FirstCause": true, + "LastCause": true + } + ] + }, + "Match": "echo ********" } ] } diff --git a/integration/testdata/spring4shell-jre11.json.golden b/integration/testdata/spring4shell-jre11.json.golden index 1f2911dbbe..cfc3da1af7 100644 --- a/integration/testdata/spring4shell-jre11.json.golden +++ b/integration/testdata/spring4shell-jre11.json.golden @@ -185,12 +185,12 @@ "Results": [ { "Target": "testdata/fixtures/images/spring4shell-jre11.tar.gz (debian 11.3)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian" }, { "Target": "Java", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "jar", "Vulnerabilities": [ { diff --git a/integration/testdata/spring4shell-jre8.json.golden b/integration/testdata/spring4shell-jre8.json.golden index aaf906adc1..c2be638a69 100644 --- a/integration/testdata/spring4shell-jre8.json.golden +++ b/integration/testdata/spring4shell-jre8.json.golden @@ -185,12 +185,12 @@ "Results": [ { "Target": "testdata/fixtures/images/spring4shell-jre8.tar.gz (debian 11.3)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "debian" }, { "Target": "Java", - "Class": "lang-pkgs", + "Class": "vuln-lang-pkgs", "Type": "jar", "Vulnerabilities": [ { diff --git a/integration/testdata/ubi-7.json.golden b/integration/testdata/ubi-7.json.golden index 2f34f82170..b33369755a 100644 --- a/integration/testdata/ubi-7.json.golden +++ b/integration/testdata/ubi-7.json.golden @@ -72,7 +72,7 @@ "Results": [ { "Target": "testdata/fixtures/images/ubi-7.tar.gz (redhat 7.7)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "redhat", "Vulnerabilities": [ { diff --git a/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden b/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden index d228b69e1a..bdbdb45ad1 100644 --- a/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden +++ b/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden @@ -67,7 +67,7 @@ "Results": [ { "Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "ubuntu", "Vulnerabilities": [ { diff --git a/integration/testdata/ubuntu-1804.json.golden b/integration/testdata/ubuntu-1804.json.golden index b9a47ea39a..40334bd5d1 100644 --- a/integration/testdata/ubuntu-1804.json.golden +++ b/integration/testdata/ubuntu-1804.json.golden @@ -67,7 +67,7 @@ "Results": [ { "Target": "testdata/fixtures/images/ubuntu-1804.tar.gz (ubuntu 18.04)", - "Class": "os-pkgs", + "Class": "vuln-os-pkgs", "Type": "ubuntu", "Vulnerabilities": [ { diff --git a/pkg/commands/app.go b/pkg/commands/app.go index b93e26284f..08a28014cc 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -240,7 +240,7 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { $ trivy image --format json --output result.json alpine:3.15 # 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. // cmd.Args -> cannot validate args here diff --git a/pkg/flag/options.go b/pkg/flag/options.go index d0de7e26c8..9bd842c218 100644 --- a/pkg/flag/options.go +++ b/pkg/flag/options.go @@ -12,6 +12,8 @@ import ( "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/report" ) type Flag struct { @@ -83,6 +85,20 @@ type Options struct { 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) { if flag == nil || flag.Name == "" { return @@ -337,6 +353,8 @@ func (f *Flags) ToOptions(appVersion string, args []string, globalFlags *GlobalF opts.VulnerabilityOptions = f.VulnerabilityFlagGroup.ToOptions() } + opts.Align() + return opts, nil } diff --git a/pkg/report/github/github.go b/pkg/report/github/github.go index 4154200edf..4b17c6aba7 100644 --- a/pkg/report/github/github.go +++ b/pkg/report/github/github.go @@ -102,7 +102,7 @@ func (w Writer) Write(report types.Report) error { manifest := Manifest{} manifest.Name = result.Type - //show path for languages only + // show path for language-specific packages only if result.Class == types.ClassLangPkg { manifest.File = &File{ SrcLocation: result.Target, diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index f4525790da..b57457e13c 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -187,9 +187,9 @@ func (sw SarifWriter) Write(report types.Report) error { func toSarifRuleName(class string) string { switch class { - case types.ClassOSPkg: + case types.ClassVulnOSPkg: return sarifOsPackageVulnerability - case types.ClassLangPkg: + case types.ClassVulnLangPkg: return sarifLanguageSpecificVulnerability case types.ClassConfig: return sarifConfigFiles diff --git a/pkg/report/sarif_test.go b/pkg/report/sarif_test.go index 07e51d7243..1591d49864 100644 --- a/pkg/report/sarif_test.go +++ b/pkg/report/sarif_test.go @@ -30,7 +30,7 @@ func TestReportWriter_Sarif(t *testing.T) { input: types.Results{ { Target: "library/test", - Class: types.ClassOSPkg, + Class: types.ClassVulnOSPkg, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2020-0001", diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index e8c464f9aa..20df75036f 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -13,6 +13,7 @@ import ( "github.com/aquasecurity/table" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" ) @@ -52,17 +53,25 @@ type Renderer interface { // Write writes the result on standard output 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 { // Not display a table of custom resources if result.Class == types.ClassCustom { continue } - tw.write(result) + tw.write(result, pkgs) } 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 { return } @@ -70,8 +79,8 @@ func (tw Writer) write(result types.Result) { var renderer Renderer switch { // vulnerability - case result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg: - renderer = NewVulnerabilityRenderer(result, tw.isOutputToTerminal(), tw.Tree, tw.Severities) + case result.Class == types.ClassVulnOSPkg || result.Class == types.ClassVulnLangPkg: + renderer = NewVulnerabilityRenderer(result, pkgs, tw.isOutputToTerminal(), tw.Tree, tw.Severities) // misconfiguration case result.Class == types.ClassConfig: renderer = NewMisconfigRenderer(result, tw.Severities, tw.Trace, tw.IncludeNonFailures, tw.isOutputToTerminal()) diff --git a/pkg/report/table/table_test.go b/pkg/report/table/table_test.go index 3da3c56fae..eca131d4d5 100644 --- a/pkg/report/table/table_test.go +++ b/pkg/report/table/table_test.go @@ -24,7 +24,7 @@ func TestReportWriter_Table(t *testing.T) { results: types.Results{ { Target: "test", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2020-0001", @@ -59,7 +59,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1) results: types.Results{ { Target: "test", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2020-0001", @@ -95,7 +95,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1) results: types.Results{ { Target: "test", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2020-0001", @@ -127,7 +127,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1) results: types.Results{ { Target: "test", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2020-1234", @@ -166,7 +166,7 @@ Total: 1 (MEDIUM: 0, HIGH: 1) results: types.Results{ { Target: "package-lock.json", - Class: "lang-pkgs", + Class: types.ClassLangPkg, Type: "npm", 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{ { VulnerabilityID: "CVE-2022-0235", @@ -256,7 +261,7 @@ package-lock.json t.Run(tc.name, func(t *testing.T) { tableWritten := bytes.Buffer{} err := report.Write(types.Report{Results: tc.results}, report.Option{ - Format: "table", + Format: report.FormatTable, Output: &tableWritten, Tree: true, IncludeNonFailures: tc.includeNonFailures, diff --git a/pkg/report/table/vulnerability.go b/pkg/report/table/vulnerability.go index e2f5003859..ed9b0bb914 100644 --- a/pkg/report/table/vulnerability.go +++ b/pkg/report/table/vulnerability.go @@ -23,13 +23,14 @@ type vulnerabilityRenderer struct { w *bytes.Buffer tableWriter *table.Table result types.Result + pkgs map[string][]ftypes.Package isTerminal bool tree bool severities []dbTypes.Severity 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{}) if !isTerminal { tml.DisableFormatting() @@ -38,6 +39,7 @@ func NewVulnerabilityRenderer(result types.Result, isTerminal, tree bool, severi w: buf, tableWriter: newTableWriter(buf, isTerminal), result: result, + pkgs: pkgs, isTerminal: isTerminal, tree: tree, severities: severities, @@ -53,7 +55,7 @@ func (r vulnerabilityRenderer) Render() string { total, summaries := summarize(r.severities, severityCount) target := r.result.Target - if r.result.Class != types.ClassOSPkg { + if r.result.Class == types.ClassVulnLangPkg { target += fmt.Sprintf(" (%s)", r.result.Type) } renderTarget(r.w, target, r.isTerminal) @@ -128,8 +130,14 @@ func (r vulnerabilityRenderer) countSeverities(vulns []types.DetectedVulnerabili } func (r vulnerabilityRenderer) renderDependencyTree() { + // Take packages + pkgs, ok := r.pkgs[r.result.Target] + if !ok { + return + } + // Get parents of each dependency - parents := reverseDeps(r.result.Packages) + parents := reverseDeps(pkgs) if len(parents) == 0 { return } diff --git a/pkg/sbom/cyclonedx/marshal.go b/pkg/sbom/cyclonedx/marshal.go index c71f9695e2..9a24a9c623 100644 --- a/pkg/sbom/cyclonedx/marshal.go +++ b/pkg/sbom/cyclonedx/marshal.go @@ -187,16 +187,17 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com var metadataDependencies []cdx.Dependency libraryUniqMap := map[string]struct{}{} vulnMap := map[string]cdx.Vulnerability{} + bomRefMap := map[string]string{} for _, result := range r.Results { var componentDependencies []cdx.Dependency - bomRefMap := map[string]string{} for _, pkg := range result.Packages { pkgComponent, err := pkgToCdxComponent(result.Type, r.Metadata, pkg) if err != nil { return nil, nil, nil, xerrors.Errorf("failed to parse pkg: %w", err) } - if _, ok := bomRefMap[pkg.Name+utils.FormatVersion(pkg)+pkg.FilePath]; !ok { - bomRefMap[pkg.Name+utils.FormatVersion(pkg)+pkg.FilePath] = pkgComponent.BOMRef + pkgID := packageID(result.Target, pkg.Name, utils.FormatVersion(pkg), pkg.FilePath) + if _, ok := bomRefMap[pkgID]; !ok { + bomRefMap[pkgID] = pkgComponent.BOMRef } // 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}) } + for _, vuln := range result.Vulnerabilities { // 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 a vulnerability depends on multiple packages, // 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 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. // e.g. // 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 } +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 { v := cdx.Vulnerability{ ID: vuln.VulnerabilityID, diff --git a/pkg/sbom/cyclonedx/marshal_test.go b/pkg/sbom/cyclonedx/marshal_test.go index 4ae980b481..89c95899f3 100644 --- a/pkg/sbom/cyclonedx/marshal_test.go +++ b/pkg/sbom/cyclonedx/marshal_test.go @@ -70,6 +70,11 @@ func TestMarshaler_Marshal(t *testing.T) { Licenses: []string{"GPLv3+"}, }, }, + }, + { + Target: "rails:latest (centos 8.3.2011)", + Class: types.ClassVulnOSPkg, + Type: fos.CentOS, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2018-20623", @@ -470,6 +475,11 @@ func TestMarshaler_Marshal(t *testing.T) { FilePath: "tools/project-doe/specifications/actionpack.gemspec", }, }, + }, + { + Target: "Ruby", + Class: types.ClassVulnOSPkg, + Type: ftypes.GemSpec, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2022-23633", @@ -1105,6 +1115,11 @@ func TestMarshaler_MarshalVulnerabilities(t *testing.T) { Licenses: []string{"GPLv3+"}, }, }, + }, + { + Target: "rails:latest (centos 8.3.2011)", + Class: types.ClassVulnOSPkg, + Type: fos.CentOS, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2018-20623", diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go index 78568067ca..efa3b15567 100644 --- a/pkg/scanner/local/scan.go +++ b/pkg/scanner/local/scan.go @@ -98,10 +98,18 @@ func (s Scanner) Scan(ctx context.Context, target, artifactKey string, blobKeys var eosl bool 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) { var vulnResults types.Results - vulnResults, eosl, err = s.checkVulnerabilities(target, artifactDetail, options) + vulnResults, eosl, err = s.scanVulnerabilities(target, artifactDetail, options) if err != nil { 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...) } + // 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 if len(artifactDetail.CustomResources) != 0 { 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 { // Fill vulnerability details 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 } -func shouldScanMisconfig(securityChecks []string) bool { - return slices.Contains(securityChecks, types.SecurityCheckConfig) || slices.Contains(securityChecks, types.SecurityCheckRbac) +func (s Scanner) osPkgsToResult(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) *types.Result { + 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) { var eosl bool var results types.Results @@ -171,7 +219,7 @@ func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetai } if slices.Contains(options.VulnType, types.VulnTypeLibrary) { - libResults, err := s.scanLibrary(detail.Applications, options) + libResults, err := s.scanLangPkgs(detail.Applications) if err != nil { 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) ( *types.Result, bool, error) { - if detail.OS == nil { + if detail.OS == nil || detail.OS.Family == "" { log.Logger.Debug("Detected OS: unknown") return nil, false, nil } @@ -194,45 +242,24 @@ func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options pkgs = mergePkgs(pkgs, detail.HistoryPackages) } - result, eosl, err := s.detectVulnsInOSPkgs(target, detail.OS.Family, detail.OS.Name, detail.Repository, 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) + vulns, eosl, err := s.ospkgDetector.Detect("", detail.OS.Family, detail.OS.Name, detail.Repository, time.Time{}, pkgs) if err == ospkgDetector.ErrUnsupportedOS { return nil, false, nil } else if err != nil { 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{ Target: artifactDetail, Vulnerabilities: vulns, - Class: types.ClassOSPkg, - Type: osFamily, + Class: types.ClassVulnOSPkg, + Type: detail.OS.Family, } 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)) if len(apps) == 0 { 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) if err != nil { return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err) + } else if len(vulns) == 0 { + return nil, nil } target := app.FilePath @@ -263,16 +292,12 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption target = t } - libReport := types.Result{ + results = append(results, types.Result{ Target: target, Vulnerabilities: vulns, - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Type: app.Type, - } - if options.ListAllPackages { - libReport.Packages = app.Libraries - } - results = append(results, libReport) + }) } sort.Slice(results, func(i, j int) bool { return results[i].Target < results[j].Target @@ -475,3 +500,7 @@ func mergePkgs(pkgs, pkgsFromCommands []ftypes.Package) []ftypes.Package { } return pkgs } + +func shouldScanMisconfig(securityChecks []string) bool { + return slices.Contains(securityChecks, types.SecurityCheckConfig) || slices.Contains(securityChecks, types.SecurityCheckRbac) +} diff --git a/pkg/scanner/local/scan_test.go b/pkg/scanner/local/scan_test.go index eaf7370201..9b1024b9f0 100644 --- a/pkg/scanner/local/scan_test.go +++ b/pkg/scanner/local/scan_test.go @@ -89,7 +89,7 @@ func TestScanner_Scan(t *testing.T) { wantResults: types.Results{ { Target: "alpine:latest (alpine 3.11)", - Class: types.ClassOSPkg, + Class: types.ClassVulnOSPkg, Type: fos.Alpine, Vulnerabilities: []types.DetectedVulnerability{ { @@ -111,7 +111,7 @@ func TestScanner_Scan(t *testing.T) { }, { Target: "/app/Gemfile.lock", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Type: ftypes.Bundler, 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{ { VulnerabilityID: "CVE-2020-9999", @@ -248,17 +267,8 @@ func TestScanner_Scan(t *testing.T) { }, { Target: "/app/Gemfile.lock", - Class: types.ClassLangPkg, + Class: types.ClassVulnLangPkg, Type: ftypes.Bundler, - Packages: []ftypes.Package{ - { - Name: "rails", - Version: "4.0.2", - Layer: ftypes.Layer{ - DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33", - }, - }, - }, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2014-0081", @@ -328,6 +338,8 @@ func TestScanner_Scan(t *testing.T) { wantResults: types.Results{ { Target: "/app/Gemfile.lock", + Class: types.ClassVulnLangPkg, + Type: "bundler", Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2014-0081", @@ -350,8 +362,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Class: types.ClassLangPkg, - Type: "bundler", }, }, wantOS: &ftypes.OS{}, @@ -399,11 +409,13 @@ func TestScanner_Scan(t *testing.T) { wantResults: types.Results{ { Target: "alpine:latest (alpine 3.11)", - Class: types.ClassOSPkg, + Class: types.ClassVulnOSPkg, Type: fos.Alpine, }, { Target: "/app/Gemfile.lock", + Class: types.ClassVulnLangPkg, + Type: ftypes.Bundler, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2014-0081", @@ -426,8 +438,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Class: types.ClassLangPkg, - Type: "bundler", }, }, wantOS: &ftypes.OS{ @@ -478,6 +488,8 @@ func TestScanner_Scan(t *testing.T) { wantResults: types.Results{ { Target: "/app/Gemfile.lock", + Class: types.ClassVulnLangPkg, + Type: ftypes.Bundler, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2014-0081", @@ -500,8 +512,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Class: types.ClassLangPkg, - Type: ftypes.Bundler, }, }, wantOS: &ftypes.OS{ @@ -535,7 +545,7 @@ func TestScanner_Scan(t *testing.T) { wantOS: nil, }, { - name: "happy path with only library detection", + name: "happy path with only language-specific package detection", args: args{ target: "alpine:latest", layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"}, @@ -597,6 +607,8 @@ func TestScanner_Scan(t *testing.T) { wantResults: types.Results{ { Target: "/app/Gemfile.lock", + Class: types.ClassVulnLangPkg, + Type: ftypes.Bundler, Vulnerabilities: []types.DetectedVulnerability{ { 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", + Class: types.ClassVulnLangPkg, + Type: ftypes.Composer, Vulnerabilities: []types.DetectedVulnerability{ { VulnerabilityID: "CVE-2021-21263", @@ -635,8 +647,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Class: types.ClassLangPkg, - Type: ftypes.Composer, }, }, wantOS: &ftypes.OS{ diff --git a/pkg/types/report.go b/pkg/types/report.go index f53aed3838..861465a6ae 100644 --- a/pkg/types/report.go +++ b/pkg/types/report.go @@ -39,12 +39,14 @@ type Results []Result type ResultClass string const ( - ClassOSPkg = "os-pkgs" - ClassLangPkg = "lang-pkgs" - ClassConfig = "config" - ClassSecret = "secret" - ClassLicense = "license" - ClassLicenseFile = "license-file" + ClassOSPkg = "os-pkgs" // For OS packages + ClassLangPkg = "lang-pkgs" // For language-specific packages + ClassVulnOSPkg = "vuln-os-pkgs" // For detected vulnerabilities in OS packages + ClassVulnLangPkg = "vuln-lang-pkgs" // For detected vulnerabilities in language-specific packages + ClassConfig = "config" // For detected misconfigurations + ClassSecret = "secret" // For detected secrets + ClassLicense = "license" // For detected package licenses + ClassLicenseFile = "license-file" // For detected licenses in files ClassCustom = "custom" )