feat: add secret scanning (#1901)

Co-authored-by: VaismanLior <97836016+VaismanLior@users.noreply.github.com>
Co-authored-by: AMF <work@afdesk.com>
This commit is contained in:
Teppei Fukuda
2022-04-22 17:08:18 +03:00
committed by GitHub
parent 0700586483
commit 5f047f97db
33 changed files with 1054 additions and 332 deletions

View File

@@ -0,0 +1,141 @@
# Configuration
Trivy tries to load `trivy-secret.yaml` in the current directory by default.
If the file doesn't exist, only builtin rules are used.
You can customize the config file name via the `--secret-config` flag.
## Custom Rules
Trivy allows defining custom rules. You can see an example.
``` yaml
rules:
- id: rule1
category: general
title: Generic Rule
severity: HIGH
path:
- .*\.sh
keywords:
- secret
regex: (?i)(?P<key>(secret))(=|:).{0,5}['"](?P<secret>[0-9a-zA-Z\-_=]{8,64})['"]
secret-group-name: secret
allow-rules:
- id: skip-text
description: skip text files
path: .*\.txt
```
`id` (required)
: - Unique identifier for this rule.
`category` (required)
: - String used for metadata and reporting purposes.
`title` (required)
: - Short human-readable title of the rule.
`severity` (required)
: - How critical this rule is.
- Allowed values:
- CRITICAL
- HIGH
- MEDIUM
- LOW
`regex` (required)
: - Golang regular expression used to detect secrets.
`path` (optional)
: - Golang regular expression used to match paths.
`keywords` (optional, recommended)
: - Keywords are used for pre-regex check filtering.
- Rules that contain keywords will perform a quick string compare check to make sure the keyword(s) are in the content being scanned.
- Ideally these values should either be part of the identifier or unique strings specific to the rule's regex.
- It is recommended to define for better performance.
`allow-rules` (optional)
: - Allow rules for a single rule to reduce false positives with known secrets.
- The details are below.
## Allow Rules
If the detected secret is matched with the specified `regex`, then that secret will be skipped and not detected.
The same logic applies for `path`.
`allow-rules` can be defined globally and per each rule. The fields are the same.
``` yaml
rules:
- id: rule1
category: general
title: Generic Rule
severity: HIGH
regex: (?i)(?P<key>(secret))(=|:).{0,5}['"](?P<secret>[0-9a-zA-Z\-_=]{8,64})['"]
allow-rules:
- id: skip-text
description: skip text files
path: .*\.txt
allow-rules:
- id: social-security-number
description: skip social security number
regex: 219-09-9999
```
`id` (required)
: - Unique identifier for this allow rule.
`description` (optional)
: - Short human-readable description of this allow rule.
`regex` (optional)
: - Golang regular expression used to allow detected secrets.
- `regex` or `path` must be specified.
`path` (optional)
: - Golang regular expression used to allow matched paths.
- `regex` or `path` must be specified.
## Disable Rules
Trivy offers builtin rules and allow rules, but you may want to disable some of them.
For example, you don't use Slack, so Slack doesn't have to be scanned.
You can specify `slack-access-token` and `slack-web-hook` in `disable-rules` so that those rules will be disabled for less false positives.
Markdown files are ignored by default, but you may want to scan markdown files as well.
You can disable the allow list by adding `markdown` to `disable-allow-rules`.
You can see a full list of rule IDs [here][builtin]. Allow rule IDs are below in the file.
``` yaml
disable-rules:
- slack-access-token
- slack-web-hook
disable-allow-rules:
- markdown
```
## Example
``` yaml
$ cat trivy-secret.yaml
rules:
- id: rule1
category: general
title: Generic Rule
severity: HIGH
regex: (?i)(?P<key>(secret))(=|:).{0,5}['"](?P<secret>[0-9a-zA-Z\-_=]{8,64})['"]
allow-rules:
- id: skip-text
description: skip text files
path: .*\.txt
allow-rules:
- id: social-security-number
description: skip social security number
regex: 219-09-9999
disable-rules:
- slack-access-token
- slack-web-hook
disable-allow-rules:
- markdown
```
[builtin]: https://github.com/aquasecurity/fanal/blob/main/secret/builtin.go

View File

@@ -0,0 +1,100 @@
# Secret Scanning
Trivy scans any container image, filesystem and git repository to detect exposed secrets like passwords, api keys, and tokens.
Secret scanning is enabled by default.
Trivy will scan every plaintext file, according to builtin rules or configuration. There are plenty of builtin rules:
- AWS access key
- GCP service account
- GitHub personal access token
- GitLab personal access token
- Slack access token
- etc.
You can see a full list of builtin rules [here][builtin].
## Quick start
This section shows how to scan secrets in container image and filesystem. Other subcommands should be the same.
### Container image
Specify an image name.
``` shell
$ trivy image myimage:1.0.0
2022-04-21T18:56:44.099+0300 INFO Detected OS: alpine
2022-04-21T18:56:44.099+0300 INFO Detecting Alpine vulnerabilities...
2022-04-21T18:56:44.101+0300 INFO Number of language-specific files: 0
myimage:1.0.0 (alpine 3.15.0)
=============================
Total: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 2)
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
| busybox | CVE-2022-28391 | CRITICAL | 1.34.1-r3 | 1.34.1-r5 | CVE-2022-28391 affecting |
| | | | | | package busybox 1.35.0 |
| | | | | | -->avd.aquasec.com/nvd/cve-2022-28391 |
+--------------+------------------| |-------------------+---------------+---------------------------------------+
| ssl_client | CVE-2022-28391 | | 1.34.1-r3 | 1.34.1-r5 | CVE-2022-28391 affecting |
| | | | | | package busybox 1.35.0 |
| | | | | | -->avd.aquasec.com/nvd/cve-2022-28391 |
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
app/secret.sh (secrets)
=======================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1)
+----------+-------------------+----------+---------+--------------------------------+
| CATEGORY | DESCRIPTION | SEVERITY | LINE NO | MATCH |
+----------+-------------------+----------+---------+--------------------------------+
| AWS | AWS Access Key ID | CRITICAL | 10 | export AWS_ACCESS_KEY_ID=***** |
+----------+-------------------+----------+---------+--------------------------------+
```
!!! tip
Trivy tries to detect a base image and skip those layers for secret scanning.
A base image usually contains a lot of files and makes secret scanning much slower.
If a secret is not detected properly, you can see base layers with the `--debug` flag.
### Filesystem
``` shell
$ trivy fs /path/to/your_project
...(snip)...
certs/key.pem (secrets)
========================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
+----------------------+------------------------+----------+---------+---------------------------------+
| CATEGORY | DESCRIPTION | SEVERITY | LINE NO | MATCH |
+----------------------+------------------------+----------+---------+---------------------------------+
| AsymmetricPrivateKey | Asymmetric Private Key | HIGH | 1 | -----BEGIN RSA PRIVATE KEY----- |
+----------------------+------------------------+----------+---------+---------------------------------+
```
!!! tip
Your project may have some secrets for testing. You can skip them with `--skip-dirs` or `--skip-files`.
We would recommend specifying these options so that the secret scanning can be faster if those files don't need to be scanned.
Also, you can specify paths to be allowed in a configuration file. See the detail [here][configuration].
## Configuration
Trivy has a set of builtin rules for secret scanning, which can be extended or modified by a configuration file.
If you don't need secret scanning, you can disable it via the `--security-checks` flag.
```shell
$ trivy image --security-checks vuln alpine:3.15
```
## Credit
This feature is inspired by [gitleaks][gitleaks].
[builtin]: https://github.com/aquasecurity/fanal/blob/main/secret/builtin.go
[configuration]: ./configuration.md
[gitleaks]: https://github.com/zricethezav/gitleaks

View File

@@ -174,7 +174,7 @@ The same image is hosted on [Amazon ECR Public][ecr] as well.
docker pull public.ecr.aws/aquasecurity/trivy:{{ git.tag[1:] }}
```
## Helm
### Installing from the the Aqua Chart Repository
### Installing from the Aqua Chart Repository
```
helm repo add aquasecurity https://aquasecurity.github.io/helm-charts/

View File

@@ -1,6 +1,6 @@
# Overview
Trivy detects two types of security issues:
Trivy detects three types of security issues:
- [Vulnerabilities][vuln]
- [OS packages][os] (Alpine, Red Hat Universal Base Image, Red Hat Enterprise Linux, CentOS, AlmaLinux, Rocky Linux, CBL-Mariner, Oracle Linux, Debian, Ubuntu, Amazon Linux, openSUSE Leap, SUSE Enterprise Linux, Photon OS and Distroless)
@@ -11,6 +11,11 @@ Trivy detects two types of security issues:
- Terraform
- CloudFormation
- more coming soon
- [Secrets][secret]
- AWS access key
- GCP service account
- GitHub personal access token
- etc.
Trivy can scan three different artifacts:
@@ -27,6 +32,8 @@ See [Integrations][integrations] for details.
[misconf]: ../docs/misconfiguration/index.md
[secret]: ../docs/secret/scanning.md
[container]: ../docs/vulnerability/scanning/image.md
[rootfs]: ../docs/vulnerability/scanning/rootfs.md
[filesystem]: ../docs/vulnerability/scanning/filesystem.md

View File

@@ -1,6 +1,6 @@
# Quick Start
## Scan image for vulnerabilities
## Scan image for vulnerabilities and secrets
Simply specify an image name (and a tag).
@@ -10,32 +10,40 @@ $ trivy image [YOUR_IMAGE_NAME]
For example:
```
$ trivy image python:3.4-alpine
```
``` shell
$ trivy image myimage:1.0.0
2022-04-21T18:56:44.099+0300 INFO Detected OS: alpine
2022-04-21T18:56:44.099+0300 INFO Detecting Alpine vulnerabilities...
2022-04-21T18:56:44.101+0300 INFO Number of language-specific files: 0
<details>
<summary>Result</summary>
myimage:1.0.0 (alpine 3.15.0)
=============================
Total: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 2)
```
2019-05-16T01:20:43.180+0900 INFO Updating vulnerability database...
2019-05-16T01:20:53.029+0900 INFO Detecting Alpine vulnerabilities...
python:3.4-alpine3.9 (alpine 3.9.2)
===================================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
+---------+------------------+----------+-------------------+---------------+--------------------------------+
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------+
| openssl | CVE-2019-1543 | MEDIUM | 1.1.1a-r1 | 1.1.1b-r1 | openssl: ChaCha20-Poly1305 |
| | | | | | with long nonces |
+---------+------------------+----------+-------------------+---------------+--------------------------------+
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
| busybox | CVE-2022-28391 | CRITICAL | 1.34.1-r3 | 1.34.1-r5 | CVE-2022-28391 affecting |
| | | | | | package busybox 1.35.0 |
| | | | | | -->avd.aquasec.com/nvd/cve-2022-28391 |
+--------------+------------------| |-------------------+---------------+---------------------------------------+
| ssl_client | CVE-2022-28391 | | 1.34.1-r3 | 1.34.1-r5 | CVE-2022-28391 affecting |
| | | | | | package busybox 1.35.0 |
| | | | | | -->avd.aquasec.com/nvd/cve-2022-28391 |
+--------------+------------------+----------+-------------------+---------------+---------------------------------------+
app/deploy.sh (secrets)
=======================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1)
+----------+-------------------+----------+---------+--------------------------------+
| CATEGORY | DESCRIPTION | SEVERITY | LINE NO | MATCH |
+----------+-------------------+----------+---------+--------------------------------+
| AWS | AWS Access Key ID | CRITICAL | 10 | export AWS_ACCESS_KEY_ID=***** |
+----------+-------------------+----------+---------+--------------------------------+
```
</details>
For more details, see [here][vulnerability].
For more details, see [vulnerability][vulnerability] and [secret][secret] pages.
## Scan directory for misconfigurations
@@ -47,16 +55,10 @@ $ trivy config [YOUR_IAC_DIR]
For example:
```
``` shell
$ ls build/
Dockerfile
$ trivy config ./build
```
<details>
<summary>Result</summary>
```
2021-07-09T10:06:29.188+0300 INFO Need to update the built-in policies
2021-07-09T10:06:29.188+0300 INFO Downloading the built-in policies...
2021-07-09T10:06:30.520+0300 INFO Detected config files: 1
@@ -75,9 +77,8 @@ Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
+---------------------------+------------+----------------------+----------+------------------------------------------+
```
</details>
For more details, see [here][misconf].
[vulnerability]: ../docs/vulnerability/scanning/index.md
[misconf]: ../docs/misconfiguration/index.md
[secret]: ../docs/secret/scanning.md

