feat(vuln): show suppressed vulnerabilities in table (#6084)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com>
This commit is contained in:
Teppei Fukuda
2024-02-13 16:35:06 +04:00
committed by GitHub
parent c107e1af29
commit 3c1601b6cb
59 changed files with 1572 additions and 1275 deletions

View File

@@ -1,81 +1,34 @@
# Filtering
Trivy provides various methods for filtering the results.
```mermaid
flowchart LR
Issues("Detected\nIssues") --> Severity
## By Status
| Scanner | Supported |
|:----------------:|:---------:|
| Vulnerability | ✓ |
| Misconfiguration | |
| Secret | |
| License | |
Trivy supports the following vulnerability statuses:
- `unknown`
- `not_affected`: this package is not affected by this vulnerability on this platform
- `affected`: this package is affected by this vulnerability on this platform, but there is no patch released yet
- `fixed`: this vulnerability is fixed on this platform
- `under_investigation`: it is currently unknown whether or not this vulnerability affects this package on this platform, and it is under investigation
- `will_not_fix`: this package is affected by this vulnerability on this platform, but there is currently no intention to fix it (this would primarily be for flaws that are of Low or Moderate impact that pose no significant risk to customers)
- `fix_deferred`: this package is affected by this vulnerability on this platform, and may be fixed in the future
- `end_of_life`: this package has been identified to contain the impacted component, but analysis to determine whether it is affected or not by this vulnerability was not performed
Note that vulnerabilities with the `unknown`, `not_affected` or `under_investigation` status are not detected.
These are only defined for comprehensiveness, and you will not have the opportunity to specify these statuses.
Some statuses are supported in limited distributions.
| OS | Fixed | Affected | Under Investigation | Will Not Fix | Fix Deferred | End of Life |
|:----------:|:-----:|:--------:|:-------------------:|:------------:|:------------:|:-----------:|
| Debian | ✓ | ✓ | | | ✓ | ✓ |
| RHEL | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Other OSes | ✓ | ✓ | | | | |
To ignore vulnerabilities with specific statuses, use the `--ignore-status <list_of_statuses>` option.
```bash
$ trivy image --ignore-status affected,fixed ruby:2.4.0
subgraph Filtering
subgraph Prioritization
direction TB
Severity("By Severity") --> Status("By Status")
end
subgraph Suppression
Status --> Ignore("By Finding IDs")
Ignore --> Rego("By Rego")
Rego --> VEX("By VEX")
end
end
VEX --> Results
```
<details>
<summary>Result</summary>
Similar to the functionality of filtering results, you can also limit the sub-targets for each scanner.
For information on these settings, please refer to the scanner-specific documentation ([vulnerability](../scanner/vulnerability.md) , [misconfiguration](../scanner/misconfiguration/index.md), etc.).
```
2019-05-16T12:50:14.786+0900 INFO Detecting Debian vulnerabilities...
## Prioritization
You can filter the results by
ruby:2.4.0 (debian 8.7)
=======================
Total: 527 (UNKNOWN: 0, LOW: 276, MEDIUM: 83, HIGH: 158, CRITICAL: 10)
- [Severity](#by-severity)
- [Status](#by-status)
┌─────────────────────────────┬──────────────────┬──────────┬──────────────┬────────────────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────────────────────────┼──────────────────┼──────────┼──────────────┼────────────────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ binutils │ CVE-2014-9939 │ CRITICAL │ will_not_fix │ 2.25-5 │ │ binutils: buffer overflow in ihex.c │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2014-9939 │
│ ├──────────────────┤ │ │ ├───────────────┼──────────────────────────────────────────────────────────────┤
│ │ CVE-2017-6969 │ │ │ │ │ binutils: Heap-based buffer over-read in readelf when │
│ │ │ │ │ │ │ processing corrupt RL78 binaries │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2017-6969 │
│ ├──────────────────┤ │ │ ├───────────────┼──────────────────────────────────────────────────────────────┤
...
```
</details>
!!! tip
To skip all unfixed vulnerabilities, you can use the `--ignore-unfixed` flag .
It is a shorthand of `--ignore-status affected,will_not_fix,fix_deferred,end_of_life`.
It displays "fixed" vulnerabilities only.
```bash
$ trivy image --ignore-unfixed ruby:2.4.0
```
## By Severity
### By Severity
| Scanner | Supported |
|:----------------:|:---------:|
@@ -202,11 +155,122 @@ See https://avd.aquasec.com/misconfig/avd-aws-0081
```
</details>
## By Finding IDs
### By Status
| Scanner | Supported |
|:----------------:|:---------:|
| Vulnerability | ✓ |
| Misconfiguration | |
| Secret | |
| License | |
Trivy supports the following vulnerability statuses:
- `unknown`
- `not_affected`: this package is not affected by this vulnerability on this platform
- `affected`: this package is affected by this vulnerability on this platform, but there is no patch released yet
- `fixed`: this vulnerability is fixed on this platform
- `under_investigation`: it is currently unknown whether or not this vulnerability affects this package on this platform, and it is under investigation
- `will_not_fix`: this package is affected by this vulnerability on this platform, but there is currently no intention to fix it (this would primarily be for flaws that are of Low or Moderate impact that pose no significant risk to customers)
- `fix_deferred`: this package is affected by this vulnerability on this platform, and may be fixed in the future
- `end_of_life`: this package has been identified to contain the impacted component, but analysis to determine whether it is affected or not by this vulnerability was not performed
Note that vulnerabilities with the `unknown`, `not_affected` or `under_investigation` status are not detected.
These are only defined for comprehensiveness, and you will not have the opportunity to specify these statuses.
Some statuses are supported in limited distributions.
| OS | Fixed | Affected | Under Investigation | Will Not Fix | Fix Deferred | End of Life |
|:----------:|:-----:|:--------:|:-------------------:|:------------:|:------------:|:-----------:|
| Debian | ✓ | ✓ | | | ✓ | ✓ |
| RHEL | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Other OSes | ✓ | ✓ | | | | |
To ignore vulnerabilities with specific statuses, use the `--ignore-status <list_of_statuses>` option.
```bash
$ trivy image --ignore-status affected,fixed ruby:2.4.0
```
<details>
<summary>Result</summary>
```
2019-05-16T12:50:14.786+0900 INFO Detecting Debian vulnerabilities...
ruby:2.4.0 (debian 8.7)
=======================
Total: 527 (UNKNOWN: 0, LOW: 276, MEDIUM: 83, HIGH: 158, CRITICAL: 10)
┌─────────────────────────────┬──────────────────┬──────────┬──────────────┬────────────────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────────────────────────┼──────────────────┼──────────┼──────────────┼────────────────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ binutils │ CVE-2014-9939 │ CRITICAL │ will_not_fix │ 2.25-5 │ │ binutils: buffer overflow in ihex.c │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2014-9939 │
│ ├──────────────────┤ │ │ ├───────────────┼──────────────────────────────────────────────────────────────┤
│ │ CVE-2017-6969 │ │ │ │ │ binutils: Heap-based buffer over-read in readelf when │
│ │ │ │ │ │ │ processing corrupt RL78 binaries │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2017-6969 │
│ ├──────────────────┤ │ │ ├───────────────┼──────────────────────────────────────────────────────────────┤
...
```
</details>
!!! tip
To skip all unfixed vulnerabilities, you can use the `--ignore-unfixed` flag .
It is a shorthand of `--ignore-status affected,will_not_fix,fix_deferred,end_of_life`.
It displays "fixed" vulnerabilities only.
```bash
$ trivy image --ignore-unfixed ruby:2.4.0
```
## Suppression
You can filter the results by
- [Finding IDs](#by-finding-ids)
- [Rego](#by-rego)
- [Vulnerability Exploitability Exchange (VEX)](#by-vulnerability-exploitability-exchange-vex)
To show the suppressed results, use the `--show-suppressed` flag.
```bash
$ trivy image --vex debian11.csaf.vex --ignorefile .trivyignore.yaml --show-suppressed debian:11
...
Suppressed Vulnerabilities (Total: 9)
┌───────────────┬───────────────┬──────────┬──────────────┬─────────────────────────────────────────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Statement │ Source │
├───────────────┼───────────────┼──────────┼──────────────┼─────────────────────────────────────────────┼───────────────────┤
│ libdb5.3 │ CVE-2019-8457 │ CRITICAL │ not_affected │ vulnerable_code_not_in_execute_path │ CSAF VEX │
├───────────────┼───────────────┼──────────┼──────────────┼─────────────────────────────────────────────┼───────────────────┤
│ bsdutils │ CVE-2022-0563 │ LOW │ ignored │ Accept the risk │ .trivyignore.yaml │
├───────────────┤ │ │ │ │ │
│ libblkid1 │ │ │ │ │ │
├───────────────┤ │ │ │ │ │
│ libmount1 │ │ │ │ │ │
├───────────────┤ │ │ │ │ │
│ libsmartcols1 │ │ │ │ │ │
├───────────────┤ │ │ │ │ │
│ libuuid1 │ │ │ │ │ │
├───────────────┤ │ │ │ │ │
│ mount │ │ │ │ │ │
├───────────────┼───────────────┤ │ ├─────────────────────────────────────────────┤ │
│ tar │ CVE-2005-2541 │ │ │ The vulnerable configuration is not enabled │ │
├───────────────┼───────────────┤ │ ├─────────────────────────────────────────────┤ │
│ util-linux │ CVE-2022-0563 │ │ │ Accept the risk │ │
└───────────────┴───────────────┴──────────┴──────────────┴─────────────────────────────────────────────┴───────────────────┘
```
### By Finding IDs
Trivy supports the [.trivyignore](#trivyignore) and [.trivyignore.yaml](#trivyignoreyaml) ignore files.
### .trivyignore
#### .trivyignore
| Scanner | Supported |
|:----------------:|:---------:|
@@ -254,7 +318,7 @@ Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
</details>
### .trivyignore.yaml
#### .trivyignore.yaml
| Scanner | Supported |
|:----------------:|:---------:|
@@ -339,76 +403,7 @@ Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
</details>
## By Vulnerability Target
| Scanner | Supported |
|:----------------:|:---------:|
| Vulnerability | ✓ |
| Misconfiguration | |
| Secret | |
| License | |
Use `--vuln-type` option.
```bash
$ trivy image --vuln-type os ruby:2.4.0
```
Available values:
- library
- os
<details>
<summary>Result</summary>
```bash
2019-05-22T19:36:50.530+0200 INFO Updating vulnerability database...
2019-05-22T19:36:51.681+0200 INFO Detecting Alpine vulnerabilities...
2019-05-22T19:36:51.685+0200 INFO Updating npm Security DB...
2019-05-22T19:36:52.389+0200 INFO Detecting npm vulnerabilities...
2019-05-22T19:36:52.390+0200 INFO Updating pipenv Security DB...
2019-05-22T19:36:53.406+0200 INFO Detecting pipenv vulnerabilities...
ruby:2.4.0 (debian 8.7)
=======================
Total: 7 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 3, CRITICAL: 2)
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| curl | CVE-2018-14618 | CRITICAL | 7.61.0-r0 | 7.61.1-r0 | curl: NTLM password overflow |
| | | | | | via integer overflow |
+ +------------------+----------+ +---------------+----------------------------------+
| | CVE-2018-16839 | HIGH | | 7.61.1-r1 | curl: Integer overflow leading |
| | | | | | to heap-based buffer overflow in |
| | | | | | Curl_sasl_create_plain_message() |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| git | CVE-2018-17456 | HIGH | 2.15.2-r0 | 2.15.3-r0 | git: arbitrary code execution |
| | | | | | via .gitmodules |
+ +------------------+ + + +----------------------------------+
| | CVE-2018-19486 | | | | git: Improper handling of |
| | | | | | PATH allows for commands to be |
| | | | | | executed from... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| libssh2 | CVE-2019-3855 | CRITICAL | 1.8.0-r2 | 1.8.1-r0 | libssh2: Integer overflow in |
| | | | | | transport read resulting in |
| | | | | | out of bounds write... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| sqlite | CVE-2018-20346 | MEDIUM | 3.21.0-r1 | 3.25.3-r0 | CVE-2018-20505 CVE-2018-20506 |
| | | | | | sqlite: Multiple flaws in |
| | | | | | sqlite which can be triggered |
| | | | | | via... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| tar | CVE-2018-20482 | LOW | 1.29-r1 | 1.31-r0 | tar: Infinite read loop in |
| | | | | | sparse_dump_region function in |
| | | | | | sparse.c |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
```
</details>
## By Rego
### By Rego
| Scanner | Supported |
|:----------------:|:---------:|
@@ -483,39 +478,15 @@ More info about the helper functions are in the library [here](https://github.co
You can find more example policies [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/pkg/result/module.go)
## By Inline Comments
### By Vulnerability Exploitability Exchange (VEX)
| Scanner | Supported |
|:----------------:|:---------:|
| Vulnerability | |
| Misconfiguration | |
| Vulnerability | |
| Misconfiguration | |
| Secret | |
| License | |
Some configuration file formats (e.g. Terraform) support inline comments.
Please refer to the [VEX documentation](../supply-chain/vex.md) for the details.
In cases where trivy can detect comments of a specific format immediately adjacent to resource definitions, it is possible to filter/ignore findings from a single point of resource definition (in contrast to `.trivyignore`, which has a directory-wide scope on all of the files scanned).
The format for these comments is `trivy:ignore:<Vulnerability ID>` immediately following the format-specific line-comment token. You can add multiple ignores on the same comment line.
For example, to filter a Vulnerability ID "AVD-GCP-0051" in a Terraform HCL file:
```terraform
#trivy:ignore:AVD-GCP-0051
resource "google_container_cluster" "one_off_test" {
name = var.cluster_name
location = var.region
}
```
For example, to filter vulnerabilities "AVD-GCP-0051" and "AVD-GCP-0053" in a Terraform HCL file:
```terraform
#trivy:ignore:AVD-GCP-0051 trivy:ignore:AVD-GCP-0053
resource "google_container_cluster" "one_off_test" {
name = var.cluster_name
location = var.region
}
```
[^1]: license name is used as id for `.trivyignore.yaml` files

View File

@@ -31,6 +31,7 @@ trivy convert [flags] RESULT_JSON
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--report string specify a report format for the output (all,summary) (default "all")
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
-t, --template string output template
```

View File

@@ -75,6 +75,7 @@ trivy filesystem [flags] PATH
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -95,6 +95,7 @@ trivy image [flags] IMAGE_NAME
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -87,6 +87,7 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg:
--scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,rbac) (default [vuln,misconfig,secret,rbac])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -74,6 +74,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -76,6 +76,7 @@ trivy rootfs [flags] ROOTDIR
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -52,6 +52,7 @@ trivy sbom [flags] SBOM_PATH
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -67,6 +67,7 @@ trivy vm [flags] VM_IMAGE
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip

View File

@@ -316,15 +316,17 @@ This section describes misconfiguration-specific configuration.
Other common options are documented [here](../../configuration/index.md).
### Enabling a subset of misconfiguration scanners
It's possible to only enable certain misconfiguration scanners if you prefer. You can do so by passing the `--misconfig-scanners` option.
It's possible to only enable certain misconfiguration scanners if you prefer.
You can do so by passing the `--misconfig-scanners` option.
This flag takes a comma-separated list of configuration scanner types.
```bash
trivy config --misconfig-scanners=terraform,dockerfile .
```
Will only scan for misconfigurations that pertain to Terraform and Dockerfiles.
### Pass custom policies
### Passing custom policies
You can pass policy files or directories including your custom policies through `--policy` option.
This can be repeated for specifying multiple files or directories.
@@ -338,7 +340,7 @@ For more details, see [Custom Policies](./custom/index.md).
!!! tip
You also need to specify `--namespaces` option.
### Pass custom data
### Passing custom data
You can pass directories including your custom data through `--data` option.
This can be repeated for specifying multiple directories.
@@ -349,7 +351,7 @@ trivy conf --policy ./policy --data ./data --namespaces user ./configs
For more details, see [Custom Data](./custom/data.md).
### Pass namespaces
### Passing namespaces
By default, Trivy evaluates policies defined in `builtin.*`.
If you want to evaluate custom policies in other packages, you have to specify package prefixes through `--namespaces` option.
This can be repeated for specifying multiple packages.
@@ -358,4 +360,32 @@ This can be repeated for specifying multiple packages.
trivy conf --policy ./policy --namespaces main --namespaces user ./configs
```
### Skipping resources by inline comments
Some configuration file formats (e.g. Terraform) support inline comments.
In cases where trivy can detect comments of a specific format immediately adjacent to resource definitions, it is possible to filter/ignore findings from a single point of resource definition (in contrast to `.trivyignore`, which has a directory-wide scope on all of the files scanned).
The format for these comments is `trivy:ignore:<Vulnerability ID>` immediately following the format-specific line-comment token.
You can add multiple ignores on the same comment line.
For example, to filter a misconfiguration ID "AVD-GCP-0051" in a Terraform HCL file:
```terraform
#trivy:ignore:AVD-GCP-0051
resource "google_container_cluster" "one_off_test" {
name = var.cluster_name
location = var.region
}
```
For example, to filter misconfigurations "AVD-GCP-0051" and "AVD-GCP-0053" in a Terraform HCL file:
```terraform
#trivy:ignore:AVD-GCP-0051 trivy:ignore:AVD-GCP-0053
resource "google_container_cluster" "one_off_test" {
name = var.cluster_name
location = var.region
}
```
[custom]: custom/index.md

View File

@@ -152,6 +152,76 @@ The default is `ghcr.io/aquasecurity/trivy-java-db`.
If authentication is required, you need to run `docker login YOUR_REGISTRY`.
Currently, specifying a username and password is not supported.
## Configuration
This section describes vulnerability-specific configuration.
Other common options are documented [here](../configuration/index.md).
### Enabling a subset of package types
It's possible to only enable certain package types if you prefer.
You can do so by passing the `--vuln-type` option.
This flag takes a comma-separated list of package types.
Available values:
- os
- Scan OS packages managed by the OS package manager (e.g. `dpkg`, `yum`, `apk`).
- library
- Scan language-specific packages (e.g. packages installed by `pip`, `npm`, or `gem`).
```bash
$ trivy image --vuln-type os ruby:2.4.0
```
<details>
<summary>Result</summary>
```bash
2019-05-22T19:36:50.530+0200 INFO Updating vulnerability database...
2019-05-22T19:36:51.681+0200 INFO Detecting Alpine vulnerabilities...
2019-05-22T19:36:51.685+0200 INFO Updating npm Security DB...
2019-05-22T19:36:52.389+0200 INFO Detecting npm vulnerabilities...
2019-05-22T19:36:52.390+0200 INFO Updating pipenv Security DB...
2019-05-22T19:36:53.406+0200 INFO Detecting pipenv vulnerabilities...
ruby:2.4.0 (debian 8.7)
=======================
Total: 7 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 3, CRITICAL: 2)
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| curl | CVE-2018-14618 | CRITICAL | 7.61.0-r0 | 7.61.1-r0 | curl: NTLM password overflow |
| | | | | | via integer overflow |
+ +------------------+----------+ +---------------+----------------------------------+
| | CVE-2018-16839 | HIGH | | 7.61.1-r1 | curl: Integer overflow leading |
| | | | | | to heap-based buffer overflow in |
| | | | | | Curl_sasl_create_plain_message() |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| git | CVE-2018-17456 | HIGH | 2.15.2-r0 | 2.15.3-r0 | git: arbitrary code execution |
| | | | | | via .gitmodules |
+ +------------------+ + + +----------------------------------+
| | CVE-2018-19486 | | | | git: Improper handling of |
| | | | | | PATH allows for commands to be |
| | | | | | executed from... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| libssh2 | CVE-2019-3855 | CRITICAL | 1.8.0-r2 | 1.8.1-r0 | libssh2: Integer overflow in |
| | | | | | transport read resulting in |
| | | | | | out of bounds write... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| sqlite | CVE-2018-20346 | MEDIUM | 3.21.0-r1 | 3.25.3-r0 | CVE-2018-20505 CVE-2018-20506 |
| | | | | | sqlite: Multiple flaws in |
| | | | | | sqlite which can be triggered |
| | | | | | via... |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
| tar | CVE-2018-20482 | LOW | 1.29-r1 | 1.31-r0 | tar: Infinite read loop in |
| | | | | | sparse_dump_region function in |
| | | | | | sparse.c |
+---------+------------------+----------+-------------------+---------------+----------------------------------+
```
</details>
[^1]: https://github.com/GoogleContainerTools/distroless
[nvd-CVE-2023-0464]: https://nvd.nist.gov/vuln/detail/CVE-2023-0464

View File

@@ -203,7 +203,11 @@ theme:
markdown_extensions:
- pymdownx.highlight
- pymdownx.superfences
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- admonition
- footnotes
- attr_list

View File

@@ -61,12 +61,12 @@ func ConvertResults(results scan.Results, provider string, scoped []string) map[
primaryURL = fmt.Sprintf("https://avd.aquasec.com/misconfig/%s", strings.ToLower(result.Rule().AVDID))
}
status := types.StatusFailure
status := types.MisconfStatusFailure
switch result.Status() {
case scan.StatusPassed:
status = types.StatusPassed
status = types.MisconfStatusPassed
case scan.StatusIgnored:
status = types.StatusException
status = types.MisconfStatusException
}
flat := result.Flatten()

View File

@@ -643,6 +643,7 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
reportFlagGroup.ShowSuppressed = nil // disable '--show-suppressed'
reportFormat := flag.ReportFormatFlag.Clone()
reportFormat.Usage = "specify a compliance report format for the output" // @TODO: support --report summary for non compliance reports
reportFlagGroup.ReportFormat = reportFormat
@@ -988,6 +989,7 @@ func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
}
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
reportFlagGroup.ShowSuppressed = nil // disable '--show-suppressed'
awsFlags := &flag.Flags{
GlobalFlagGroup: globalFlags,

View File

@@ -26,7 +26,10 @@ func TestJSONWriter_Write(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV012", Status: types.StatusFailure},
{
AVDID: "AVD-KSV012",
Status: types.MisconfStatusFailure,
},
},
},
},
@@ -38,7 +41,10 @@ func TestJSONWriter_Write(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV013", Status: types.StatusFailure},
{
AVDID: "AVD-KSV013",
Status: types.MisconfStatusFailure,
},
},
},
},
@@ -69,7 +75,10 @@ func TestJSONWriter_Write(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := new(bytes.Buffer)
tr := report.JSONWriter{Report: tt.reportType, Output: buf}
tr := report.JSONWriter{
Report: tt.reportType,
Output: buf,
}
err := tr.Write(tt.input)
require.NoError(t, err)

View File

@@ -57,13 +57,13 @@ func TestBuildComplianceReport(t *testing.T) {
"https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted",
"https://avd.aquasec.com/misconfig/ksv001",
},
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
},
{
Type: "Kubernetes Security Check",
ID: "KSV002",
AVDID: "AVD-KSV-9999",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -179,7 +179,7 @@ func TestBuildComplianceReport(t *testing.T) {
"https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted",
"https://avd.aquasec.com/misconfig/ksv001",
},
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
},
},
},

View File

@@ -32,7 +32,10 @@ func TestBuildSummary(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV012", Status: types.StatusFailure},
{
AVDID: "AVD-KSV012",
Status: types.MisconfStatusFailure,
},
},
},
},
@@ -44,7 +47,10 @@ func TestBuildSummary(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV013", Status: types.StatusFailure},
{
AVDID: "AVD-KSV013",
Status: types.MisconfStatusFailure,
},
},
},
},
@@ -86,7 +92,10 @@ func TestBuildSummary(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV012", Status: types.StatusFailure},
{
AVDID: "AVD-KSV012",
Status: types.MisconfStatusFailure,
},
},
},
},
@@ -98,7 +107,10 @@ func TestBuildSummary(t *testing.T) {
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{AVDID: "AVD-KSV013", Status: types.StatusFailure},
{
AVDID: "AVD-KSV013",
Status: types.MisconfStatusFailure,
},
},
},
},

