mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 23:26:39 -08:00
BREAKING: add new classes for vulnerabilities (#2541)
This commit is contained in:
@@ -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.
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -231,6 +239,12 @@ $ cat result.json | jq .
|
||||
|
||||
</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
|
||||
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/
|
||||
[cyclonedx]: https://cyclonedx.org/
|
||||
[sbom]: https://cyclonedx.org/capabilities/sbom/
|
||||
[bov]: https://cyclonedx.org/capabilities/bov/
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
2
integration/testdata/almalinux-8.json.golden
vendored
2
integration/testdata/almalinux-8.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/alpine-310.json.golden
vendored
2
integration/testdata/alpine-310.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/alpine-39.json.golden
vendored
2
integration/testdata/alpine-39.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/amazon-1.json.golden
vendored
2
integration/testdata/amazon-1.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/amazon-2.json.golden
vendored
2
integration/testdata/amazon-2.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"Results": [
|
||||
{
|
||||
"Target": "Cargo.lock",
|
||||
"Class": "lang-pkgs",
|
||||
"Class": "vuln-lang-pkgs",
|
||||
"Type": "cargo",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
|
||||
2
integration/testdata/centos-6.json.golden
vendored
2
integration/testdata/centos-6.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/centos-7.json.golden
vendored
2
integration/testdata/centos-7.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
6
integration/testdata/gomod.json.golden
vendored
6
integration/testdata/gomod.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/mariner-1.0.json.golden
vendored
2
integration/testdata/mariner-1.0.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/nodejs.json.golden
vendored
2
integration/testdata/nodejs.json.golden
vendored
@@ -17,7 +17,7 @@
|
||||
"Results": [
|
||||
{
|
||||
"Target": "package-lock.json",
|
||||
"Class": "lang-pkgs",
|
||||
"Class": "vuln-lang-pkgs",
|
||||
"Type": "npm",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/photon-30.json.golden
vendored
2
integration/testdata/photon-30.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
7
integration/testdata/pip.json.golden
vendored
7
integration/testdata/pip.json.golden
vendored
@@ -55,7 +55,12 @@
|
||||
"Version": "2.0.0",
|
||||
"Layer": {}
|
||||
}
|
||||
],
|
||||
]
|
||||
},
|
||||
{
|
||||
"Target": "requirements.txt",
|
||||
"Class": "vuln-lang-pkgs",
|
||||
"Type": "pip",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-14806",
|
||||
|
||||
2
integration/testdata/pnpm.json.golden
vendored
2
integration/testdata/pnpm.json.golden
vendored
@@ -5,7 +5,7 @@
|
||||
"Results": [
|
||||
{
|
||||
"Target": "pnpm-lock.yaml",
|
||||
"Class": "lang-pkgs",
|
||||
"Class": "vuln-lang-pkgs",
|
||||
"Type": "pnpm",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
|
||||
2
integration/testdata/pom.json.golden
vendored
2
integration/testdata/pom.json.golden
vendored
@@ -17,7 +17,7 @@
|
||||
"Results": [
|
||||
{
|
||||
"Target": "pom.xml",
|
||||
"Class": "lang-pkgs",
|
||||
"Class": "vuln-lang-pkgs",
|
||||
"Type": "pom",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
127
integration/testdata/secrets.json.golden
vendored
127
integration/testdata/secrets.json.golden
vendored
@@ -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 ********"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/ubi-7.json.golden
vendored
2
integration/testdata/ubi-7.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
2
integration/testdata/ubuntu-1804.json.golden
vendored
2
integration/testdata/ubuntu-1804.json.golden
vendored
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user