View File

@@ -5,8 +5,8 @@
"elements": [
{
"type": "rectangle",
"version": 476,
"versionNonce": 916788210,
"version": 787,
"versionNonce": 1318065410,
"isDeleted": false,
"id": "BkXuq_6BxgqZGZWc8oCtu",
"fillStyle": "hachure",
@@ -15,21 +15,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 599.211669921875,
"y": 376.32061767578125,
"x": 599.653076171875,
"y": 734.7542114257812,
"strokeColor": "#000000",
"backgroundColor": "#fd7e14",
"width": 1076.4584350585938,
"width": 1227.452155219184,
"height": 151.39703369140625,
"seed": 1632394695,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1650567161407,
"link": null,
"locked": false
},
{
"type": "text",
"version": 423,
"versionNonce": 931200686,
"version": 653,
"versionNonce": 1863936606,
"isDeleted": false,
"id": "YQURTHNPSe05RPSlYRcok",
"fillStyle": "hachure",
@@ -38,8 +41,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1119.1937866210938,
"y": 403.56756591796875,
"x": 1137.7821926540798,
"y": 764.0207858615452,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 88,
@@ -47,18 +50,23 @@
"seed": 891391049,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567336690,
"link": null,
"locked": false,
"fontSize": 36,
"fontFamily": 1,
"text": "Trivy",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Trivy"
},
{
"type": "text",
"version": 758,
"versionNonce": 813811122,
"version": 1139,
"versionNonce": 1994750018,
"isDeleted": false,
"id": "6dpF2EyZBtYgO6MrvGj0-",
"fillStyle": "hachure",
@@ -67,27 +75,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 922.1328735351562,
"y": 470.18975830078125,
"x": 900.5941772460938,
"y": 819.7337171766493,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 507,
"height": 35,
"width": 612,
"height": 36,
"seed": 687997545,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567334181,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Vulnerability/Misconfiguration Scanner",
"text": "Vulnerability/Misconfiguration/Secret Scanner",
"baseline": 25,
"textAlign": "left",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Vulnerability/Misconfiguration/Secret Scanner"
},
{
"type": "rectangle",
"version": 595,
"versionNonce": 1705780846,
"version": 805,
"versionNonce": 1609410334,
"isDeleted": false,
"id": "cpnTMy7L2AUg9IDJppF4H",
"fillStyle": "hachure",
@@ -96,21 +109,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 597.4796142578125,
"y": 258.9286651611328,
"x": 600.9835205078125,
"y": 627.2060089111328,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 349.1224975585937,
"height": 103.28388977050778,
"width": 298.6342163085937,
"height": 96.74092102050778,
"seed": 77164935,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "text",
"version": 921,
"versionNonce": 929185650,
"version": 1011,
"versionNonce": 477782466,
"isDeleted": false,
"id": "9-blmNVtLesthMSY_f60t",
"fillStyle": "hachure",
@@ -119,8 +135,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 655.6057739257812,
"y": 292.4844055175781,
"x": 628.7854614257812,
"y": 658.9062805175781,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 238,
@@ -128,18 +144,23 @@
"seed": 860091815,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Container Images",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Container Images"
},
{
"type": "rectangle",
"version": 853,
"versionNonce": 377039022,
"version": 1051,
"versionNonce": 1210520414,
"isDeleted": false,
"id": "gugZxhi7ThlcjWY_MFO7q",
"fillStyle": "hachure",
@@ -148,21 +169,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 955.929443359375,
"y": 262.11351776123047,
"x": 911.257568359375,
"y": 625.7697677612305,
"strokeColor": "#000000",
"backgroundColor": "#be4bdb",
"width": 359.85211181640625,
"width": 452.44976806640636,
"height": 99.05134582519533,
"seed": 1232790121,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1065,
"versionNonce": 126714162,
"version": 1202,
"versionNonce": 842114,
"isDeleted": false,
"id": "K48gtpesBxIGJxLTnI2CB",
"fillStyle": "hachure",
@@ -171,8 +195,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1064.449462890625,
"y": 296.9230194091797,
"x": 1065.672119140625,
"y": 656.4816131591797,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 137,
@@ -180,18 +204,23 @@
"seed": 449264361,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Filesystem",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Filesystem"
},
{
"type": "rectangle",
"version": 896,
"versionNonce": 585884398,
"version": 1163,
"versionNonce": 1149481794,
"isDeleted": false,
"id": "La6f87LDZ0uEIZB947bXo",
"fillStyle": "hachure",
@@ -200,21 +229,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1329.0839233398438,
"y": 264.9097213745117,
"x": 1375.0136108398438,
"y": 626.2495651245117,
"strokeColor": "#000000",
"backgroundColor": "#12b886",
"width": 346.5517578125,
"width": 452.76554361979186,
"height": 96.3990020751953,
"seed": 2005637801,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1650567157857,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1186,
"versionNonce": 1013615346,
"version": 1371,
"versionNonce": 1552918366,
"isDeleted": false,
"id": "aOgRPVQ81jhOfkvzjWTMF",
"fillStyle": "hachure",
@@ -223,286 +255,512 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1392.300048828125,
"y": 294.1288604736328,
"x": 1490.9330512152778,
"y": 654.3717736138237,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 223,
"height": 35,
"width": 224,
"height": 36,
"seed": 1284472935,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567166643,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Git Repositories",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Git Repositories"
},
{
"type": "rectangle",
"version": 974,
"versionNonce": 1011959534,
"version": 2340,
"versionNonce": 1952732126,
"isDeleted": false,
"id": "BYJwfkhd1BilbLQGc973f",
"id": "p8fn5gPx8DfP8QE1lN98-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1141.5093994140625,
"y": 171.09759140014648,
"x": 1064.5642678676506,
"y": 537.71609717149,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 169.93957519531259,
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1923498546,
"seed": 684019996,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
},
{
"type": "text",
"version": 403,
"versionNonce": 1635608306,
"isDeleted": false,
"id": "eedUyCpr8i1aY_3PHsHAB",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1149.8379821777344,
"y": 197.31159591674805,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 155,
"height": 25,
"seed": 595309038,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 20,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle"
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 1035,
"versionNonce": 1646453614,
"version": 2497,
"versionNonce": 832692482,
"isDeleted": false,
"id": "SPkrBrH6DGvkgQXtZQjIJ",
"id": "kFTL0HnUdDs_ngg2xVFbn",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 959.9851989746094,
"y": 170.4835319519043,
"x": 600.7947366176511,
"y": 538.67312842149,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 169.93957519531259,
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1896460914,
"seed": 541443108,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "text",
"version": 532,
"versionNonce": 1887556210,
"type": "rectangle",
"version": 2345,
"versionNonce": 1129796482,
"isDeleted": false,
"id": "n06MNIqirDmVZBkDg_UPV",
"id": "KIztJcYHiVtM-GMlZAXAE",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 988.8137817382812,
"y": 196.69753646850586,
"x": 753.0955178676511,
"y": 538.61062842149,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 145,
"height": 77.80606079101562,
"seed": 424425892,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567234562,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2329,
"versionNonce": 1518497986,
"isDeleted": false,
"id": "IWq_LcOearBV5cvqbJ_-o",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 911.3064553676511,
"y": 537.97781592149,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 468230812,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2318,
"versionNonce": 1504408670,
"isDeleted": false,
"id": "TXeK066NA0hvyPSeZl1a5",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1218.075986617651,
"y": 536.86062842149,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1408574372,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2364,
"versionNonce": 3868802,
"isDeleted": false,
"id": "10WjipxoLx2zzSI91pXbR",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1375.544736617651,
"y": 537.04812842149,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1813731484,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2355,
"versionNonce": 2067347614,
"isDeleted": false,
"id": "M7Cngti6H0_kawKRN8yJ6",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1528.868955367651,
"y": 535.89187842149,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1260603804,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "text",
"version": 597,
"versionNonce": 1519036482,
"isDeleted": false,
"id": "GHDrLyBOErQtv_WT5Lx3p",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 614.2293679653073,
"y": 565.5605338169978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 114,
"height": 25,
"seed": 1131832750,
"seed": 13297180,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"type": "rectangle",
"version": 1072,
"versionNonce": 789595566,
"isDeleted": false,
"id": "0JP6OL7EFfoH4E4vFARFl",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1508.9087371826172,
"y": 170.7038917541504,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 169.93957519531259,
"height": 77.80606079101562,
"seed": 101784622,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"verticalAlign": "middle",
"containerId": null,
"originalText": "Vulnerability"
},
{
"type": "text",
"version": 496,
"versionNonce": 1027781682,
"version": 655,
"versionNonce": 1728345310,
"isDeleted": false,
"id": "jRmlh5MZuRKm3FtbC6qdZ",
"id": "Iq57wFRtO1a8AU0rT6lRD",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1517.237319946289,
"y": 196.91789627075195,
"x": 1533.6317117153076,
"y": 565.2714713169978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 155,
"height": 25,
"seed": 1950385586,
"width": 136.25488281249991,
"height": 21.80078124999998,
"seed": 1329695396,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 20,
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 17.440624999999976,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 18,
"baseline": 14.800781249999979,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"type": "rectangle",
"version": 1133,
"versionNonce": 882335726,
"isDeleted": false,
"id": "EQRF92xU4o9CfeHHvbd-a",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1327.384536743164,
"y": 170.0898323059082,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 169.93957519531259,
"height": 77.80606079101562,
"seed": 1379493486,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"verticalAlign": "middle",
"containerId": null,
"originalText": "Misconfiguration"
},
{
"type": "text",
"version": 569,
"versionNonce": 184638962,
"version": 666,
"versionNonce": 1364217858,
"isDeleted": false,
"id": "_04YR8geM-ar9vZhNZtSj",
"id": "gjnZl9nxrqzliwPk8sbK-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1356.213119506836,
"y": 196.30383682250977,
"x": 1067.4339578090576,
"y": 565.8827994419978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 136.25488281249991,
"height": 21.80078124999998,
"seed": 290336932,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 17.440624999999976,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 14.800781249999979,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Misconfiguration"
},
{
"type": "text",
"version": 613,
"versionNonce": 1721641246,
"isDeleted": false,
"id": "_cm6xpfcL9Yv2XBK5MBZF",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1390.1199929653073,
"y": 563.8456900669978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 114,
"height": 25,
"seed": 357105522,
"seed": 807441828,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"type": "rectangle",
"version": 1215,
"versionNonce": 650195502,
"isDeleted": false,
"id": "8SHSNGf7PNddFLi2ZA3Vi",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 599.9767150878906,
"y": 169.0025749206543,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 344.1738281250001,
"height": 77.80606079101562,
"seed": 1986948530,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"verticalAlign": "middle",
"containerId": null,
"originalText": "Vulnerability"
},
{
"type": "text",
"version": 680,
"versionNonce": 113561522,
"version": 635,
"versionNonce": 2022375362,
"isDeleted": false,
"id": "3Z5w3RXdgpvP43dlHqq26",
"id": "An4-igVUkLzCwSdvDmtZl",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 700.3721618652344,
"y": 190.79421615600586,
"x": 925.6707742153073,
"y": 560.4550650669978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 160,
"height": 35,
"seed": 1077804654,
"width": 114,
"height": 25,
"seed": 1262859164,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 28,
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 25,
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle"
"verticalAlign": "middle",
"containerId": null,
"originalText": "Vulnerability"
},
{
"type": "text",
"version": 681,
"versionNonce": 1813371650,
"isDeleted": false,
"id": "p3-AVxdx5KP6eNViMVTLq",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 792.0184304653073,
"y": 564.4160025669978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 65,
"height": 25,
"seed": 729823772,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567236437,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Secret",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Secret"
},
{
"type": "text",
"version": 692,
"versionNonce": 621238878,
"isDeleted": false,
"id": "7fe9NOM7QTEEW7nyXAMjh",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1259.0613992153073,
"y": 562.4238150669978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 65,
"height": 25,
"seed": 968541220,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567238433,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Secret",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Secret"
},
{
"type": "rectangle",
"version": 2417,
"versionNonce": 1222703518,
"isDeleted": false,
"id": "Fq7meULupm1A9leboPlko",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1684.2588079543348,
"y": 536.1830067815082,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 230693534,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "text",
"version": 760,
"versionNonce": 503898690,
"isDeleted": false,
"id": "OUGk8nZzvgcKUHhKUcQov",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1723.2672240242127,
"y": 561.8650424396028,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 65,
"height": 25,
"seed": 2044527454,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567240607,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Secret",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Secret"
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
}
},
"files": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 KiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@@ -6,10 +6,10 @@ hide:
![logo](imgs/logo.png){ align=left }
`Trivy` (`tri` pronounced like **tri**gger, `vy` pronounced like en**vy**) is a simple and comprehensive [vulnerability][vulnerability]/[misconfiguration][misconf] scanner for containers and other artifacts.
A software vulnerability is a glitch, flaw, or weakness present in the software or in an Operating System.
`Trivy` (`tri` pronounced like **tri**gger, `vy` pronounced like en**vy**) is a simple and comprehensive [vulnerability][vulnerability]/[misconfiguration][misconf]/[secret][secret] scanner for containers and other artifacts.
`Trivy` detects vulnerabilities of [OS packages][os] (Alpine, RHEL, CentOS, etc.) and [language-specific packages][lang] (Bundler, Composer, npm, yarn, etc.).
In addition, `Trivy` scans [Infrastructure as Code (IaC) files][iac] such as Terraform and Kubernetes, to detect potential configuration issues that expose your deployments to the risk of attack.
`Trivy` also scans [hardcoded secrets][secret] like passwords, api keys, and tokens.
`Trivy` is easy to use. Just install the binary and you're ready to scan.
All you need to do for scanning is to specify a target such as an image name of the container.
@@ -40,6 +40,7 @@ Contact us about any matter by opening a GitHub Discussion [here][discussions]
[vulnerability]: docs/vulnerability/scanning/index.md
[misconf]: docs/misconfiguration/index.md
[secret]: docs/secret/scanning.md
[os]: docs/vulnerability/detection/os.md
[lang]: docs/vulnerability/detection/language.md
[iac]: docs/misconfiguration/iac.md

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
github.com/Masterminds/sprig/v3 v3.2.2
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/fanal v0.0.0-20220421095103-63f3f8193fa8
github.com/aquasecurity/fanal v0.0.0-20220421211205-bf8b076b6bf1
github.com/aquasecurity/go-dep-parser v0.0.0-20220412145205-d0501f906d90
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798