View File

@@ -3,7 +3,6 @@ package report
import (
"context"
"io"
"sync"
"golang.org/x/xerrors"
@@ -31,9 +30,8 @@ func (tw TableWriter) Write(ctx context.Context, report *ComplianceReport) error
switch tw.Report {
case allReport:
t := pkgReport.Writer{
Output: tw.Output,
Severities: tw.Severities,
ShowMessageOnce: &sync.Once{},
Output: tw.Output,
Severities: tw.Severities,
}
for _, cr := range report.Results {
r := types.Report{Results: cr.Results}

View File

@@ -39,7 +39,7 @@ func TestTableWriter_Write(t *testing.T) {
Misconfigurations: []types.DetectedMisconfiguration{
{
AVDID: "AVD-KSV012",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -54,7 +54,7 @@ func TestTableWriter_Write(t *testing.T) {
Misconfigurations: []types.DetectedMisconfiguration{
{
AVDID: "AVD-KSV013",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},

View File

@@ -4,7 +4,6 @@ import (
"github.com/samber/lo"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -63,8 +62,8 @@ func filterHighSecrets(result types.Result) types.Result {
}
func filterSecrets(result types.Result, severity dbTypes.Severity) types.Result {
filtered := lo.Filter(result.Secrets, func(vuln ftypes.SecretFinding, _ int) bool {
return vuln.Severity == severity.String()
filtered := lo.Filter(result.Secrets, func(secret types.DetectedSecret, _ int) bool {
return secret.Severity == severity.String()
})
return types.Result{
Target: result.Target,

View File

@@ -11,10 +11,10 @@ func MapSpecCheckIDToFilteredResults(result types.Result, checkIDs map[types.Sca
mapCheckByID := make(map[string]types.Results)
for _, vuln := range result.Vulnerabilities {
// Skip irrelevant check IDs
if !slices.Contains(checkIDs[types.VulnerabilityScanner], vuln.GetID()) {
if !slices.Contains(checkIDs[types.VulnerabilityScanner], vuln.VulnerabilityID) {
continue
}
mapCheckByID[vuln.GetID()] = append(mapCheckByID[vuln.GetID()], types.Result{
mapCheckByID[vuln.VulnerabilityID] = append(mapCheckByID[vuln.VulnerabilityID], types.Result{
Target: result.Target,
Class: result.Class,
Type: result.Type,
@@ -23,11 +23,11 @@ func MapSpecCheckIDToFilteredResults(result types.Result, checkIDs map[types.Sca
}
for _, m := range result.Misconfigurations {
// Skip irrelevant check IDs
if !slices.Contains(checkIDs[types.MisconfigScanner], m.GetID()) {
if !slices.Contains(checkIDs[types.MisconfigScanner], m.AVDID) {
continue
}
mapCheckByID[m.GetID()] = append(mapCheckByID[m.GetID()], types.Result{
mapCheckByID[m.AVDID] = append(mapCheckByID[m.AVDID], types.Result{
Target: result.Target,
Class: result.Class,
Type: result.Type,
@@ -45,11 +45,11 @@ func MapSpecCheckIDToFilteredResults(result types.Result, checkIDs map[types.Sca
func misconfigSummary(misconfig types.DetectedMisconfiguration) *types.MisconfSummary {
rms := types.MisconfSummary{}
switch misconfig.Status {
case types.StatusPassed:
case types.MisconfStatusPassed:
rms.Successes = 1
case types.StatusFailure:
case types.MisconfStatusFailure:
rms.Failures = 1
case types.StatusException:
case types.MisconfStatusException:
rms.Exceptions = 1
}
return &rms

View File

@@ -42,15 +42,15 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
Misconfigurations: []types.DetectedMisconfiguration{
{
AVDID: "AVD-KSV012",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
{
AVDID: "AVD-KSV013",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
{
AVDID: "AVD-1.2.31",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -68,7 +68,7 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
Misconfigurations: []types.DetectedMisconfiguration{
{
AVDID: "AVD-KSV012",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -86,7 +86,7 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
Misconfigurations: []types.DetectedMisconfiguration{
{
AVDID: "AVD-1.2.31",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -99,7 +99,7 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
result: types.Result{
Target: "target",
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "aws-access-key-id",
Category: secret.CategoryAWS,
@@ -135,7 +135,7 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
{
Target: "target",
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "aws-access-key-id",
Category: secret.CategoryAWS,

View File

@@ -101,6 +101,11 @@ var (
ConfigName: "scan.compliance",
Usage: "compliance report to generate",
}
ShowSuppressedFlag = Flag[bool]{
Name: "show-suppressed",
ConfigName: "scan.show-suppressed",
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
}
)
// ReportFlagGroup composes common printer flag structs
@@ -119,6 +124,7 @@ type ReportFlagGroup struct {
OutputPluginArg *Flag[string]
Severity *Flag[[]string]
Compliance *Flag[string]
ShowSuppressed *Flag[bool]
}
type ReportOptions struct {
@@ -135,6 +141,7 @@ type ReportOptions struct {
OutputPluginArgs []string
Severities []dbTypes.Severity
Compliance spec.ComplianceSpec
ShowSuppressed bool
}
func NewReportFlagGroup() *ReportFlagGroup {
@@ -152,6 +159,7 @@ func NewReportFlagGroup() *ReportFlagGroup {
OutputPluginArg: OutputPluginArgFlag.Clone(),
Severity: SeverityFlag.Clone(),
Compliance: ComplianceFlag.Clone(),
ShowSuppressed: ShowSuppressedFlag.Clone(),
}
}
@@ -174,6 +182,7 @@ func (f *ReportFlagGroup) Flags() []Flagger {
f.OutputPluginArg,
f.Severity,
f.Compliance,
f.ShowSuppressed,
}
}
@@ -247,6 +256,7 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) {
OutputPluginArgs: outputPluginArgs,
Severities: toSeverity(f.Severity.Value()),
Compliance: cs,
ShowSuppressed: f.ShowSuppressed.Value(),
}, nil
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -28,37 +27,37 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
{
ID: "ID102",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
{
ID: "ID103",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "CRITICAL",
},
{
ID: "ID104",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "UNKNOWN",
},
{
ID: "ID105",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID106",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
},
@@ -131,37 +130,37 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
{
ID: "ID102",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
{
ID: "ID103",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "CRITICAL",
},
{
ID: "ID104",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "UNKNOWN",
},
{
ID: "ID105",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID106",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
},
@@ -244,7 +243,7 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
},
@@ -258,7 +257,7 @@ var (
Name: "lua",
Results: types.Results{
{
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "secret1",
Severity: "CRITICAL",
@@ -281,28 +280,28 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "KSV-ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "KSV-ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
{
ID: "KSV-ID102",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
{
ID: "KCV-ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "KCV-ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
},

View File

@@ -3,7 +3,6 @@ package report
import (
"context"
"io"
"sync"
"golang.org/x/xerrors"
@@ -48,9 +47,8 @@ func (tw TableWriter) Write(ctx context.Context, report Report) error {
switch tw.Report {
case AllReport:
t := pkgReport.Writer{
Output: tw.Output,
Severities: tw.Severities,
ShowMessageOnce: &sync.Once{},
Output: tw.Output,
Severities: tw.Severities,
}
for _, r := range report.Resources {
if r.Report.Results.Failed() {

View File

@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/k8s/report"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -38,7 +37,7 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
},
@@ -54,28 +53,28 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "KSV-ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "KSV-ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
{
ID: "KSV-ID102",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
{
ID: "KCV-ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "KCV-ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
},
@@ -88,7 +87,7 @@ var (
Name: "lua",
Results: types.Results{
{
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "secret1",
Severity: "CRITICAL",
@@ -110,37 +109,37 @@ var (
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "ID100",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID101",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "MEDIUM",
},
{
ID: "ID102",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
{
ID: "ID103",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "CRITICAL",
},
{
ID: "ID104",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "UNKNOWN",
},
{
ID: "ID105",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "LOW",
},
{
ID: "ID106",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Severity: "HIGH",
},
},

View File

@@ -266,16 +266,16 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(in *jle
in.Delim('[')
if out.Secrets == nil {
if !in.IsDelim(']') {
out.Secrets = make([]types1.SecretFinding, 0, 0)
out.Secrets = make([]types.DetectedSecret, 0, 0)
} else {
out.Secrets = []types1.SecretFinding{}
out.Secrets = []types.DetectedSecret{}
}
} else {
out.Secrets = (out.Secrets)[:0]
}
for !in.IsDelim(']') {
var v10 types1.SecretFinding
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in, &v10)
var v10 types.DetectedSecret
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in, &v10)
out.Secrets = append(out.Secrets, v10)
in.WantComma()
}
@@ -298,7 +298,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(in *jle
}
for !in.IsDelim(']') {
var v11 types.DetectedLicense
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in, &v11)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes4(in, &v11)
out.Licenses = append(out.Licenses, v11)
in.WantComma()
}
@@ -321,7 +321,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(in *jle
}
for !in.IsDelim(']') {
var v12 types1.CustomResource
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &v12)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in, &v12)
out.CustomResources = append(out.CustomResources, v12)
in.WantComma()
}
@@ -412,7 +412,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(out *jw
if v19 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out, v20)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out, v20)
}
out.RawByte(']')
}
@@ -426,7 +426,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(out *jw
if v21 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out, v22)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes4(out, v22)
}
out.RawByte(']')
}
@@ -440,7 +440,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(out *jw
if v23 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, v24)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out, v24)
}
out.RawByte(']')
}
@@ -471,7 +471,7 @@ func (v *Result) UnmarshalJSON(data []byte) error {
func (v *Result) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(l, v)
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in *jlexer.Lexer, out *types1.CustomResource) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in *jlexer.Lexer, out *types1.CustomResource) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -495,7 +495,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in *jlexer.L
case "FilePath":
out.FilePath = string(in.String())
case "Layer":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Layer)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
case "Data":
if m, ok := out.Data.(easyjson.Unmarshaler); ok {
m.UnmarshalEasyJSON(in)
@@ -514,7 +514,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out *jwriter.Writer, in types1.CustomResource) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out *jwriter.Writer, in types1.CustomResource) {
out.RawByte('{')
first := true
_ = first
@@ -531,7 +531,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out *jwriter
{
const prefix string = ",\"Layer\":"
out.RawString(prefix)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Layer)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
}
{
const prefix string = ",\"Data\":"
@@ -546,7 +546,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out *jwriter
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in *jlexer.Lexer, out *types1.Layer) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in *jlexer.Lexer, out *types1.Layer) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -581,7 +581,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out *jwriter.Writer, in types1.Layer) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out *jwriter.Writer, in types1.Layer) {
out.RawByte('{')
first := true
_ = first
@@ -613,7 +613,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out *jwriter
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in *jlexer.Lexer, out *types.DetectedLicense) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes4(in *jlexer.Lexer, out *types.DetectedLicense) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -656,7 +656,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in *jlexer.Lexer,
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out *jwriter.Writer, in types.DetectedLicense) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes4(out *jwriter.Writer, in types.DetectedLicense) {
out.RawByte('{')
first := true
_ = first
@@ -697,7 +697,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out *jwriter.Writ
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in *jlexer.Lexer, out *types1.SecretFinding) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in *jlexer.Lexer, out *types.DetectedSecret) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -729,11 +729,11 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in *jlexer.L
case "EndLine":
out.EndLine = int(in.Int())
case "Code":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in, &out.Code)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Code)
case "Match":
out.Match = string(in.String())
case "Layer":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Layer)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
default:
in.SkipRecursive()
}
@@ -744,7 +744,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out *jwriter.Writer, in types1.SecretFinding) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out *jwriter.Writer, in types.DetectedSecret) {
out.RawByte('{')
first := true
_ = first
@@ -781,7 +781,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out *jwriter
{
const prefix string = ",\"Code\":"
out.RawString(prefix)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out, in.Code)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Code)
}
{
const prefix string = ",\"Match\":"
@@ -791,11 +791,11 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out *jwriter
if true {
const prefix string = ",\"Layer\":"
out.RawString(prefix)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Layer)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in *jlexer.Lexer, out *types1.Code) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in *jlexer.Lexer, out *types1.Code) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -831,7 +831,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in *jlexer.L
}
for !in.IsDelim(']') {
var v25 types1.Line
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in, &v25)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in, &v25)
out.Lines = append(out.Lines, v25)
in.WantComma()
}
@@ -847,7 +847,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out *jwriter.Writer, in types1.Code) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out *jwriter.Writer, in types1.Code) {
out.RawByte('{')
first := true
_ = first
@@ -862,14 +862,14 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out *jwriter
if v26 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out, v27)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out, v27)
}
out.RawByte(']')
}
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in *jlexer.Lexer, out *types1.Line) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in *jlexer.Lexer, out *types1.Line) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -914,7 +914,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out *jwriter.Writer, in types1.Line) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out *jwriter.Writer, in types1.Line) {
out.RawByte('{')
first := true
_ = first
@@ -1027,9 +1027,9 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes2(in *jlexer.Lexer,
case "Status":
out.Status = types.MisconfStatus(in.String())
case "Layer":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Layer)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
case "CauseMetadata":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in, &out.CauseMetadata)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in, &out.CauseMetadata)
case "Traces":
if in.IsNull() {
in.Skip()
@@ -1210,7 +1210,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes2(out *jwriter.Writ
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Layer)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
}
if true {
const prefix string = ",\"CauseMetadata\":"
@@ -1220,7 +1220,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes2(out *jwriter.Writ
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out, in.CauseMetadata)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out, in.CauseMetadata)
}
if len(in.Traces) != 0 {
const prefix string = ",\"Traces\":"
@@ -1243,7 +1243,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes2(out *jwriter.Writ
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.Lexer, out *types1.CauseMetadata) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in *jlexer.Lexer, out *types1.CauseMetadata) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -1273,7 +1273,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.L
case "EndLine":
out.EndLine = int(in.Int())
case "Code":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in, &out.Code)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Code)
case "Occurrences":
if in.IsNull() {
in.Skip()
@@ -1291,7 +1291,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.L
}
for !in.IsDelim(']') {
var v34 types1.Occurrence
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in, &v34)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in, &v34)
out.Occurrences = append(out.Occurrences, v34)
in.WantComma()
}
@@ -1307,7 +1307,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out *jwriter.Writer, in types1.CauseMetadata) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out *jwriter.Writer, in types1.CauseMetadata) {
out.RawByte('{')
first := true
_ = first
@@ -1365,7 +1365,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out *jwriter
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out, in.Code)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Code)
}
if len(in.Occurrences) != 0 {
const prefix string = ",\"Occurrences\":"
@@ -1381,14 +1381,14 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out *jwriter
if v35 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out, v36)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out, v36)
}
out.RawByte(']')
}
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in *jlexer.Lexer, out *types1.Occurrence) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.Lexer, out *types1.Occurrence) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -1412,7 +1412,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in *jlexer.L
case "Filename":
out.Filename = string(in.String())
case "Location":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in, &out.Location)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in, &out.Location)
default:
in.SkipRecursive()
}
@@ -1423,7 +1423,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out *jwriter.Writer, in types1.Occurrence) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out *jwriter.Writer, in types1.Occurrence) {
out.RawByte('{')
first := true
_ = first
@@ -1451,11 +1451,11 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out *jwriter
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out, in.Location)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out, in.Location)
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in *jlexer.Lexer, out *types1.Location) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in *jlexer.Lexer, out *types1.Location) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -1488,7 +1488,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out *jwriter.Writer, in types1.Location) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out *jwriter.Writer, in types1.Location) {
out.RawByte('{')
first := true
_ = first
@@ -1629,7 +1629,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes(in *jlexer.Lexer,
in.AddError((out.Status).UnmarshalJSON(data))
}
case "Layer":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Layer)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
case "SeveritySource":
out.SeveritySource = types2.SourceID(in.String())
case "PrimaryURL":
@@ -1885,7 +1885,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes(out *jwriter.Write
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Layer)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
}
if in.SeveritySource != "" {
const prefix string = ",\"SeveritySource\":"
@@ -2297,7 +2297,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Le
if out.BuildInfo == nil {
out.BuildInfo = new(types1.BuildInfo)
}
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in, out.BuildInfo)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in, out.BuildInfo)
}
case "Indirect":
out.Indirect = bool(in.Bool())
@@ -2325,7 +2325,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Le
in.Delim(']')
}
case "Layer":
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Layer)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
case "FilePath":
out.FilePath = string(in.String())
case "Digest":
@@ -2347,7 +2347,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Le
}
for !in.IsDelim(']') {
var v52 types1.Location
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in, &v52)
easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in, &v52)
out.Locations = append(out.Locations, v52)
in.WantComma()
}
@@ -2553,7 +2553,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter.
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out, *in.BuildInfo)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out, *in.BuildInfo)
}
if in.Indirect {
const prefix string = ",\"Indirect\":"
@@ -2592,7 +2592,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter.
} else {
out.RawString(prefix)
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Layer)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
}
if in.FilePath != "" {
const prefix string = ",\"FilePath\":"
@@ -2628,7 +2628,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter.
if v58 > 0 {
out.RawByte(',')
}
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out, v59)
easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out, v59)
}
out.RawByte(']')
}
@@ -2654,7 +2654,7 @@ func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter.
}
out.RawByte('}')
}
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in *jlexer.Lexer, out *types1.BuildInfo) {
func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in *jlexer.Lexer, out *types1.BuildInfo) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -2710,7 +2710,7 @@ func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes9(in *jlexer.L
in.Consumed()
}
}
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes9(out *jwriter.Writer, in types1.BuildInfo) {
func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out *jwriter.Writer, in types1.BuildInfo) {
out.RawByte('{')
first := true
_ = first

View File

@@ -197,7 +197,7 @@ func TestReportWriter_Sarif(t *testing.T) {
Message: "Message",
Severity: "HIGH",
PrimaryURL: "https://avd.aquasec.com/appshield/ksv001",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
{
Type: "Kubernetes Security Check",
@@ -206,7 +206,7 @@ func TestReportWriter_Sarif(t *testing.T) {
Message: "Message",
Severity: "CRITICAL",
PrimaryURL: "https://avd.aquasec.com/appshield/ksv002",
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
},
},
},
@@ -338,7 +338,7 @@ func TestReportWriter_Sarif(t *testing.T) {
{
Target: "library/test",
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "aws-secret-access-key",
Category: "AWS",

View File

@@ -74,7 +74,7 @@ func (r *misconfigRenderer) Render() string {
func (r *misconfigRenderer) countSeverities() map[string]int {
severityCount := make(map[string]int)
for _, misconf := range r.result.Misconfigurations {
if misconf.Status == types.StatusFailure {
if misconf.Status == types.MisconfStatusFailure {
severityCount[misconf.Severity]++
}
}
@@ -110,11 +110,11 @@ func (r *misconfigRenderer) renderSummary(misconf types.DetectedMisconfiguration
// show pass/fail/exception unless we are only showing failures
if r.includeNonFailures {
switch misconf.Status {
case types.StatusPassed:
case types.MisconfStatusPassed:
r.printf("<green><bold>%s: ", misconf.Status)
case types.StatusFailure:
case types.MisconfStatusFailure:
r.printf("<red><bold>%s: ", misconf.Status)
case types.StatusException:
case types.MisconfStatusException:
r.printf("<yellow><bold>%s: ", misconf.Status)
}
}
@@ -217,7 +217,7 @@ func (r *misconfigRenderer) outputTrace() {
}
c := green
if misconf.Status == types.StatusFailure {
if misconf.Status == types.MisconfStatusFailure {
c = red
}

View File

@@ -9,19 +9,19 @@ import (
"github.com/aquasecurity/tml"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
type secretRenderer struct {
w *bytes.Buffer
target string
secrets []types.SecretFinding
secrets []types.DetectedSecret
severities []dbTypes.Severity
width int
ansi bool
}
func NewSecretRenderer(target string, secrets []types.SecretFinding, ansi bool, severities []dbTypes.Severity) *secretRenderer {
func NewSecretRenderer(target string, secrets []types.DetectedSecret, ansi bool, severities []dbTypes.Severity) *secretRenderer {
width, _, err := term.GetSize(0)
if err != nil || width == 0 {
width = 40
@@ -76,13 +76,13 @@ func (r *secretRenderer) printSingleDivider() {
r.printf("<dim>%s\r\n", strings.Repeat("─", r.width))
}
func (r *secretRenderer) renderSingle(secret types.SecretFinding) {
func (r *secretRenderer) renderSingle(secret types.DetectedSecret) {
r.renderSummary(secret)
r.renderCode(secret)
r.printf("\r\n\r\n")
}
func (r *secretRenderer) renderSummary(secret types.SecretFinding) {
func (r *secretRenderer) renderSummary(secret types.DetectedSecret) {
// severity
switch secret.Severity {
@@ -108,7 +108,7 @@ func (r *secretRenderer) renderSummary(secret types.SecretFinding) {
r.printSingleDivider()
}
func (r *secretRenderer) renderCode(secret types.SecretFinding) {
func (r *secretRenderer) renderCode(secret types.DetectedSecret) {
// highlight code if we can...
if lines := secret.Code.Lines; len(lines) > 0 {

View File

@@ -1,6 +1,7 @@
package table_test
import (
"github.com/aquasecurity/trivy/pkg/types"
"strings"
"testing"
@@ -15,12 +16,12 @@ func TestSecretRenderer(t *testing.T) {
tests := []struct {
name string
input []ftypes.SecretFinding
input []types.DetectedSecret
want string
}{
{
name: "single line",
input: []ftypes.SecretFinding{
input: []types.DetectedSecret{
{
RuleID: "rule-id",
Category: ftypes.SecretRuleCategory("category"),
@@ -62,7 +63,7 @@ this is a title
},
{
name: "multiple line",
input: []ftypes.SecretFinding{
input: []types.DetectedSecret{
{
RuleID: "rule-id",
Category: ftypes.SecretRuleCategory("category"),
@@ -135,7 +136,10 @@ this is a title
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
renderer := table.NewSecretRenderer("my-file", test.input, false, []dbTypes.Severity{dbTypes.SeverityHigh, dbTypes.SeverityMedium})
renderer := table.NewSecretRenderer("my-file", test.input, false, []dbTypes.Severity{
dbTypes.SeverityHigh,
dbTypes.SeverityMedium,
})
assert.Equal(t, test.want, strings.ReplaceAll(renderer.Render(), "\r\n", "\n"))
})
}

View File

@@ -7,7 +7,6 @@ import (
"os"
"runtime"
"strings"
"sync"
"github.com/fatih/color"
"golang.org/x/exp/slices"
@@ -36,8 +35,8 @@ type Writer struct {
// Show dependency origin tree
Tree bool
// We have to show a message once about using the '-format json' subcommand to get the full pkgPath
ShowMessageOnce *sync.Once
// Show suppressed findings
ShowSuppressed bool
// For misconfigurations
IncludeNonFailures bool
@@ -54,6 +53,7 @@ type Renderer interface {
// Write writes the result on standard output
func (tw Writer) Write(_ context.Context, report types.Report) error {
for _, result := range report.Results {
// Not display a table of custom resources
if result.Class == types.ClassCustom {
@@ -73,7 +73,7 @@ func (tw Writer) write(result types.Result) {
switch {
// vulnerability
case result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg:
renderer = NewVulnerabilityRenderer(result, tw.isOutputToTerminal(), tw.Tree, tw.Severities)
renderer = NewVulnerabilityRenderer(result, tw.isOutputToTerminal(), tw.Tree, tw.ShowSuppressed, tw.Severities)
// misconfiguration
case result.Class == types.ClassConfig:
renderer = NewMisconfigRenderer(result, tw.Severities, tw.Trace, tw.IncludeNonFailures, tw.isOutputToTerminal())

View File

@@ -2,17 +2,17 @@ package table_test
import (
"bytes"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"testing"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/report/table"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportWriter_Table(t *testing.T) {
func TestWriter_Write(t *testing.T) {
testCases := []struct {
name string
results types.Results
@@ -20,7 +20,7 @@ func TestReportWriter_Table(t *testing.T) {
includeNonFailures bool
}{
{
name: "happy path full",
name: "vulnerability and custom resource",
results: types.Results{
{
Target: "test",
@@ -39,6 +39,12 @@ func TestReportWriter_Table(t *testing.T) {
},
},
},
CustomResources: []ftypes.CustomResource{
{
Type: "test",
Data: "test",
},
},
},
},
expectedOutput: `
@@ -55,291 +61,15 @@ Total: 1 (MEDIUM: 0, HIGH: 1)
`,
},
{
name: "happy path with filePath in result",
name: "no vulns",
results: types.Results{
{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
PkgPath: "foo/bar",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌───────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├───────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo (bar) │ CVE-2020-0001 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ foobar │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└───────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
},
{
name: "no title for vuln and missing primary link",
results: types.Results{
{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Description: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ foo │ CVE-2020-0001 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ foobar │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
`,
},
{
name: "long title for vuln",
results: types.Results{
{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-1234",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-1234",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Title: "a b c d e f g h i j k l m n o p q r s t u v",
Description: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo │ CVE-2020-1234 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ a b c d e f g h i j k l... │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-1234 │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
},
{
name: "no vulns",
expectedOutput: ``,
},
{
name: "happy path with vulnerability origin graph with direct dependency info",
results: types.Results{
{
Target: "package-lock.json",
Class: types.ClassLangPkg,
Type: "npm",
Packages: []ftypes.Package{
{
ID: "node-fetch@1.7.3",
Name: "node-fetch",
Version: "1.7.3",
Indirect: true,
},
{
ID: "isomorphic-fetch@2.2.1",
Name: "isomorphic-fetch",
Version: "2.2.1",
Indirect: true,
DependsOn: []string{
"node-fetch@1.7.3",
},
},
{
ID: "fbjs@0.8.18",
Name: "fbjs",
Version: "0.8.18",
Indirect: true,
DependsOn: []string{
"isomorphic-fetch@2.2.1",
},
},
{
ID: "sanitize-html@1.20.0",
Name: "sanitize-html",
Version: "1.20.0",
},
{
ID: "styled-components@3.1.3",
Name: "styled-components",
Version: "3.1.3",
DependsOn: []string{
"fbjs@0.8.18",
},
},
},
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2022-0235",
PkgID: "node-fetch@1.7.3",
PkgName: "node-fetch",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
InstalledVersion: "1.7.3",
FixedVersion: "2.6.7, 3.1.1",
Status: dbTypes.StatusFixed,
},
{
VulnerabilityID: "CVE-2021-26539",
PkgID: "sanitize-html@1.20.0",
PkgName: "sanitize-html",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "MEDIUM",
},
InstalledVersion: "1.20.0",
FixedVersion: "2.3.1",
Status: dbTypes.StatusFixed,
},
},
},
},
expectedOutput: `
package-lock.json (npm)
=======================
Total: 2 (MEDIUM: 1, HIGH: 1)
┌───────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├───────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch │ CVE-2022-0235 │ HIGH │ fixed │ 1.7.3 │ 2.6.7, 3.1.1 │ foobar │
├───────────────┼────────────────┼──────────┤ ├───────────────────┼───────────────┤ │
│ sanitize-html │ CVE-2021-26539 │ MEDIUM │ │ 1.20.0 │ 2.3.1 │ │
└───────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
Dependency Origin Tree (Reversed)
=================================
package-lock.json
├── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
│ └── ...(omitted)...
│ └── styled-components@3.1.3
└── sanitize-html@1.20.0, (MEDIUM: 1, HIGH: 0)
`,
},
{
name: "happy path with vulnerability origin graph without direct dependency info",
results: types.Results{
{
Target: "package-lock.json",
Class: types.ClassLangPkg,
Type: "npm",
Packages: []ftypes.Package{
{
ID: "node-fetch@1.7.3",
Name: "node-fetch",
Version: "1.7.3",
Indirect: true,
},
{
ID: "isomorphic-fetch@2.2.1",
Name: "isomorphic-fetch",
Version: "2.2.1",
Indirect: true,
DependsOn: []string{
"node-fetch@1.7.3",
},
},
{
ID: "fbjs@0.8.18",
Name: "fbjs",
Version: "0.8.18",
Indirect: true,
DependsOn: []string{
"isomorphic-fetch@2.2.1",
},
},
{
ID: "styled-components@3.1.3",
Name: "styled-components",
Version: "3.1.3",
Indirect: true,
DependsOn: []string{
"fbjs@0.8.18",
},
},
},
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2022-0235",
PkgID: "node-fetch@1.7.3",
PkgName: "node-fetch",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
InstalledVersion: "1.7.3",
FixedVersion: "2.6.7, 3.1.1",
Status: dbTypes.StatusFixed,
},
},
},
},
expectedOutput: `
package-lock.json (npm)
=======================
Total: 1 (MEDIUM: 0, HIGH: 1)
┌────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch │ CVE-2022-0235 │ HIGH │ fixed │ 1.7.3 │ 2.6.7, 3.1.1 │ foobar │
└────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
Dependency Origin Tree (Reversed)
=================================
package-lock.json
└── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
└── ...(omitted)...
└── styled-components@3.1.3
`,
},
}
for _, tc := range testCases {

View File

@@ -21,35 +21,56 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
var showSuppressedOnce = sync.OnceFunc(func() {
log.Logger.Info(`Some vulnerabilities have been ignored/suppressed. Use the "--show-suppressed" flag to display them.`)
})
type vulnerabilityRenderer struct {
w *bytes.Buffer
tableWriter *table.Table
result types.Result
isTerminal bool
tree bool
severities []dbTypes.Severity
once *sync.Once
w *bytes.Buffer
result types.Result
isTerminal bool
tree bool // Show dependency tree
showSuppressed bool // Show suppressed vulnerabilities
severities []dbTypes.Severity
once *sync.Once
}
func NewVulnerabilityRenderer(result types.Result, isTerminal, tree bool, severities []dbTypes.Severity) *vulnerabilityRenderer {
func NewVulnerabilityRenderer(result types.Result, isTerminal, tree, suppressed bool, severities []dbTypes.Severity) *vulnerabilityRenderer {
buf := bytes.NewBuffer([]byte{})
if !isTerminal {
tml.DisableFormatting()
}
return &vulnerabilityRenderer{
w: buf,
tableWriter: newTableWriter(buf, isTerminal),
result: result,
isTerminal: isTerminal,
tree: tree,
severities: severities,
once: new(sync.Once),
w: buf,
result: result,
isTerminal: isTerminal,
tree: tree,
showSuppressed: suppressed,
severities: severities,
once: new(sync.Once),
}
}
func (r *vulnerabilityRenderer) Render() string {
r.setHeaders()
r.setVulnerabilityRows(r.result.Vulnerabilities)
r.renderDetectedVulnerabilities()
if r.tree {
r.renderDependencyTree()
}
if r.showSuppressed {
r.renderModifiedVulnerabilities()
} else if len(r.result.ModifiedFindings) > 0 {
showSuppressedOnce()
}
return r.w.String()
}
func (r *vulnerabilityRenderer) renderDetectedVulnerabilities() {
tw := newTableWriter(r.w, r.isTerminal)
r.setHeaders(tw)
r.setVulnerabilityRows(tw, r.result.Vulnerabilities)
severityCount := r.countSeverities(r.result.Vulnerabilities)
total, summaries := summarize(r.severities, severityCount)
@@ -61,15 +82,10 @@ func (r *vulnerabilityRenderer) Render() string {
RenderTarget(r.w, target, r.isTerminal)
r.printf("Total: %d (%s)\n\n", total, strings.Join(summaries, ", "))
r.tableWriter.Render()
if r.tree {
r.renderDependencyTree()
}
return r.w.String()
tw.Render()
}
func (r *vulnerabilityRenderer) setHeaders() {
func (r *vulnerabilityRenderer) setHeaders(tw *table.Table) {
if len(r.result.Vulnerabilities) == 0 {
return
}
@@ -82,10 +98,10 @@ func (r *vulnerabilityRenderer) setHeaders() {
"Fixed Version",
"Title",
}
r.tableWriter.SetHeaders(header...)
tw.SetHeaders(header...)
}
func (r *vulnerabilityRenderer) setVulnerabilityRows(vulns []types.DetectedVulnerability) {
func (r *vulnerabilityRenderer) setVulnerabilityRows(tw *table.Table, vulns []types.DetectedVulnerability) {
for _, v := range vulns {
lib := v.PkgName
if v.PkgPath != "" {
@@ -139,7 +155,7 @@ func (r *vulnerabilityRenderer) setVulnerabilityRows(vulns []types.DetectedVulne
}
}
r.tableWriter.AddRow(row...)
tw.AddRow(row...)
}
}
@@ -151,6 +167,46 @@ func (r *vulnerabilityRenderer) countSeverities(vulns []types.DetectedVulnerabil
return severityCount
}
func (r *vulnerabilityRenderer) renderModifiedVulnerabilities() {
tw := newTableWriter(r.w, r.isTerminal)
header := []string{
"Library",
"Vulnerability",
"Severity",
"Status",
"Statement",
"Source",
}
tw.SetHeaders(header...)
var total int
for _, m := range r.result.ModifiedFindings {
if m.Type != types.FindingTypeVulnerability {
continue
}
vuln := m.Finding.(types.DetectedVulnerability)
total++
stmt := lo.Ternary(m.Statement != "", m.Statement, "N/A")
tw.AddRow(vuln.PkgName, vuln.VulnerabilityID, vuln.Severity, string(m.Status), stmt, m.Source)
}
if total == 0 {
return
}
title := fmt.Sprintf("Suppressed Vulnerabilities (Total: %d)", total)
if r.isTerminal {
// nolint
_ = tml.Fprintf(r.w, "\n<underline>%s</underline>\n\n", title)
} else {
_, _ = fmt.Fprintf(r.w, "\n%s\n", title)
_, _ = fmt.Fprintf(r.w, "%s\n", strings.Repeat("=", len(title)))
}
tw.Render()
}
func (r *vulnerabilityRenderer) renderDependencyTree() {
// Get parents of each dependency
parents := ftypes.Packages(r.result.Packages).ParentDeps()

View File

@@ -0,0 +1,410 @@
package table_test
import (
"testing"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/report/table"
"github.com/aquasecurity/trivy/pkg/types"
)
func Test_vulnerabilityRenderer_Render(t *testing.T) {
tests := []struct {
name string
result types.Result
want string
includeNonFailures bool
showSuppressed bool
}{
{
name: "happy path full",
result: types.Result{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Status: dbTypes.StatusWillNotFix,
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
// It won't be shown as `showSuppressed` is false.
ModifiedFindings: []types.ModifiedFinding{
{
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: ".trivyignore",
Finding: types.DetectedVulnerability{
VulnerabilityID: "CVE-2020-0002",
},
},
},
},
want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬──────────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼──────────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo │ CVE-2020-0001 │ HIGH │ will_not_fix │ 1.2.3 │ │ foobar │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└─────────┴───────────────┴──────────┴──────────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
},
{
name: "happy path with filePath in result",
result: types.Result{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
PkgPath: "foo/bar",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
},
want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌───────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├───────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo (bar) │ CVE-2020-0001 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ foobar │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└───────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
},
{
name: "no title for vuln and missing primary link",
result: types.Result{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Description: "foobar",
Severity: "HIGH",
},
},
},
},
want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ foo │ CVE-2020-0001 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ foobar │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
`,
},
{
name: "long title for vuln",
result: types.Result{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-1234",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-1234",
Status: dbTypes.StatusFixed,
Vulnerability: dbTypes.Vulnerability{
Title: "a b c d e f g h i j k l m n o p q r s t u v",
Description: "foobar",
Severity: "HIGH",
},
},
},
},
want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo │ CVE-2020-1234 │ HIGH │ fixed │ 1.2.3 │ 3.4.5 │ a b c d e f g h i j k l... │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-1234 │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
},
{
name: "happy path with vulnerability origin graph with direct dependency info",
result: types.Result{
Target: "package-lock.json",
Class: types.ClassLangPkg,
Type: "npm",
Packages: []ftypes.Package{
{
ID: "node-fetch@1.7.3",
Name: "node-fetch",
Version: "1.7.3",
Indirect: true,
},
{
ID: "isomorphic-fetch@2.2.1",
Name: "isomorphic-fetch",
Version: "2.2.1",
Indirect: true,
DependsOn: []string{
"node-fetch@1.7.3",
},
},
{
ID: "fbjs@0.8.18",
Name: "fbjs",
Version: "0.8.18",
Indirect: true,
DependsOn: []string{
"isomorphic-fetch@2.2.1",
},
},
{
ID: "sanitize-html@1.20.0",
Name: "sanitize-html",
Version: "1.20.0",
},
{
ID: "styled-components@3.1.3",
Name: "styled-components",
Version: "3.1.3",
DependsOn: []string{
"fbjs@0.8.18",
},
},
},
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2022-0235",
PkgID: "node-fetch@1.7.3",
PkgName: "node-fetch",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
InstalledVersion: "1.7.3",
FixedVersion: "2.6.7, 3.1.1",
Status: dbTypes.StatusFixed,
},
{
VulnerabilityID: "CVE-2021-26539",
PkgID: "sanitize-html@1.20.0",
PkgName: "sanitize-html",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "MEDIUM",
},
InstalledVersion: "1.20.0",
FixedVersion: "2.3.1",
Status: dbTypes.StatusFixed,
},
},
},
want: `
package-lock.json (npm)
=======================
Total: 2 (MEDIUM: 1, HIGH: 1)
┌───────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├───────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch │ CVE-2022-0235 │ HIGH │ fixed │ 1.7.3 │ 2.6.7, 3.1.1 │ foobar │
├───────────────┼────────────────┼──────────┤ ├───────────────────┼───────────────┤ │
│ sanitize-html │ CVE-2021-26539 │ MEDIUM │ │ 1.20.0 │ 2.3.1 │ │
└───────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
Dependency Origin Tree (Reversed)
=================================
package-lock.json
├── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
│ └── ...(omitted)...
│ └── styled-components@3.1.3
└── sanitize-html@1.20.0, (MEDIUM: 1, HIGH: 0)
`,
},
{
name: "happy path with vulnerability origin graph without direct dependency info",
result: types.Result{
Target: "package-lock.json",
Class: types.ClassLangPkg,
Type: "npm",
Packages: []ftypes.Package{
{
ID: "node-fetch@1.7.3",
Name: "node-fetch",
Version: "1.7.3",
Indirect: true,
},
{
ID: "isomorphic-fetch@2.2.1",
Name: "isomorphic-fetch",
Version: "2.2.1",
Indirect: true,
DependsOn: []string{
"node-fetch@1.7.3",
},
},
{
ID: "fbjs@0.8.18",
Name: "fbjs",
Version: "0.8.18",
Indirect: true,
DependsOn: []string{
"isomorphic-fetch@2.2.1",
},
},
{
ID: "styled-components@3.1.3",
Name: "styled-components",
Version: "3.1.3",
Indirect: true,
DependsOn: []string{
"fbjs@0.8.18",
},
},
},
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2022-0235",
PkgID: "node-fetch@1.7.3",
PkgName: "node-fetch",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
InstalledVersion: "1.7.3",
FixedVersion: "2.6.7, 3.1.1",
Status: dbTypes.StatusFixed,
},
},
},
want: `
package-lock.json (npm)
=======================
Total: 1 (MEDIUM: 0, HIGH: 1)
┌────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch │ CVE-2022-0235 │ HIGH │ fixed │ 1.7.3 │ 2.6.7, 3.1.1 │ foobar │
└────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
Dependency Origin Tree (Reversed)
=================================
package-lock.json
└── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
└── ...(omitted)...
└── styled-components@3.1.3
`,
},
{
name: "show suppressed vulnerabilities",
result: types.Result{
Target: "test",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Status: dbTypes.StatusWillNotFix,
Vulnerability: dbTypes.Vulnerability{
Title: "title1",
Description: "desc1",
Severity: "HIGH",
},
},
},
ModifiedFindings: []types.ModifiedFinding{
{
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Statement: "Not exploitable",
Source: ".trivyignore.yaml",
Finding: types.DetectedVulnerability{
VulnerabilityID: "CVE-2020-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
Status: dbTypes.StatusWillNotFix,
Vulnerability: dbTypes.Vulnerability{
Title: "title2",
Description: "desc2",
Severity: "MEDIUM",
},
},
},
},
},
showSuppressed: true,
want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)
┌─────────┬───────────────┬──────────┬──────────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼───────────────┼──────────┼──────────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo │ CVE-2020-0001 │ HIGH │ will_not_fix │ 1.2.3 │ │ title1 │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└─────────┴───────────────┴──────────┴──────────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
Suppressed Vulnerabilities (Total: 1)
=====================================
┌─────────┬───────────────┬──────────┬─────────┬─────────────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Statement │ Source │
├─────────┼───────────────┼──────────┼─────────┼─────────────────┼───────────────────┤
│ bar │ CVE-2020-0002 │ MEDIUM │ ignored │ Not exploitable │ .trivyignore.yaml │
└─────────┴───────────────┴──────────┴─────────┴─────────────────┴───────────────────┘
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := table.NewVulnerabilityRenderer(tt.result, false, true, tt.showSuppressed, []dbTypes.Severity{
dbTypes.SeverityHigh,
dbTypes.SeverityMedium,
})
assert.Equal(t, tt.want, r.Render(), tt.name)
})
}
}

View File

@@ -5,7 +5,6 @@ import (
"errors"
"io"
"strings"
"sync"
"golang.org/x/xerrors"
@@ -49,7 +48,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e
Output: output,
Severities: option.Severities,
Tree: option.DependencyTree,
ShowMessageOnce: &sync.Once{},
ShowSuppressed: option.ShowSuppressed,
IncludeNonFailures: option.IncludeNonFailures,
Trace: option.Trace,
LicenseRiskThreshold: option.LicenseRiskThreshold,

View File

@@ -50,7 +50,7 @@ func TestResults_Failed(t *testing.T) {
{
Type: "Docker Security Check",
ID: "ID-001",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
},
},
},
@@ -67,7 +67,7 @@ func TestResults_Failed(t *testing.T) {
{
Type: "Docker Security Check",
ID: "ID-001",
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
},
},
},

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"sort"
"github.com/open-policy-agent/opa/rego"
@@ -13,7 +14,6 @@ import (
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vex"
)
@@ -35,12 +35,7 @@ type FilterOption struct {
// Filter filters out the report
func Filter(ctx context.Context, report types.Report, opt FilterOption) error {
// Filter out vulnerabilities based on the given VEX document.
if err := filterByVEX(report, opt); err != nil {
return xerrors.Errorf("VEX error: %w", err)
}
ignoreConf, err := getIgnoredFindings(ctx, opt.IgnoreFile)
ignoreConf, err := parseIgnoreFile(ctx, opt.IgnoreFile)
if err != nil {
return xerrors.Errorf("%s error: %w", opt.IgnoreFile, err)
}
@@ -50,6 +45,12 @@ func Filter(ctx context.Context, report types.Report, opt FilterOption) error {
return xerrors.Errorf("unable to filter vulnerabilities: %w", err)
}
}
// Filter out vulnerabilities based on the given VEX document.
if err = filterByVEX(report, opt); err != nil {
return xerrors.Errorf("VEX error: %w", err)
}
return nil
}
@@ -60,29 +61,17 @@ func FilterResult(ctx context.Context, result *types.Result, ignoreConf IgnoreCo
return s.String()
})
filteredVulns := filterVulnerabilities(result, severities, opt.IgnoreStatuses, ignoreConf.Vulnerabilities)
misconfSummary, filteredMisconfs := filterMisconfigurations(result, severities, opt.IncludeNonFailures, ignoreConf.Misconfigurations)
result.Secrets = filterSecrets(result, severities, ignoreConf.Secrets)
result.Licenses = filterLicenses(result.Licenses, severities, opt.IgnoreLicenses, ignoreConf.Licenses)
filterVulnerabilities(result, severities, opt.IgnoreStatuses, ignoreConf)
filterMisconfigurations(result, severities, opt.IncludeNonFailures, ignoreConf)
filterSecrets(result, severities, ignoreConf)
filterLicenses(result, severities, opt.IgnoreLicenses, ignoreConf)
var ignoredMisconfs int
if opt.PolicyFile != "" {
var err error
var ignored int
filteredVulns, filteredMisconfs, ignored, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, opt.PolicyFile)
if err != nil {
if err := applyPolicy(ctx, result, opt.PolicyFile); err != nil {
return xerrors.Errorf("failed to apply the policy: %w", err)
}
ignoredMisconfs += ignored
}
sort.Sort(types.BySeverity(filteredVulns))
result.Vulnerabilities = filteredVulns
result.MisconfSummary = misconfSummary
if result.MisconfSummary != nil {
result.MisconfSummary.Exceptions += ignoredMisconfs
}
result.Misconfigurations = filteredMisconfs
sort.Sort(types.BySeverity(result.Vulnerabilities))
return nil
}
@@ -102,15 +91,13 @@ func filterByVEX(report types.Report, opt FilterOption) error {
if len(result.Vulnerabilities) == 0 {
continue
}
report.Results[i].Vulnerabilities = vexDoc.Filter(result.Vulnerabilities)
vexDoc.Filter(&report.Results[i])
}
return nil
}
func filterVulnerabilities(result *types.Result, severities []string, ignoreStatuses []dbTypes.Status,
ignoreFindings IgnoreFindings) []types.DetectedVulnerability {
func filterVulnerabilities(result *types.Result, severities []string, ignoreStatuses []dbTypes.Status, ignoreConfig IgnoreConfig) {
uniqVulns := make(map[string]types.DetectedVulnerability)
for _, vuln := range result.Vulnerabilities {
if vuln.Severity == "" {
vuln.Severity = dbTypes.SeverityUnknown.String()
@@ -123,9 +110,12 @@ func filterVulnerabilities(result *types.Result, severities []string, ignoreStat
// Filter by status
case slices.Contains(ignoreStatuses, vuln.Status):
continue
}
// Filter by ignore file
case ignoreFindings.Match(result.Target, vuln.VulnerabilityID) ||
ignoreFindings.Match(vuln.PkgPath, vuln.VulnerabilityID):
if f := ignoreConfig.MatchVulnerability(vuln.VulnerabilityID, result.Target, vuln.PkgPath); f != nil {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(vuln, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath))
continue
}
@@ -136,96 +126,116 @@ func filterVulnerabilities(result *types.Result, severities []string, ignoreStat
}
uniqVulns[key] = vuln
}
if len(uniqVulns) == 0 {
return nil
// Override the detected vulnerabilities
result.Vulnerabilities = maps.Values(uniqVulns)
if len(result.Vulnerabilities) == 0 {
result.Vulnerabilities = nil
}
return maps.Values(uniqVulns)
}
func filterMisconfigurations(result *types.Result, severities []string, includeNonFailures bool,
ignoreMisconfs IgnoreFindings) (*types.MisconfSummary, []types.DetectedMisconfiguration) {
ignoreConfig IgnoreConfig) {
var filtered []types.DetectedMisconfiguration
summary := new(types.MisconfSummary)
result.MisconfSummary = new(types.MisconfSummary)
for _, misconf := range result.Misconfigurations {
// Filter by severity
if !slices.Contains(severities, misconf.Severity) {
// Filter by severity
continue
} else if ignoreMisconfs.Match(result.Target, misconf.ID) || ignoreMisconfs.Match(result.Target, misconf.AVDID) {
// Filter misconfigurations by ignore file
summary.Exceptions++
}
// Filter by ignore file
if f := ignoreConfig.MatchMisconfiguration(misconf.ID, misconf.AVDID, result.Target); f != nil {
result.MisconfSummary.Exceptions++
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(misconf, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath))
continue
}
// Count successes, failures, and exceptions
summarize(misconf.Status, summary)
summarize(misconf.Status, result.MisconfSummary)
if misconf.Status != types.StatusFailure && !includeNonFailures {
if misconf.Status != types.MisconfStatusFailure && !includeNonFailures {
continue
}
filtered = append(filtered, misconf)
}
if summary.Empty() {
return nil, nil
result.Misconfigurations = filtered
if result.MisconfSummary.Empty() {
result.Misconfigurations = nil
result.MisconfSummary = nil
}
return summary, filtered
}
func filterSecrets(result *types.Result, severities []string, ignoreFindings IgnoreFindings) []ftypes.SecretFinding {
var filtered []ftypes.SecretFinding
func filterSecrets(result *types.Result, severities []string, ignoreConfig IgnoreConfig) {
var filtered []types.DetectedSecret
for _, secret := range result.Secrets {
if !slices.Contains(severities, secret.Severity) {
// Filter by severity
continue
} else if ignoreFindings.Match(result.Target, secret.RuleID) {
} else if f := ignoreConfig.MatchSecret(secret.RuleID, result.Target); f != nil {
// Filter by ignore file
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(secret, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath))
continue
}
filtered = append(filtered, secret)
}
return filtered
result.Secrets = filtered
}
func filterLicenses(licenses []types.DetectedLicense, severities, ignoreLicenseNames []string, ignoreFindings IgnoreFindings) []types.DetectedLicense {
func filterLicenses(result *types.Result, severities, ignoreLicenseNames []string, ignoreConfig IgnoreConfig) {
// Merge ignore license names into ignored findings
var ignoreLicenses IgnoreFindings
for _, licenseName := range ignoreLicenseNames {
ignoreFindings = append(ignoreFindings, IgnoreFinding{
ignoreLicenses = append(ignoreLicenses, IgnoreFinding{
ID: licenseName,
})
}
var filtered []types.DetectedLicense
for _, l := range licenses {
for _, l := range result.Licenses {
// Filter by severity
if !slices.Contains(severities, l.Severity) {
// Filter by severity
continue
} else if ignoreFindings.Match(l.FilePath, l.Name) {
// Filter by ignore file or ignore license names
continue
}
// Filter by `--ignored-licenses`
if f := ignoreLicenses.Match(l.Name, l.FilePath); f != nil {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(l, types.FindingStatusIgnored, "", "--ignored-licenses"))
continue
}
// Filter by ignore file
if f := ignoreConfig.MatchLicense(l.Name, l.FilePath); f != nil {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(l, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath))
continue
}
filtered = append(filtered, l)
}
return filtered
result.Licenses = filtered
}
func summarize(status types.MisconfStatus, summary *types.MisconfSummary) {
switch status {
case types.StatusFailure:
case types.MisconfStatusFailure:
summary.Failures++
case types.StatusPassed:
case types.MisconfStatusPassed:
summary.Successes++
case types.StatusException:
case types.MisconfStatusException:
summary.Exceptions++
}
}
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misconfs []types.DetectedMisconfiguration,
policyFile string) ([]types.DetectedVulnerability, []types.DetectedMisconfiguration, int, error) {
func applyPolicy(ctx context.Context, result *types.Result, policyFile string) error {
policy, err := os.ReadFile(policyFile)
if err != nil {
return nil, nil, 0, xerrors.Errorf("unable to read the policy file: %w", err)
return xerrors.Errorf("unable to read the policy file: %w", err)
}
query, err := rego.New(
@@ -234,38 +244,53 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
rego.Module("trivy.rego", string(policy)),
).PrepareForEval(ctx)
if err != nil {
return nil, nil, 0, xerrors.Errorf("unable to prepare for eval: %w", err)
return xerrors.Errorf("unable to prepare for eval: %w", err)
}
policyFile = filepath.ToSlash(filepath.Clean(policyFile))
// Vulnerabilities
var filteredVulns []types.DetectedVulnerability
for _, vuln := range vulns {
for _, vuln := range result.Vulnerabilities {
ignored, err := evaluate(ctx, query, vuln)
if err != nil {
return nil, nil, 0, err
return err
}
if ignored {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(vuln, types.FindingStatusIgnored, "Filtered by Rego", policyFile))
continue
}
filteredVulns = append(filteredVulns, vuln)
}
result.Vulnerabilities = filteredVulns
// Misconfigurations
var ignoredMisconfs int
var filteredMisconfs []types.DetectedMisconfiguration
for _, misconf := range misconfs {
for _, misconf := range result.Misconfigurations {
ignored, err := evaluate(ctx, query, misconf)
if err != nil {
return nil, nil, 0, err
return err
}
if ignored {
ignoredMisconfs++
result.MisconfSummary.Exceptions++
switch misconf.Status {
case types.MisconfStatusFailure:
result.MisconfSummary.Failures--
case types.MisconfStatusPassed:
result.MisconfSummary.Successes--
}
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(misconf, types.FindingStatusIgnored, "Filtered by Rego", policyFile))
continue
}
filteredMisconfs = append(filteredMisconfs, misconf)
}
return filteredVulns, filteredMisconfs, ignoredMisconfs, nil
result.Misconfigurations = filteredMisconfs
return nil
}
func evaluate(ctx context.Context, query rego.PreparedEvalQuery, input interface{}) (bool, error) {
results, err := query.Eval(ctx, rego.EvalInput(input))
if err != nil {

View File

@@ -17,6 +17,143 @@ import (
)
func TestFilter(t *testing.T) {
var (
vuln1 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "foo",
Version: "1.2.3",
},
},
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
}
vuln2 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0002",
PkgName: "foo",
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "foo",
Version: "4.5.6",
},
},
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
}
vuln3 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
}
vuln4 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0004",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
}
vuln5 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0005",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
}
vuln6 = types.DetectedVulnerability{
VulnerabilityID: "CVE-2019-0006",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
}
misconf1 = types.DetectedMisconfiguration{
Type: "Kubernetes Security Check",
ID: "ID100",
AVDID: "AVD-ID100",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityHigh.String(),
Status: types.MisconfStatusFailure,
}
misconf2 = types.DetectedMisconfiguration{
Type: "Kubernetes Security Check",
ID: "ID200",
AVDID: "AVD-ID200",
Title: "Bad Pod",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.MisconfStatusPassed,
}
misconf3 = types.DetectedMisconfiguration{
Type: "Kubernetes Security Check",
ID: "ID300",
AVDID: "AVD-ID300",
Title: "Bad Job",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.MisconfStatusFailure,
}
secret1 = types.DetectedSecret{
RuleID: "generic-wanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should pass filter on rule id",
StartLine: 1,
EndLine: 2,
Match: "*****",
}
secret2 = types.DetectedSecret{
RuleID: "generic-unwanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should not pass filter on rule id",
StartLine: 3,
EndLine: 4,
Match: "*****",
}
secret3 = types.DetectedSecret{
RuleID: "generic-unwanted-rule2",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should not pass filter on rule id",
StartLine: 5,
EndLine: 6,
Match: "*****",
}
license1 = types.DetectedLicense{
Name: "GPL-3.0",
Severity: dbTypes.SeverityLow.String(),
FilePath: "usr/share/gcc/python/libstdcxx/v6/__init__.py",
Category: "restricted",
Confidence: 1,
}
license2 = types.DetectedLicense{
Name: "GPL-3.0",
Severity: dbTypes.SeverityLow.String(),
FilePath: "usr/share/gcc/python/libstdcxx/v6/printers.py",
Category: "restricted",
Confidence: 1,
}
)
type args struct {
report types.Report
severities []dbTypes.Severity
@@ -37,44 +174,14 @@ func TestFilter(t *testing.T) {
Results: []types.Result{
{
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
vuln1,
vuln2,
},
Misconfigurations: []types.DetectedMisconfiguration{
{
Type: "Kubernetes Security Check",
ID: "ID100",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusFailure,
},
{
Type: "Kubernetes Security Check",
ID: "ID200",
Title: "Bad Pod",
Message: "something bad",
Severity: dbTypes.SeverityMedium.String(),
Status: types.StatusPassed,
},
misconf1,
misconf2,
},
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "generic-critical-rule",
Severity: dbTypes.SeverityCritical.String(),
@@ -104,15 +211,7 @@ func TestFilter(t *testing.T) {
Results: []types.Result{
{
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
vuln2,
},
MisconfSummary: &types.MisconfSummary{
Successes: 0,
@@ -120,16 +219,9 @@ func TestFilter(t *testing.T) {
Exceptions: 0,
},
Misconfigurations: []types.DetectedMisconfiguration{
{
Type: "Kubernetes Security Check",
ID: "ID100",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusFailure,
},
misconf1,
},
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "generic-critical-rule",
Severity: dbTypes.SeverityCritical.String(),
@@ -150,40 +242,8 @@ func TestFilter(t *testing.T) {
Results: types.Results{
types.Result{
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "foo",
Version: "1.2.3",
},
},
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "bar",
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "bar",
Version: "4.5.6",
},
},
InstalledVersion: "4.5.6",
FixedVersion: "4.5.7",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
vuln1,
vuln2,
},
},
},
@@ -201,22 +261,15 @@ func TestFilter(t *testing.T) {
Results: types.Results{
types.Result{
Vulnerabilities: []types.DetectedVulnerability{
vuln2,
},
ModifiedFindings: []types.ModifiedFinding{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "bar",
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/aquasecurity",
Name: "bar",
Version: "4.5.6",
},
},
InstalledVersion: "4.5.6",
FixedVersion: "4.5.7",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusNotAffected,
Statement: "vulnerable_code_not_in_execute_path",
Source: "OpenVEX",
Finding: vuln1,
},
},
},
@@ -231,25 +284,8 @@ func TestFilter(t *testing.T) {
types.Result{
Target: "debian:11 (debian 11)",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2018-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Status: dbTypes.StatusWillNotFix,
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
vuln1,
vuln2,
},
},
},
@@ -277,98 +313,28 @@ func TestFilter(t *testing.T) {
Target: "package-lock.json",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0002",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2022-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2022-0002",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2022-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
vuln1, // ignored
vuln2, // filtered by severity
vuln3,
vuln4,
vuln5, // ignored
vuln6, // ignored
},
},
{
Target: "Dockerfile",
Target: "deployment.yaml",
Class: types.ClassConfig,
Misconfigurations: []types.DetectedMisconfiguration{
{
Type: "Kubernetes Security Check",
ID: "ID100",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.StatusFailure,
},
misconf1, // filtered by severity
misconf2,
misconf3,
},
},
{
Secrets: []ftypes.SecretFinding{
{
RuleID: "generic-wanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should pass filter on rule id",
StartLine: 1,
EndLine: 2,
Match: "*****",
},
{
RuleID: "generic-unwanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should not pass filter on rule id",
StartLine: 3,
EndLine: 4,
Match: "*****",
},
Target: "config.yaml",
Secrets: []types.DetectedSecret{
secret1,
secret2,
},
},
},
@@ -382,44 +348,58 @@ func TestFilter(t *testing.T) {
Target: "package-lock.json",
Class: types.ClassLangPkg,
Vulnerabilities: []types.DetectedVulnerability{
vuln3,
vuln4,
},
ModifiedFindings: []types.ModifiedFinding{
{
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore",
Finding: vuln1,
},
{
VulnerabilityID: "CVE-2022-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore",
Finding: vuln5,
},
{
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore",
Finding: vuln6,
},
},
},
{
Target: "Dockerfile",
Target: "deployment.yaml",
Class: types.ClassConfig,
MisconfSummary: &types.MisconfSummary{
Successes: 0,
Successes: 1,
Failures: 0,
Exceptions: 1,
},
ModifiedFindings: []types.ModifiedFinding{
{
Type: types.FindingTypeMisconfiguration,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore",
Finding: misconf3,
},
},
},
{
Secrets: []ftypes.SecretFinding{
Target: "config.yaml",
Secrets: []types.DetectedSecret{
secret1,
},
ModifiedFindings: []types.ModifiedFinding{
{
RuleID: "generic-wanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should pass filter on rule id",
StartLine: 1,
EndLine: 2,
Match: "*****",
Type: types.FindingTypeSecret,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore",
Finding: secret2,
},
},
},
@@ -434,146 +414,35 @@ func TestFilter(t *testing.T) {
{
Target: "foo/package-lock.json",
Vulnerabilities: []types.DetectedVulnerability{
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0002",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
PkgPath: "bar/package.json",
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2019-0004",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0005",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
VulnerabilityID: "CVE-2019-0006",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
vuln1, // ignored
vuln2, // filtered by severity
vuln3, // ignored
vuln4,
vuln5, // ignored
vuln6,
},
},
{
Target: "app/Dockerfile",
Misconfigurations: []types.DetectedMisconfiguration{
{
// this misconfiguration is ignored
Type: "Kubernetes Security Check",
ID: "ID100",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.StatusFailure,
},
{
// this misconfiguration is ignored
Type: "Kubernetes Security Check",
ID: "ID200",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.StatusFailure,
},
{
Type: "Kubernetes Security Check",
ID: "ID300",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.StatusFailure,
},
misconf1, // filtered by severity
misconf2, // ignored
misconf3,
},
},
{
Target: "config.yaml",
Secrets: []ftypes.SecretFinding{
{
RuleID: "generic-wanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should pass filter on rule id",
StartLine: 1,
EndLine: 2,
Match: "*****",
},
{
// this secret is ignored
RuleID: "generic-unwanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should not pass filter on rule id",
StartLine: 3,
EndLine: 4,
Match: "*****",
},
{
// this secret is ignored
RuleID: "generic-unwanted-rule2",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should not pass filter on rule id",
StartLine: 5,
EndLine: 6,
Match: "*****",
},
Secrets: []types.DetectedSecret{
secret1,
secret2, // ignored
secret3, // ignored
},
},
{
Target: "LICENSE.txt",
Licenses: []types.DetectedLicense{
{
// this license is ignored
Name: "GPL-3.0",
Severity: dbTypes.SeverityLow.String(),
FilePath: "usr/share/gcc/python/libstdcxx/v6/__init__.py",
Category: "restricted",
Confidence: 1,
},
{
Name: "GPL-3.0",
Severity: dbTypes.SeverityLow.String(),
FilePath: "usr/share/gcc/python/libstdcxx/v6/printers.py",
Category: "restricted",
Confidence: 1,
},
license1, // ignored
license2,
},
},
},
@@ -586,23 +455,27 @@ func TestFilter(t *testing.T) {
{
Target: "foo/package-lock.json",
Vulnerabilities: []types.DetectedVulnerability{
vuln4,
vuln6,
},
ModifiedFindings: []types.ModifiedFinding{
{
VulnerabilityID: "CVE-2019-0004",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: vuln1,
},
{
VulnerabilityID: "CVE-2019-0006",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: vuln3,
},
{
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: vuln5,
},
},
},
@@ -611,40 +484,51 @@ func TestFilter(t *testing.T) {
MisconfSummary: &types.MisconfSummary{
Successes: 0,
Failures: 1,
Exceptions: 2,
Exceptions: 1,
},
Misconfigurations: []types.DetectedMisconfiguration{
misconf3,
},
ModifiedFindings: []types.ModifiedFinding{
{
Type: "Kubernetes Security Check",
ID: "ID300",
Title: "Bad Deployment",
Message: "something bad",
Severity: dbTypes.SeverityLow.String(),
Status: types.StatusFailure,
Type: types.FindingTypeMisconfiguration,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: misconf2,
},
},
},
{
Target: "config.yaml",
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
secret1,
},
ModifiedFindings: []types.ModifiedFinding{
{
RuleID: "generic-wanted-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Secret that should pass filter on rule id",
StartLine: 1,
EndLine: 2,
Match: "*****",
Type: types.FindingTypeSecret,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: secret2,
},
{
Type: types.FindingTypeSecret,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: secret3,
},
},
},
{
Target: "LICENSE.txt",
Licenses: []types.DetectedLicense{
license2,
},
ModifiedFindings: []types.ModifiedFinding{
{
Name: "GPL-3.0",
Severity: dbTypes.SeverityLow.String(),
FilePath: "usr/share/gcc/python/libstdcxx/v6/printers.py",
Category: "restricted",
Confidence: 1,
Type: types.FindingTypeLicense,
Status: types.FindingStatusIgnored,
Source: "testdata/.trivyignore.yaml",
Finding: license1,
},
},
},
@@ -652,60 +536,35 @@ func TestFilter(t *testing.T) {
},
},
{
name: "policy file",
name: "policy file for vulnerabilities",
args: args{
report: types.Report{
Results: types.Results{
{
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0002",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
{
// this vulnerability is ignored
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
},
vuln1,
vuln2, // ignored by severity
vuln3, // ignored by policy
},
},
},
},
severities: []dbTypes.Severity{dbTypes.SeverityLow},
policyFile: "./testdata/test.rego",
policyFile: "./testdata/ignore-vuln.rego",
},
want: types.Report{
Results: types.Results{
{
Vulnerabilities: []types.DetectedVulnerability{
vuln1,
},
ModifiedFindings: []types.ModifiedFinding{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityLow.String(),
},
Type: types.FindingTypeVulnerability,
Status: types.FindingStatusIgnored,
Statement: "Filtered by Rego",
Source: "testdata/ignore-vuln.rego",
Finding: vuln3,
},
},
},
@@ -713,60 +572,43 @@ func TestFilter(t *testing.T) {
},
},
{
name: "ignore file for misconf",
name: "policy file for misconfigurations",
args: args{
report: types.Report{
Results: types.Results{
{
Misconfigurations: []types.DetectedMisconfiguration{
{
ID: "AVD-TEST-0001",
AVDID: "AVD-TEST-0001",
Title: "test-0001",
Description: "foo",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusFailure,
},
{
ID: "AVD-TEST-0002",
AVDID: "AVD-TEST-0002",
Title: "test-0002",
Description: "bar",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusPassed,
},
{
// this misconf is ignored
ID: "AVD-TEST-0003",
AVDID: "AVD-TEST-0003",
Title: "test-0003",
Description: "baz",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusFailure,
},
misconf1,
misconf2,
misconf3, // ignored by policy
},
},
},
},
severities: []dbTypes.Severity{dbTypes.SeverityHigh},
policyFile: "./testdata/test-ignore-policy-misconf.rego",
severities: []dbTypes.Severity{
dbTypes.SeverityLow,
dbTypes.SeverityHigh,
},
policyFile: "./testdata/ignore-misconf.rego",
},
want: types.Report{
Results: types.Results{
{
MisconfSummary: &types.MisconfSummary{
Successes: 1,
Failures: 2,
Failures: 1,
Exceptions: 1,
},
Misconfigurations: []types.DetectedMisconfiguration{
misconf1,
},
ModifiedFindings: []types.ModifiedFinding{
{
ID: "AVD-TEST-0001",
AVDID: "AVD-TEST-0001",
Title: "test-0001",
Description: "foo",
Severity: dbTypes.SeverityHigh.String(),
Status: types.StatusFailure,
Type: types.FindingTypeMisconfiguration,
Status: types.FindingStatusIgnored,
Statement: "Filtered by Rego",
Source: "testdata/ignore-misconf.rego",
Finding: misconf3,
},
},
},

View File

@@ -43,7 +43,7 @@ type IgnoreFinding struct {
type IgnoreFindings []IgnoreFinding
func (f *IgnoreFindings) Match(path, id string) bool {
func (f *IgnoreFindings) Match(id, path string) *IgnoreFinding {
for _, finding := range *f {
if id != finding.ID {
continue
@@ -53,10 +53,10 @@ func (f *IgnoreFindings) Match(path, id string) bool {
continue
}
log.Logger.Debugw("Ignored", log.String("id", id), log.String("path", path))
return true
return &finding
}
return false
return nil
}
func pathMatch(path string, patterns []string) bool {
@@ -96,13 +96,48 @@ func (f *IgnoreFindings) Filter(ctx context.Context) {
// IgnoreConfig represents the structure of .trivyignore.yaml.
type IgnoreConfig struct {
FilePath string
Vulnerabilities IgnoreFindings `yaml:"vulnerabilities"`
Misconfigurations IgnoreFindings `yaml:"misconfigurations"`
Secrets IgnoreFindings `yaml:"secrets"`
Licenses IgnoreFindings `yaml:"licenses"`
}
func getIgnoredFindings(ctx context.Context, ignoreFile string) (IgnoreConfig, error) {
func (c *IgnoreConfig) MatchVulnerability(vulnID, filePath, pkgPath string) *IgnoreFinding {
paths := []string{
filePath,
pkgPath,
}
for _, p := range paths {
if f := c.Vulnerabilities.Match(vulnID, p); f != nil {
return f
}
}
return nil
}
func (c *IgnoreConfig) MatchMisconfiguration(misconfID, avdID, filePath string) *IgnoreFinding {
ids := []string{
misconfID,
avdID,
}
for _, id := range ids {
if f := c.Misconfigurations.Match(id, filePath); f != nil {
return f
}
}
return nil
}
func (c *IgnoreConfig) MatchSecret(secretID, filePath string) *IgnoreFinding {
return c.Secrets.Match(secretID, filePath)
}
func (c *IgnoreConfig) MatchLicense(licenseID, filePath string) *IgnoreFinding {
return c.Licenses.Match(licenseID, filePath)
}
func parseIgnoreFile(ctx context.Context, ignoreFile string) (IgnoreConfig, error) {
var conf IgnoreConfig
if _, err := os.Stat(ignoreFile); errors.Is(err, fs.ErrNotExist) {
// .trivyignore doesn't necessarily exist
@@ -132,6 +167,7 @@ func getIgnoredFindings(ctx context.Context, ignoreFile string) (IgnoreConfig, e
conf.Misconfigurations.Filter(ctx)
conf.Secrets.Filter(ctx)
conf.Licenses.Filter(ctx)
conf.FilePath = filepath.ToSlash(filepath.Clean(ignoreFile))
return conf, nil
}

View File

@@ -1,12 +1,12 @@
# vulnerabilities
CVE-2019-0001
CVE-2019-0002
CVE-2022-0001 exp:2020-01-01
CVE-2022-0002 exp:9999-01-01
CVE-2022-0003 exp:9999-01-01 key2:value2
CVE-2019-0004 exp:2020-01-01
CVE-2019-0005 exp:9999-01-01
CVE-2019-0006 exp:9999-01-01 key2:value2
# misconfigurations
ID100
ID300
# secrets
generic-unwanted-rule

View File

@@ -5,5 +5,5 @@ import data.lib.trivy
default ignore=false
ignore {
input.AVDID != "AVD-TEST-0001"
input.AVDID != "AVD-ID100"
}

View File

@@ -393,7 +393,7 @@ func ConvertFromRPCResults(rpcResults []*scanner.Result) []types.Result {
Type: ftypes.TargetType(result.Type),
Packages: ConvertFromRPCPkgs(result.Packages),
CustomResources: ConvertFromRPCCustomResources(result.CustomResources),
Secrets: ConvertFromRPCSecretFindings(result.Secrets),
Secrets: ConvertFromRPCDetectedSecrets(result.Secrets),
Licenses: ConvertFromRPCDetectedLicenses(result.Licenses),
})
}
@@ -461,6 +461,15 @@ func ConvertFromRPCCode(rpcCode *common.Code) ftypes.Code {
}
}
func ConvertFromRPCDetectedSecrets(rpcFindings []*common.SecretFinding) []types.DetectedSecret {
if len(rpcFindings) == 0 {
return nil
}
return lo.Map(ConvertFromRPCSecretFindings(rpcFindings), func(s ftypes.SecretFinding, _ int) types.DetectedSecret {
return types.DetectedSecret(s)
})
}
func ConvertFromRPCSecretFindings(rpcFindings []*common.SecretFinding) []ftypes.SecretFinding {
var findings []ftypes.SecretFinding
for _, finding := range rpcFindings {
@@ -913,16 +922,19 @@ func ConvertToMissingBlobsRequest(imageID string, layerIDs []string) *cache.Miss
func ConvertToRPCScanResponse(results types.Results, fos ftypes.OS) *scanner.ScanResponse {
var rpcResults []*scanner.Result
for _, result := range results {
secretFindings := lo.Map(result.Secrets, func(s types.DetectedSecret, _ int) ftypes.SecretFinding {
return ftypes.SecretFinding(s)
})
rpcResults = append(rpcResults, &scanner.Result{
Target: result.Target,
Class: string(result.Class),
Type: string(result.Type),
Packages: ConvertToRPCPkgs(result.Packages),
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
Misconfigurations: ConvertToRPCMisconfs(result.Misconfigurations),
Packages: ConvertToRPCPkgs(result.Packages),
CustomResources: ConvertToRPCCustomResources(result.CustomResources),
Secrets: ConvertToRPCSecretFindings(result.Secrets),
Secrets: ConvertToRPCSecretFindings(secretFindings),
Licenses: ConvertToRPCLicenses(result.Licenses),
CustomResources: ConvertToRPCCustomResources(result.CustomResources),
})
}

View File

@@ -885,7 +885,7 @@ func TestMarshaler_Marshal(t *testing.T) {
{
Target: "key.pem",
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
Secrets: []types.DetectedSecret{
{
RuleID: "private-key",
Category: "AsymmetricPrivateKey",

View File

@@ -229,16 +229,16 @@ func (s Scanner) MisconfsToResults(misconfs []ftypes.Misconfiguration) types.Res
var detected []types.DetectedMisconfiguration
for _, f := range misconf.Failures {
detected = append(detected, toDetectedMisconfiguration(f, dbTypes.SeverityCritical, types.StatusFailure, misconf.Layer))
detected = append(detected, toDetectedMisconfiguration(f, dbTypes.SeverityCritical, types.MisconfStatusFailure, misconf.Layer))
}
for _, w := range misconf.Warnings {
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityMedium, types.StatusFailure, misconf.Layer))
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityMedium, types.MisconfStatusFailure, misconf.Layer))
}
for _, w := range misconf.Successes {
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.StatusPassed, misconf.Layer))
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusPassed, misconf.Layer))
}
for _, w := range misconf.Exceptions {
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.StatusException, misconf.Layer))
detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusException, misconf.Layer))
}
results = append(results, types.Result{
@@ -266,9 +266,11 @@ func (s Scanner) secretsToResults(secrets []ftypes.Secret, options types.ScanOpt
log.Logger.Debugf("Secret file: %s", secret.FilePath)
results = append(results, types.Result{
Target: secret.FilePath,
Class: types.ClassSecret,
Secrets: secret.Findings,
Target: secret.FilePath,
Class: types.ClassSecret,
Secrets: lo.Map(secret.Findings, func(secret ftypes.SecretFinding, index int) types.DetectedSecret {
return types.DetectedSecret(secret)
}),
})
}
return results

View File

@@ -981,7 +981,7 @@ func TestScanner_Scan(t *testing.T) {
Message: "something bad",
Namespace: "main.kubernetes.id100",
Severity: "HIGH",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -997,7 +997,7 @@ func TestScanner_Scan(t *testing.T) {
References: []string{
"https://avd.aquasec.com/misconfig/id200",
},
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -1016,7 +1016,7 @@ func TestScanner_Scan(t *testing.T) {
Message: "No issues found",
Namespace: "main.kubernetes.id300",
Severity: "MEDIUM",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -1028,7 +1028,7 @@ func TestScanner_Scan(t *testing.T) {
Message: "No issues found",
Namespace: "main.kubernetes.id100",
Severity: "HIGH",
Status: types.StatusException,
Status: types.MisconfStatusException,
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -1199,7 +1199,7 @@ func TestScanner_Scan(t *testing.T) {
Description: "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
Severity: "HIGH",
Resolution: "Add 'USER <non root user name>' line to the Dockerfile",
Status: types.StatusFailure,
Status: types.MisconfStatusFailure,
PrimaryURL: "https://avd.aquasec.com/misconfig/ds002",
References: []string{"https://avd.aquasec.com/misconfig/ds002"},
CauseMetadata: ftypes.CauseMetadata{
@@ -1219,7 +1219,7 @@ func TestScanner_Scan(t *testing.T) {
Description: "When using a 'FROM' statement you should use a specific tag to avoid uncontrolled behavior when the image is updated.",
Severity: "MEDIUM",
Resolution: "Add a tag to the image in the 'FROM' statement",
Status: types.StatusPassed,
Status: types.MisconfStatusPassed,
CauseMetadata: ftypes.CauseMetadata{
Provider: "Dockerfile",
Service: "general",

47
pkg/types/finding.go Normal file
View File

@@ -0,0 +1,47 @@
package types
type FindingType string
type FindingStatus string
const (
FindingTypeVulnerability FindingType = "vulnerability"
FindingTypeMisconfiguration FindingType = "misconfiguration"
FindingTypeSecret FindingType = "secret"
FindingTypeLicense FindingType = "license"
FindingStatusIgnored FindingStatus = "ignored" // Trivy
FindingStatusUnknown FindingStatus = "unknown" // Trivy
FindingStatusNotAffected FindingStatus = "not_affected" // VEX
FindingStatusAffected FindingStatus = "affected" // VEX
FindingStatusFixed FindingStatus = "fixed" // VEX
FindingStatusUnderInvestigation FindingStatus = "under_investigation" // VEX
)
// Finding represents one of the findings that Trivy can detect,
// such as vulnerabilities, misconfigurations, secrets, and licenses.
type finding interface {
findingType() FindingType
}
// ModifiedFinding represents a security finding that has been modified by an external source,
// such as .trivyignore and VEX. Currently, it is primarily used to account for vulnerabilities
// that are ignored via .trivyignore or identified as not impactful through VEX.
// However, it is planned to also store vulnerabilities whose severity has been adjusted by VEX,
// or that have been detected through Wasm modules in the future.
type ModifiedFinding struct {
Type FindingType
Status FindingStatus
Statement string
Source string
Finding finding // one of findings
}
func NewModifiedFinding(f finding, status FindingStatus, statement, source string) ModifiedFinding {
return ModifiedFinding{
Type: f.findingType(),
Status: status,
Statement: statement,
Source: source,
Finding: f,
}
}

View File

@@ -29,3 +29,5 @@ type DetectedLicense struct {
// Link is a SPDX link of the license
Link string
}
func (DetectedLicense) findingType() FindingType { return FindingTypeLicense }

View File

@@ -28,17 +28,14 @@ type DetectedMisconfiguration struct {
type MisconfStatus string
const (
// StatusPassed represents successful status
StatusPassed MisconfStatus = "PASS"
// MisconfStatusPassed represents successful status
MisconfStatusPassed MisconfStatus = "PASS"
// StatusFailure represents failure status
StatusFailure MisconfStatus = "FAIL"
// MisconfStatusFailure represents failure status
MisconfStatusFailure MisconfStatus = "FAIL"
// StatusException Passed represents the status of exception
StatusException MisconfStatus = "EXCEPTION"
// MisconfStatusException Passed represents the status of exception
MisconfStatusException MisconfStatus = "EXCEPTION"
)
// GetID retrun misconfig ID
func (mc *DetectedMisconfiguration) GetID() string {
return mc.AVDID
}
func (DetectedMisconfiguration) findingType() FindingType { return FindingTypeMisconfiguration }

View File

@@ -108,14 +108,19 @@ type Result struct {
Vulnerabilities []DetectedVulnerability `json:"Vulnerabilities,omitempty"`
MisconfSummary *MisconfSummary `json:"MisconfSummary,omitempty"`
Misconfigurations []DetectedMisconfiguration `json:"Misconfigurations,omitempty"`
Secrets []ftypes.SecretFinding `json:"Secrets,omitempty"`
Secrets []DetectedSecret `json:"Secrets,omitempty"`
Licenses []DetectedLicense `json:"Licenses,omitempty"`
CustomResources []ftypes.CustomResource `json:"CustomResources,omitempty"`
// ModifiedFindings holds a list of findings that have been modified from their original state.
// This can include vulnerabilities that have been marked as ignored, not affected, or have had
// their severity adjusted. It is currently available only in the table format.
ModifiedFindings []ModifiedFinding `json:"-"`
}
func (r *Result) IsEmpty() bool {
return len(r.Packages) == 0 && len(r.Vulnerabilities) == 0 && len(r.Misconfigurations) == 0 &&
len(r.Secrets) == 0 && len(r.Licenses) == 0 && len(r.CustomResources) == 0
len(r.Secrets) == 0 && len(r.Licenses) == 0 && len(r.CustomResources) == 0 && len(r.ModifiedFindings) == 0
}
type MisconfSummary struct {
@@ -135,7 +140,7 @@ func (results Results) Failed() bool {
return true
}
for _, m := range r.Misconfigurations {
if m.Status == StatusFailure {
if m.Status == MisconfStatusFailure {
return true
}
}

9
pkg/types/secret.go Normal file
View File

@@ -0,0 +1,9 @@
package types
import (
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)
type DetectedSecret ftypes.SecretFinding
func (DetectedSecret) findingType() FindingType { return FindingTypeSecret }

View File

@@ -30,10 +30,7 @@ type DetectedVulnerability struct {
types.Vulnerability
}
// GetID retrun Vulnerability ID
func (vuln *DetectedVulnerability) GetID() string {
return vuln.VulnerabilityID
}
func (DetectedVulnerability) findingType() FindingType { return FindingTypeVulnerability }
// BySeverity implements sort.Interface based on the Severity field.
type BySeverity []DetectedVulnerability

View File

@@ -23,8 +23,8 @@ func newCSAF(advisory csaf.Advisory) VEX {
}
}
func (v *CSAF) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability {
return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool {
func (v *CSAF) Filter(result *types.Result) {
result.Vulnerabilities = lo.Filter(result.Vulnerabilities, func(vuln types.DetectedVulnerability, _ int) bool {
found, ok := lo.Find(v.advisory.Vulnerabilities, func(item *csaf.Vulnerability) bool {
return string(*item.CVE) == vuln.VulnerabilityID
})
@@ -32,31 +32,29 @@ func (v *CSAF) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulne
return true
}
return v.affected(found, vuln.PkgIdentifier.PURL)
if status := v.match(found, vuln.PkgIdentifier.PURL); status != "" {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(vuln, status, statement(found), "CSAF VEX"))
return false
}
return true
})
}
func (v *CSAF) affected(vuln *csaf.Vulnerability, pkgURL *packageurl.PackageURL) bool {
func (v *CSAF) match(vuln *csaf.Vulnerability, pkgURL *packageurl.PackageURL) types.FindingStatus {
if pkgURL == nil || vuln.ProductStatus == nil {
return true
return ""
}
var status Status
var status types.FindingStatus
switch {
case v.matchPURL(vuln.ProductStatus.KnownNotAffected, pkgURL):
status = StatusNotAffected
status = types.FindingStatusNotAffected
case v.matchPURL(vuln.ProductStatus.Fixed, pkgURL):
status = StatusFixed
status = types.FindingStatusFixed
}
if status != "" {
v.logger.Infow("Filtered out the detected vulnerability",
zap.String("vulnerability-id", string(*vuln.CVE)),
zap.String("status", string(status)))
return false
}
return true
return status
}
// matchPURL returns true if the given PackageURL is found in the ProductTree.
@@ -83,3 +81,13 @@ func (v *CSAF) matchPURL(products *csaf.Products, pkgURL *packageurl.PackageURL)
return false
}
func statement(vuln *csaf.Vulnerability) string {
threat, ok := lo.Find(vuln.Threats, func(threat *csaf.Threat) bool {
return lo.FromPtr(threat.Category) == csaf.CSAFThreatCategoryImpact
})
if !ok {
return ""
}
return lo.FromPtr(threat.Details)
}

View File

@@ -19,8 +19,8 @@ type CycloneDX struct {
type Statement struct {
VulnerabilityID string
Affects []string
Status Status
Justification string // TODO: define a type
Status types.FindingStatus
Justification string
}
func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX {
@@ -45,15 +45,20 @@ func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX {
}
}
func (v *CycloneDX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability {
return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool {
func (v *CycloneDX) Filter(result *types.Result) {
result.Vulnerabilities = lo.Filter(result.Vulnerabilities, func(vuln types.DetectedVulnerability, _ int) bool {
stmt, ok := lo.Find(v.statements, func(item Statement) bool {
return item.VulnerabilityID == vuln.VulnerabilityID
})
if !ok {
return true
}
return v.affected(vuln, stmt)
if !v.affected(vuln, stmt) {
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(vuln, stmt.Status, stmt.Justification, "CycloneDX VEX"))
return false
}
return true
})
}
@@ -66,29 +71,28 @@ func (v *CycloneDX) affected(vuln types.DetectedVulnerability, stmt Statement) b
continue
}
if v.sbom.SerialNumber != link.SerialNumber() || v.sbom.Version != link.Version() {
v.logger.Warnw("URN doesn't match with SBOM", zap.String("serial number", link.SerialNumber()),
v.logger.Warnw("URN doesn't match with SBOM",
zap.String("serial number", link.SerialNumber()),
zap.Int("version", link.Version()))
continue
}
if vuln.PkgIdentifier.Match(link.Reference()) && (stmt.Status == StatusNotAffected || stmt.Status == StatusFixed) {
v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID),
zap.String("status", string(stmt.Status)), zap.String("justification", stmt.Justification))
if vuln.PkgIdentifier.Match(link.Reference()) && (stmt.Status == types.FindingStatusNotAffected || stmt.Status == types.FindingStatusFixed) {
return false
}
}
return true
}
func cdxStatus(s cdx.ImpactAnalysisState) Status {
func cdxStatus(s cdx.ImpactAnalysisState) types.FindingStatus {
switch s {
case cdx.IASResolved, cdx.IASResolvedWithPedigree:
return StatusFixed
return types.FindingStatusFixed
case cdx.IASExploitable:
return StatusAffected
return types.FindingStatusAffected
case cdx.IASInTriage:
return StatusUnderInvestigation
return types.FindingStatusUnderInvestigation
case cdx.IASFalsePositive, cdx.IASNotAffected:
return StatusNotAffected
return types.FindingStatusNotAffected
}
return StatusUnknown
return types.FindingStatusUnknown
}

View File

@@ -3,31 +3,27 @@ package vex
import (
openvex "github.com/openvex/go-vex/pkg/vex"
"github.com/samber/lo"
"go.uber.org/zap"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
type OpenVEX struct {
vex openvex.VEX
logger *zap.SugaredLogger
vex openvex.VEX
}
func newOpenVEX(vex openvex.VEX) VEX {
return &OpenVEX{
vex: vex,
logger: log.Logger.With(zap.String("VEX format", "OpenVEX")),
vex: vex,
}
}
func (v *OpenVEX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability {
return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool {
var stmts []openvex.Statement
if vuln.PkgIdentifier.PURL != nil {
matchedStmts := v.vex.Matches(vuln.VulnerabilityID, vuln.PkgIdentifier.PURL.String(), nil)
stmts = append(stmts, matchedStmts...)
func (v *OpenVEX) Filter(result *types.Result) {
result.Vulnerabilities = lo.Filter(result.Vulnerabilities, func(vuln types.DetectedVulnerability, _ int) bool {
if vuln.PkgIdentifier.PURL == nil {
return true
}
stmts := v.vex.Matches(vuln.VulnerabilityID, vuln.PkgIdentifier.PURL.String(), nil)
if len(stmts) == 0 {
return true
}
@@ -37,10 +33,23 @@ func (v *OpenVEX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVu
// cf. https://github.com/openvex/spec/blob/fa5ba0c0afedb008dc5ebad418548cacf16a3ca7/OPENVEX-SPEC.md#the-vex-statement
stmt := stmts[len(stmts)-1]
if stmt.Status == openvex.StatusNotAffected || stmt.Status == openvex.StatusFixed {
v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID),
zap.String("status", string(stmt.Status)), zap.String("justification", string(stmt.Justification)))
result.ModifiedFindings = append(result.ModifiedFindings,
types.NewModifiedFinding(vuln, findingStatus(stmt.Status), string(stmt.Justification), "OpenVEX"))
return false
}
return true
})
}
func findingStatus(status openvex.Status) types.FindingStatus {
switch status {
case openvex.StatusNotAffected:
return types.FindingStatusNotAffected
case openvex.StatusFixed:
return types.FindingStatusFixed
case openvex.StatusUnderInvestigation:
return types.FindingStatusUnderInvestigation
default:
return types.FindingStatusUnknown
}
}

View File

@@ -1,11 +0,0 @@
package vex
type Status string
const (
StatusNotAffected Status = "not_affected"
StatusAffected Status = "affected"
StatusFixed Status = "fixed"
StatusUnderInvestigation Status = "under_investigation"
StatusUnknown Status = "unknown"
)

View File

@@ -20,7 +20,7 @@ import (
// Note: This is in the experimental stage and does not yet support many specifications.
// The implementation may change significantly.
type VEX interface {
Filter([]types.DetectedVulnerability) []types.DetectedVulnerability
Filter(*types.Result)
}
func New(filePath string, report types.Report) (VEX, error) {

View File

@@ -316,7 +316,12 @@ func TestVEX_Filter(t *testing.T) {
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, v.Filter(tt.args.vulns))
got := &types.Result{
Vulnerabilities: tt.args.vulns,
}
v.Filter(got)
assert.Equal(t, tt.want, got.Vulnerabilities)
})
}
}