4
go.sum
View File

@@ -237,8 +237,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
github.com/aquasecurity/defsec v0.28.5-0.20220416075528-0f0c8fdf63b8 h1:TQOc6oTNT1943KbsivdxBnNHlZbp6cF3AcMzVLJCotg=
github.com/aquasecurity/defsec v0.28.5-0.20220416075528-0f0c8fdf63b8/go.mod h1:vUdThwusBM7y1gJ7CVX3+h3bsPvpmOIEp3NEdzTDkhA=
github.com/aquasecurity/fanal v0.0.0-20220421095103-63f3f8193fa8 h1:3kXo5widBEz1euPnd292X5JSq0kEDuobi3YAXl4DbhU=
github.com/aquasecurity/fanal v0.0.0-20220421095103-63f3f8193fa8/go.mod h1:PYU7igSuHlhOFTVNhMlv/P9oTYbcgMb0wn5+Sz+xkMs=
github.com/aquasecurity/fanal v0.0.0-20220421211205-bf8b076b6bf1 h1:57WZwnbs6hC38uhHu88T6WYqEsbmgvhsA+r81a5P13Q=
github.com/aquasecurity/fanal v0.0.0-20220421211205-bf8b076b6bf1/go.mod h1:zDhHI2dChX5My7gGtY6PGgKTfhJ6y8c6cDI+y38rVk8=
github.com/aquasecurity/go-dep-parser v0.0.0-20220412145205-d0501f906d90 h1:uZcI5qV7J1pzOc6W49l7iEey/KtEVlaqsNU5l65vZLk=
github.com/aquasecurity/go-dep-parser v0.0.0-20220412145205-d0501f906d90/go.mod h1:rK/5BoRt8/D7xXydoVVeBaQuk6zDJ6W+FWz/RqFuJxI=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=

View File

@@ -24,6 +24,7 @@ func TestFilesystem(t *testing.T) {
namespaces []string
listAllPkgs bool
input string
secretConfig string
}
tests := []struct {
name string
@@ -100,6 +101,15 @@ func TestFilesystem(t *testing.T) {
},
golden: "testdata/dockerfile-custom-policies.json.golden",
},
{
name: "secrets",
args: args{
securityChecks: "vuln,secret",
input: "testdata/fixtures/fs/secrets",
secretConfig: "testdata/fixtures/fs/secrets/trivy-secret.yaml",
},
golden: "testdata/secrets.json.golden",
},
}
// Set up testing DB
@@ -143,6 +153,10 @@ func TestFilesystem(t *testing.T) {
osArgs = append(osArgs, "--list-all-pkgs")
}
if tt.args.secretConfig != "" {
osArgs = append(osArgs, "--secret-config", tt.args.secretConfig)
}
osArgs = append(osArgs, "--output", outputFile)
osArgs = append(osArgs, tt.args.input)

View File

@@ -0,0 +1,7 @@
#!/bin/sh
export AWS_ACCESS_KEY_ID=AKIAABCDEFGHI1234567
export GITHUB_PAT=ghp_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
echo mysecret

View File

@@ -0,0 +1,8 @@
rules:
- id: mysecret
category: Custom
title: My Secret
severity: HIGH
regex: mysecret
disable-rules:
- github-pat

View File

@@ -0,0 +1,31 @@
{
"SchemaVersion": 2,
"ArtifactName": "testdata/fixtures/fs/secrets",
"ArtifactType": "filesystem",
"Results": [
{
"Target": "deploy.sh",
"Class": "secret",
"Secrets": [
{
"RuleID": "aws-access-key-id",
"Category": "AWS",
"Severity": "CRITICAL",
"Title": "AWS Access Key ID",
"StartLine": 3,
"EndLine": 3,
"Match": "export AWS_ACCESS_KEY_ID=*****"
},
{
"RuleID": "mysecret",
"Category": "Custom",
"Severity": "HIGH",
"Title": "My Secret",
"StartLine": 7,
"EndLine": 7,
"Match": "echo *****"
}
]
}
]
}

View File

@@ -60,6 +60,9 @@ nav:
- vs Conftest: docs/misconfiguration/comparison/conftest.md
- vs tfsec: docs/misconfiguration/comparison/tfsec.md
- vs cfsec: docs/misconfiguration/comparison/cfsec.md
- Secret:
- Scanning: docs/secret/scanning.md
- Configuration: docs/secret/configuration.md
- SBOM:
- Overview: docs/sbom/index.md
- CycloneDX: docs/sbom/cyclonedx.md

View File

@@ -147,8 +147,8 @@ var (
securityChecksFlag = cli.StringFlag{
Name: "security-checks",
Value: types.SecurityCheckVulnerability,
Usage: "comma-separated list of what security issues to detect (vuln,config)",
Value: fmt.Sprintf("%s,%s", types.SecurityCheckVulnerability, types.SecurityCheckSecret),
Usage: "comma-separated list of what security issues to detect (vuln,config,secret)",
EnvVars: []string{"TRIVY_SECURITY_CHECKS"},
}
@@ -332,6 +332,13 @@ var (
EnvVars: []string{"TRIVY_DB_REPOSITORY"},
}
secretConfig = cli.StringFlag{
Name: "secret-config",
Usage: "specify a path to config file for secret scanning",
Value: "trivy-secret.yaml",
EnvVars: []string{"TRIVY_SECRET_CONFIG"},
}
// Global flags
globalFlags = []cli.Flag{
&quietFlag,
@@ -456,6 +463,7 @@ func NewImageCommand() *cli.Command {
&offlineScan,
&insecureFlag,
&dbRepositoryFlag,
&secretConfig,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
},
@@ -493,6 +501,7 @@ func NewFilesystemCommand() *cli.Command {
&listAllPackages,
&offlineScan,
&dbRepositoryFlag,
&secretConfig,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
@@ -540,6 +549,7 @@ func NewRootfsCommand() *cli.Command {
&listAllPackages,
&offlineScan,
&dbRepositoryFlag,
&secretConfig,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
stringSliceFlag(configPolicy),
@@ -584,6 +594,7 @@ func NewRepositoryCommand() *cli.Command {
&offlineScan,
&insecureFlag,
&dbRepositoryFlag,
&secretConfig,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),
},
@@ -620,6 +631,7 @@ func NewClientCommand() *cli.Command {
&listAllPackages,
&offlineScan,
&insecureFlag,
&secretConfig,
&token,
&tokenHeader,

View File

@@ -12,8 +12,7 @@ import (
// filesystemStandaloneScanner initializes a filesystem scanner in standalone mode
func filesystemStandaloneScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
conf.ArtifactOption, conf.MisconfOption)
s, cleanup, err := initializeFilesystemScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
@@ -22,8 +21,7 @@ func filesystemStandaloneScanner(ctx context.Context, conf scannerConfig) (scann
// filesystemRemoteScanner initializes a filesystem scanner in client/server mode
func filesystemRemoteScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeRemoteFilesystemScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption,
conf.ArtifactOption, conf.MisconfOption)
s, cleanup, err := initializeRemoteFilesystemScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
@@ -39,6 +37,7 @@ func FilesystemRun(ctx *cli.Context) error {
// Disable the individual package scanning
opt.DisabledAnalyzers = analyzer.TypeIndividualPkgs
//opt.DisabledAnalyzers = append(opt.DisabledAnalyzers, analyzer.TypeSecret)
// client/server mode
if opt.RemoteAddr != "" {

View File

@@ -19,7 +19,7 @@ func imageScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, fun
return scanner.Scanner{}, nil, err
}
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
dockerOpt, conf.ArtifactOption, conf.MisconfOption)
dockerOpt, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
}
@@ -29,8 +29,7 @@ func imageScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, fun
// archiveScanner initializes an image archive scanner in standalone mode
// $ trivy image --input alpine.tar
func archiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
conf.ArtifactOption, conf.MisconfOption)
s, err := initializeArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
@@ -48,7 +47,7 @@ func remoteImageScanner(ctx context.Context, conf scannerConfig) (
}
s, cleanup, err := initializeRemoteDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption,
dockerOpt, conf.ArtifactOption, conf.MisconfOption)
dockerOpt, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err)
}
@@ -59,8 +58,7 @@ func remoteImageScanner(ctx context.Context, conf scannerConfig) (
// $ trivy image --server localhost:4954 --input alpine.tar
func remoteArchiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
// Scan tar file
s, err := initializeRemoteArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption,
conf.ArtifactOption, conf.MisconfOption)
s, err := initializeRemoteArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/google/wire"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/artifact"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/types"
@@ -24,8 +23,8 @@ import (
// initializeDockerScanner is for container image scanning in standalone mode
// e.g. dockerd, container registry, podman, etc.
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option) (
scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil
}
@@ -33,23 +32,20 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
// initializeArchiveScanner is for container image archive scanning in standalone mode
// e.g. docker save -o alpine.tar alpine:3.15
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil
}
// initializeFilesystemScanner is for filesystem scanning in standalone mode
func initializeFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil
}
@@ -66,8 +62,7 @@ func initializeResultClient() result.Client {
// initializeRemoteDockerScanner is for container image scanning in client/server mode
// e.g. dockerd, container registry, podman, etc.
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option,
configScannerOption config.ScannerOption) (
remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option) (
scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil, nil
@@ -76,16 +71,14 @@ func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifa
// initializeRemoteArchiveScanner is for container image archive scanning in client/server mode
// e.g. docker save -o alpine.tar alpine:3.15
func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (
scanner.Scanner, error) {
remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil
}
// initializeRemoteFilesystemScanner is for filesystem scanning in client/server mode
func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) {
remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteFilesystemSet)
return scanner.Scanner{}, nil, nil
}

View File

@@ -19,6 +19,7 @@ type Option struct {
option.ConfigOption
option.RemoteOption
option.SbomOption
option.SecretOption
// We don't want to allow disabled analyzers to be passed by users,
// but it differs depending on scanning modes.
@@ -42,6 +43,7 @@ func NewOption(c *cli.Context) (Option, error) {
ConfigOption: option.NewConfigOption(c),
RemoteOption: option.NewRemoteOption(c),
SbomOption: option.NewSbomOption(c),
SecretOption: option.NewSecretOption(c),
}, nil
}

View File

@@ -13,8 +13,7 @@ import (
// filesystemStandaloneScanner initializes a repository scanner in standalone mode
func repositoryScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeRepositoryScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
conf.ArtifactOption, conf.MisconfOption)
s, cleanup, err := initializeRepositoryScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/analyzer/secret"
"github.com/aquasecurity/fanal/artifact"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
@@ -41,9 +42,6 @@ type scannerConfig struct {
// Artifact options
ArtifactOption artifact.Option
// Misconfiguration scanning options
MisconfOption config.ScannerOption
}
// InitializeScanner defines the initialize function signature of scanner
@@ -189,11 +187,16 @@ func disabledAnalyzers(opt Option) []analyzer.Type {
analyzers = append(analyzers, analyzer.TypeApkCommand)
}
// Don't analyze programming language packages when not running in 'library' mode
// Do not analyze programming language packages when not running in 'library' mode
if !slices.Contains(opt.VulnType, types.VulnTypeLibrary) {
analyzers = append(analyzers, analyzer.TypeLanguages...)
}
// Do not perform secret scanning when it is not specified.
if !slices.Contains(opt.SecurityChecks, types.SecurityCheckSecret) {
analyzers = append(analyzers, analyzer.TypeSecret)
}
return analyzers
}
@@ -246,8 +249,15 @@ func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner,
InsecureSkipTLS: opt.Insecure,
Offline: opt.OfflineScan,
NoProgress: opt.NoProgress || opt.Quiet,
// For misconfiguration scanning
MisconfScannerOption: configScannerOptions,
// For secret scanning
SecretScannerOption: secret.ScannerOption{
ConfigPath: opt.SecretConfigPath,
},
},
MisconfOption: configScannerOptions,
})
if err != nil {
return types.Report{}, xerrors.Errorf("unable to initialize a scanner: %w", err)
@@ -269,7 +279,7 @@ func filter(ctx context.Context, opt Option, report types.Report) (types.Report,
if opt.RemoteAddr == "" {
resultClient.FillVulnerabilityInfo(results[i].Vulnerabilities, results[i].Type)
}
vulns, misconfSummary, misconfs, err := resultClient.Filter(ctx, results[i].Vulnerabilities, results[i].Misconfigurations,
vulns, misconfSummary, misconfs, secrets, err := resultClient.Filter(ctx, results[i].Vulnerabilities, results[i].Misconfigurations, results[i].Secrets,
opt.Severities, opt.IgnoreUnfixed, opt.IncludeNonFailures, opt.IgnoreFile, opt.IgnorePolicy)
if err != nil {
return types.Report{}, xerrors.Errorf("unable to filter vulnerabilities: %w", err)
@@ -277,6 +287,7 @@ func filter(ctx context.Context, opt Option, report types.Report) (types.Report,
results[i].Vulnerabilities = vulns
results[i].Misconfigurations = misconfs
results[i].MisconfSummary = misconfSummary
results[i].Secrets = secrets
}
return report, nil
}

View File

@@ -8,7 +8,6 @@ package artifact
import (
"context"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/applier"
"github.com/aquasecurity/fanal/artifact"
image2 "github.com/aquasecurity/fanal/artifact/image"
@@ -29,7 +28,7 @@ import (
// initializeDockerScanner is for container image scanning in standalone mode
// e.g. dockerd, container registry, podman, etc.
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
@@ -37,7 +36,7 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil {
return scanner.Scanner{}, nil, err
}
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption)
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil {
cleanup()
return scanner.Scanner{}, nil, err
@@ -50,7 +49,7 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
// initializeArchiveScanner is for container image archive scanning in standalone mode
// e.g. docker save -o alpine.tar alpine:3.15
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
@@ -58,7 +57,7 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
if err != nil {
return scanner.Scanner{}, err
}
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption)
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil {
return scanner.Scanner{}, err
}
@@ -67,11 +66,11 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
}
// initializeFilesystemScanner is for filesystem scanning in standalone mode
func initializeFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
func initializeFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption, configScannerOption)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
@@ -80,11 +79,11 @@ func initializeFilesystemScanner(ctx context.Context, path string, artifactCache
}, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
artifactArtifact, cleanup, err := remote.NewArtifact(url, artifactCache, artifactOption, configScannerOption)
artifactArtifact, cleanup, err := remote.NewArtifact(url, artifactCache, artifactOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
@@ -95,20 +94,21 @@ func initializeRepositoryScanner(ctx context.Context, url string, artifactCache
}
func initializeResultClient() result.Client {
dbConfig := db.Config{}
client := result.NewClient(dbConfig)
config := db.Config{}
client := result.NewClient(config)
return client
}
// initializeRemoteDockerScanner is for container image scanning in client/server mode
// e.g. dockerd, container registry, podman, etc.
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
clientScanner := client.NewScanner(remoteScanOptions)
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
v := _wireValue
clientScanner := client.NewScanner(remoteScanOptions, v...)
typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt)
if err != nil {
return scanner.Scanner{}, nil, err
}
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption)
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil {
cleanup()
return scanner.Scanner{}, nil, err
@@ -119,15 +119,20 @@ func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifa
}, nil
}
var (
_wireValue = []client.Option(nil)
)
// initializeRemoteArchiveScanner is for container image archive scanning in client/server mode
// e.g. docker save -o alpine.tar alpine:3.15
func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
clientScanner := client.NewScanner(remoteScanOptions)
func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, error) {
v := _wireValue
clientScanner := client.NewScanner(remoteScanOptions, v...)
typesImage, err := image.NewArchiveImage(filePath)
if err != nil {
return scanner.Scanner{}, err
}
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption)
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil {
return scanner.Scanner{}, err
}
@@ -136,9 +141,10 @@ func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifa
}
// initializeRemoteFilesystemScanner is for filesystem scanning in client/server mode
func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
clientScanner := client.NewScanner(remoteScanOptions)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption, configScannerOption)
func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
v := _wireValue
clientScanner := client.NewScanner(remoteScanOptions, v...)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
@@ -148,7 +154,7 @@ func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifac
}
func initializeRemoteResultClient() result.Client {
dbConfig := db.Config{}
resultClient := result.NewClient(dbConfig)
config := db.Config{}
resultClient := result.NewClient(config)
return resultClient
}

View File

@@ -0,0 +1,17 @@
package option
import (
"github.com/urfave/cli/v2"
)
// SecretOption holds the options for secret scanning
type SecretOption struct {
SecretConfigPath string
}
// NewSecretOption is the factory method to return secret options
func NewSecretOption(c *cli.Context) SecretOption {
return SecretOption{
SecretConfigPath: c.String("secret-config"),
}
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/olekukonko/tablewriter"
"golang.org/x/exp/slices"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
@@ -42,33 +43,41 @@ func (tw TableWriter) write(result types.Result) {
table := tablewriter.NewWriter(tw.Output)
var severityCount map[string]int
if len(result.Vulnerabilities) != 0 {
switch {
case len(result.Vulnerabilities) != 0:
severityCount = tw.writeVulnerabilities(table, result.Vulnerabilities)
} else if len(result.Misconfigurations) != 0 {
case len(result.Misconfigurations) != 0:
severityCount = tw.writeMisconfigurations(table, result.Misconfigurations)
case len(result.Secrets) != 0:
severityCount = tw.writeSecrets(table, result.Secrets)
}
total, summaries := tw.summary(severityCount)
target := result.Target
if result.Class != types.ClassOSPkg {
if result.Class == types.ClassSecret {
if len(result.Secrets) == 0 {
return
}
target += " (secrets)"
} else if result.Class != types.ClassOSPkg {
target += fmt.Sprintf(" (%s)", result.Type)
}
fmt.Printf("\n%s\n", target)
fmt.Println(strings.Repeat("=", len(target)))
if result.MisconfSummary != nil {
if result.Class == types.ClassConfig {
// for misconfigurations
summary := result.MisconfSummary
fmt.Printf("Tests: %d (SUCCESSES: %d, FAILURES: %d, EXCEPTIONS: %d)\n",
summary.Successes+summary.Failures+summary.Exceptions, summary.Successes, summary.Failures, summary.Exceptions)
fmt.Printf("Failures: %d (%s)\n\n", total, strings.Join(summaries, ", "))
} else {
// for vulnerabilities
// for vulnerabilities and secrets
fmt.Printf("Total: %d (%s)\n\n", total, strings.Join(summaries, ", "))
}
if len(result.Vulnerabilities) == 0 && len(result.Misconfigurations) == 0 {
if len(result.Vulnerabilities) == 0 && len(result.Misconfigurations) == 0 && len(result.Secrets) == 0 {
return
}
@@ -134,6 +143,20 @@ func (tw TableWriter) writeMisconfigurations(table *tablewriter.Table, misconfs
return severityCount
}
func (tw TableWriter) writeSecrets(table *tablewriter.Table, secrets []ftypes.SecretFinding) map[string]int {
table.SetColWidth(80)
alignment := []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER,
tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
header := []string{"Category", "Description", "Severity", "Line No", "Match"}
table.SetColumnAlignment(alignment)
table.SetHeader(header)
severityCount := tw.setSecretRows(table, secrets)
return severityCount
}
func (tw TableWriter) setVulnerabilityRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
severityCount := map[string]int{}
for _, v := range vulns {
@@ -212,6 +235,24 @@ func (tw TableWriter) setMisconfRows(table *tablewriter.Table, misconfs []types.
return severityCount
}
func (tw TableWriter) setSecretRows(table *tablewriter.Table, secrets []ftypes.SecretFinding) map[string]int {
severityCount := map[string]int{}
for _, secret := range secrets {
severity := secret.Severity
severityCount[severity]++
if tw.Output == os.Stdout {
severity = dbTypes.ColorizeSeverity(severity)
}
row := []string{string(secret.Category), secret.Title, severity,
fmt.Sprint(secret.StartLine), // multi-line is not supported for now.
secret.Match}
table.Append(row)
}
return severityCount
}
func (tw TableWriter) outputTrace(result types.Result) {
blue := color.New(color.FgBlue).SprintFunc()
green := color.New(color.FgGreen).SprintfFunc()

View File

@@ -14,6 +14,7 @@ import (
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
@@ -131,24 +132,25 @@ func (c Client) getPrimaryURL(vulnID string, refs []string, source dbTypes.Sourc
}
// Filter filter out the vulnerabilities
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, misconfs []types.DetectedMisconfiguration,
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, misconfs []types.DetectedMisconfiguration, secrets []ftypes.SecretFinding,
severities []dbTypes.Severity, ignoreUnfixed, includeNonFailures bool, ignoreFile, policyFile string) (
[]types.DetectedVulnerability, *types.MisconfSummary, []types.DetectedMisconfiguration, error) {
[]types.DetectedVulnerability, *types.MisconfSummary, []types.DetectedMisconfiguration, []ftypes.SecretFinding, error) {
ignoredIDs := getIgnoredIDs(ignoreFile)
filteredVulns := filterVulnerabilities(vulns, severities, ignoreUnfixed, ignoredIDs)
misconfSummary, filteredMisconfs := filterMisconfigurations(misconfs, severities, includeNonFailures, ignoredIDs)
filteredSecrets := filterSecrets(secrets, severities)
if policyFile != "" {
var err error
filteredVulns, filteredMisconfs, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, policyFile)
if err != nil {
return nil, nil, nil, xerrors.Errorf("failed to apply the policy: %w", err)
return nil, nil, nil, nil, xerrors.Errorf("failed to apply the policy: %w", err)
}
}
sort.Sort(types.BySeverity(filteredVulns))
return filteredVulns, misconfSummary, filteredMisconfs, nil
return filteredVulns, misconfSummary, filteredMisconfs, filteredSecrets, nil
}
func filterVulnerabilities(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
@@ -215,6 +217,20 @@ func filterMisconfigurations(misconfs []types.DetectedMisconfiguration, severiti
return summary, filtered
}
func filterSecrets(secrets []ftypes.SecretFinding, severities []dbTypes.Severity) []ftypes.SecretFinding {
var filtered []ftypes.SecretFinding
for _, secret := range secrets {
// Filter secrets by severity
for _, s := range severities {
if s.String() == secret.Severity {
filtered = append(filtered, secret)
break
}
}
}
return filtered
}
func summarize(status types.MisconfStatus, summary *types.MisconfSummary) {
switch status {
case types.StatusFailure:

View File

@@ -363,6 +363,7 @@ func TestClient_Filter(t *testing.T) {
type args struct {
vulns []types.DetectedVulnerability
misconfs []types.DetectedMisconfiguration
secrets []ftypes.SecretFinding
severities []dbTypes.Severity
ignoreUnfixed bool
ignoreFile string
@@ -374,6 +375,7 @@ func TestClient_Filter(t *testing.T) {
wantVulns []types.DetectedVulnerability
wantMisconfSummary *types.MisconfSummary
wantMisconfs []types.DetectedMisconfiguration
wantSecrets []ftypes.SecretFinding
}{
{
name: "happy path",
@@ -443,6 +445,24 @@ func TestClient_Filter(t *testing.T) {
Status: types.StatusPassed,
},
},
secrets: []ftypes.SecretFinding{
{
RuleID: "generic-critical-rule",
Severity: dbTypes.SeverityCritical.String(),
Title: "Critical Secret should pass filter",
StartLine: 1,
EndLine: 2,
Match: "*****",
},
{
RuleID: "generic-low-rule",
Severity: dbTypes.SeverityLow.String(),
Title: "Low Secret should be ignored",
StartLine: 3,
EndLine: 4,
Match: "*****",
},
},
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false,
},
@@ -499,6 +519,16 @@ func TestClient_Filter(t *testing.T) {
Status: types.StatusFailure,
},
},
wantSecrets: []ftypes.SecretFinding{
{
RuleID: "generic-critical-rule",
Severity: dbTypes.SeverityCritical.String(),
Title: "Critical Secret should pass filter",
StartLine: 1,
EndLine: 2,
Match: "*****",
},
},
},
{
name: "happy path with ignore-unfixed",
@@ -770,12 +800,13 @@ func TestClient_Filter(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := Client{}
gotVulns, gotMisconfSummary, gotMisconfs, err := c.Filter(context.Background(), tt.args.vulns, tt.args.misconfs,
gotVulns, gotMisconfSummary, gotMisconfs, gotSecrets, err := c.Filter(context.Background(), tt.args.vulns, tt.args.misconfs, tt.args.secrets,
tt.args.severities, tt.args.ignoreUnfixed, false, tt.args.ignoreFile, tt.args.policyFile)
require.NoError(t, err)
assert.Equal(t, tt.wantVulns, gotVulns)
assert.Equal(t, tt.wantMisconfSummary, gotMisconfSummary)
assert.Equal(t, tt.wantMisconfs, gotMisconfs)
assert.Equal(t, tt.wantSecrets, gotSecrets)
})
}
}

View File

@@ -17,10 +17,10 @@ type options struct {
rpcClient rpc.Scanner
}
type option func(*options)
type Option func(*options)
// WithRPCClient takes rpc client for testability
func WithRPCClient(c rpc.Scanner) option {
func WithRPCClient(c rpc.Scanner) Option {
return func(opts *options) {
opts.rpcClient = c
}
@@ -40,7 +40,7 @@ type Scanner struct {
}
// NewScanner is the factory method to return RPC Scanner
func NewScanner(scannerOptions ScannerOption, opts ...option) Scanner {
func NewScanner(scannerOptions ScannerOption, opts ...Option) Scanner {
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,

View File

@@ -104,10 +104,16 @@ func (s Scanner) Scan(target string, artifactKey string, blobKeys []string, opti
// Scan IaC config files
if slices.Contains(options.SecurityChecks, types.SecurityCheckConfig) {
configResults := s.misconfsToResults(artifactDetail.Misconfigurations, options)
configResults := s.misconfsToResults(artifactDetail.Misconfigurations)
results = append(results, configResults...)
}
// Scan secrets
if slices.Contains(options.SecurityChecks, types.SecurityCheckSecret) {
secretResults := s.secretsToResults(artifactDetail.Secrets)
results = append(results, secretResults...)
}
return results, artifactDetail.OS, nil
}
@@ -236,7 +242,7 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
return results, nil
}
func (s Scanner) misconfsToResults(misconfs []ftypes.Misconfiguration, options types.ScanOptions) types.Results {
func (s Scanner) misconfsToResults(misconfs []ftypes.Misconfiguration) types.Results {
log.Logger.Infof("Detected config files: %d", len(misconfs))
var results types.Results
for _, misconf := range misconfs {
@@ -272,6 +278,20 @@ func (s Scanner) misconfsToResults(misconfs []ftypes.Misconfiguration, options t
return results
}
func (s Scanner) secretsToResults(secrets []ftypes.Secret) types.Results {
var results types.Results
for _, secret := range secrets {
log.Logger.Debugf("Secret file: %s", secret.FilePath)
results = append(results, types.Result{
Target: secret.FilePath,
Class: types.ClassSecret,
Secrets: secret.Findings,
})
}
return results
}
func toDetectedMisconfiguration(res ftypes.MisconfResult, defaultSeverity dbTypes.Severity,
status types.MisconfStatus, layer ftypes.Layer) types.DetectedMisconfiguration {

View File

@@ -63,6 +63,7 @@ var StandaloneRepositorySet = wire.NewSet(
// RemoteSuperSet is used in the client mode
var RemoteSuperSet = wire.NewSet(
client.NewScanner,
wire.Value([]client.Option(nil)),
wire.Bind(new(Driver), new(client.Scanner)),
NewScanner,
)

View File

@@ -37,6 +37,7 @@ const (
ClassOSPkg = "os-pkgs"
ClassLangPkg = "lang-pkgs"
ClassConfig = "config"
ClassSecret = "secret"
)
// Result holds a target and detected vulnerabilities
@@ -48,6 +49,7 @@ type Result struct {
Vulnerabilities []DetectedVulnerability `json:"Vulnerabilities,omitempty"`
MisconfSummary *MisconfSummary `json:"MisconfSummary,omitempty"`
Misconfigurations []DetectedMisconfiguration `json:"Misconfigurations,omitempty"`
Secrets []ftypes.SecretFinding `json:"Secrets,omitempty"`
CustomResources []ftypes.CustomResource `json:"CustomResources,omitempty"`
}

View File

@@ -28,11 +28,14 @@ const (
// SecurityCheckConfig is a security check of misconfigurations
SecurityCheckConfig = SecurityCheck("config")
// SecurityCheckSecret is a security check of secrets
SecurityCheckSecret = SecurityCheck("secret")
)
var (
vulnTypes = []string{VulnTypeOS, VulnTypeLibrary}
securityChecks = []string{SecurityCheckVulnerability, SecurityCheckConfig}
securityChecks = []string{SecurityCheckVulnerability, SecurityCheckConfig, SecurityCheckSecret}
)
// NewVulnType returns an instance of VulnType