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:] }} docker pull public.ecr.aws/aquasecurity/trivy:{{ git.tag[1:] }}
``` ```
## Helm ## Helm
### Installing from the the Aqua Chart Repository ### Installing from the Aqua Chart Repository
``` ```
helm repo add aquasecurity https://aquasecurity.github.io/helm-charts/ helm repo add aquasecurity https://aquasecurity.github.io/helm-charts/

View File

@@ -1,6 +1,6 @@
# Overview # Overview
Trivy detects two types of security issues: Trivy detects three types of security issues:
- [Vulnerabilities][vuln] - [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) - [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 - Terraform
- CloudFormation - CloudFormation
- more coming soon - more coming soon
- [Secrets][secret]
- AWS access key
- GCP service account
- GitHub personal access token
- etc.
Trivy can scan three different artifacts: Trivy can scan three different artifacts:
@@ -27,6 +32,8 @@ See [Integrations][integrations] for details.
[misconf]: ../docs/misconfiguration/index.md [misconf]: ../docs/misconfiguration/index.md
[secret]: ../docs/secret/scanning.md
[container]: ../docs/vulnerability/scanning/image.md [container]: ../docs/vulnerability/scanning/image.md
[rootfs]: ../docs/vulnerability/scanning/rootfs.md [rootfs]: ../docs/vulnerability/scanning/rootfs.md
[filesystem]: ../docs/vulnerability/scanning/filesystem.md [filesystem]: ../docs/vulnerability/scanning/filesystem.md

View File

@@ -1,6 +1,6 @@
# Quick Start # Quick Start
## Scan image for vulnerabilities ## Scan image for vulnerabilities and secrets
Simply specify an image name (and a tag). Simply specify an image name (and a tag).
@@ -10,32 +10,40 @@ $ trivy image [YOUR_IMAGE_NAME]
For example: For example:
``` ``` shell
$ trivy image python:3.4-alpine $ 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> myimage:1.0.0 (alpine 3.15.0)
<summary>Result</summary> =============================
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 | | 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 | | busybox | CVE-2022-28391 | CRITICAL | 1.34.1-r3 | 1.34.1-r5 | CVE-2022-28391 affecting |
| | | | | | with long nonces | | | | | | | 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 [vulnerability][vulnerability] and [secret][secret] pages.
For more details, see [here][vulnerability].
## Scan directory for misconfigurations ## Scan directory for misconfigurations
@@ -47,16 +55,10 @@ $ trivy config [YOUR_IAC_DIR]
For example: For example:
``` ``` shell
$ ls build/ $ ls build/
Dockerfile Dockerfile
$ trivy config ./build $ 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 Need to update the built-in policies
2021-07-09T10:06:29.188+0300 INFO Downloading 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 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]. For more details, see [here][misconf].
[vulnerability]: ../docs/vulnerability/scanning/index.md [vulnerability]: ../docs/vulnerability/scanning/index.md
[misconf]: ../docs/misconfiguration/index.md [misconf]: ../docs/misconfiguration/index.md
[secret]: ../docs/secret/scanning.md

View File

@@ -5,8 +5,8 @@
"elements": [ "elements": [
{ {
"type": "rectangle", "type": "rectangle",
"version": 476, "version": 787,
"versionNonce": 916788210, "versionNonce": 1318065410,
"isDeleted": false, "isDeleted": false,
"id": "BkXuq_6BxgqZGZWc8oCtu", "id": "BkXuq_6BxgqZGZWc8oCtu",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -15,21 +15,24 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 599.211669921875, "x": 599.653076171875,
"y": 376.32061767578125, "y": 734.7542114257812,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#fd7e14", "backgroundColor": "#fd7e14",
"width": 1076.4584350585938, "width": 1227.452155219184,
"height": 151.39703369140625, "height": 151.39703369140625,
"seed": 1632394695, "seed": 1632394695,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
"updated": 1650567161407,
"link": null,
"locked": false
}, },
{ {
"type": "text", "type": "text",
"version": 423, "version": 653,
"versionNonce": 931200686, "versionNonce": 1863936606,
"isDeleted": false, "isDeleted": false,
"id": "YQURTHNPSe05RPSlYRcok", "id": "YQURTHNPSe05RPSlYRcok",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -38,8 +41,8 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1119.1937866210938, "x": 1137.7821926540798,
"y": 403.56756591796875, "y": 764.0207858615452,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"width": 88, "width": 88,
@@ -47,18 +50,23 @@
"seed": 891391049, "seed": 891391049,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567336690,
"link": null,
"locked": false,
"fontSize": 36, "fontSize": 36,
"fontFamily": 1, "fontFamily": 1,
"text": "Trivy", "text": "Trivy",
"baseline": 32, "baseline": 32,
"textAlign": "left", "textAlign": "left",
"verticalAlign": "top" "verticalAlign": "top",
"containerId": null,
"originalText": "Trivy"
}, },
{ {
"type": "text", "type": "text",
"version": 758, "version": 1139,
"versionNonce": 813811122, "versionNonce": 1994750018,
"isDeleted": false, "isDeleted": false,
"id": "6dpF2EyZBtYgO6MrvGj0-", "id": "6dpF2EyZBtYgO6MrvGj0-",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -67,27 +75,32 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 922.1328735351562, "x": 900.5941772460938,
"y": 470.18975830078125, "y": 819.7337171766493,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"width": 507, "width": 612,
"height": 35, "height": 36,
"seed": 687997545, "seed": 687997545,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567334181,
"link": null,
"locked": false,
"fontSize": 28, "fontSize": 28,
"fontFamily": 1, "fontFamily": 1,
"text": "Vulnerability/Misconfiguration Scanner", "text": "Vulnerability/Misconfiguration/Secret Scanner",
"baseline": 25, "baseline": 25,
"textAlign": "left", "textAlign": "left",
"verticalAlign": "top" "verticalAlign": "top",
"containerId": null,
"originalText": "Vulnerability/Misconfiguration/Secret Scanner"
}, },
{ {
"type": "rectangle", "type": "rectangle",
"version": 595, "version": 805,
"versionNonce": 1705780846, "versionNonce": 1609410334,
"isDeleted": false, "isDeleted": false,
"id": "cpnTMy7L2AUg9IDJppF4H", "id": "cpnTMy7L2AUg9IDJppF4H",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -96,21 +109,24 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 597.4796142578125, "x": 600.9835205078125,
"y": 258.9286651611328, "y": 627.2060089111328,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#fab005", "backgroundColor": "#fab005",
"width": 349.1224975585937, "width": 298.6342163085937,
"height": 103.28388977050778, "height": 96.74092102050778,
"seed": 77164935, "seed": 77164935,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
}, },
{ {
"type": "text", "type": "text",
"version": 921, "version": 1011,
"versionNonce": 929185650, "versionNonce": 477782466,
"isDeleted": false, "isDeleted": false,
"id": "9-blmNVtLesthMSY_f60t", "id": "9-blmNVtLesthMSY_f60t",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -119,8 +135,8 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 655.6057739257812, "x": 628.7854614257812,
"y": 292.4844055175781, "y": 658.9062805175781,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"width": 238, "width": 238,
@@ -128,18 +144,23 @@
"seed": 860091815, "seed": 860091815,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 28, "fontSize": 28,
"fontFamily": 1, "fontFamily": 1,
"text": "Container Images", "text": "Container Images",
"baseline": 25, "baseline": 25,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "top" "verticalAlign": "top",
"containerId": null,
"originalText": "Container Images"
}, },
{ {
"type": "rectangle", "type": "rectangle",
"version": 853, "version": 1051,
"versionNonce": 377039022, "versionNonce": 1210520414,
"isDeleted": false, "isDeleted": false,
"id": "gugZxhi7ThlcjWY_MFO7q", "id": "gugZxhi7ThlcjWY_MFO7q",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -148,21 +169,24 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 955.929443359375, "x": 911.257568359375,
"y": 262.11351776123047, "y": 625.7697677612305,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#be4bdb", "backgroundColor": "#be4bdb",
"width": 359.85211181640625, "width": 452.44976806640636,
"height": 99.05134582519533, "height": 99.05134582519533,
"seed": 1232790121, "seed": 1232790121,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
}, },
{ {
"type": "text", "type": "text",
"version": 1065, "version": 1202,
"versionNonce": 126714162, "versionNonce": 842114,
"isDeleted": false, "isDeleted": false,
"id": "K48gtpesBxIGJxLTnI2CB", "id": "K48gtpesBxIGJxLTnI2CB",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -171,8 +195,8 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1064.449462890625, "x": 1065.672119140625,
"y": 296.9230194091797, "y": 656.4816131591797,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"width": 137, "width": 137,
@@ -180,18 +204,23 @@
"seed": 449264361, "seed": 449264361,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 28, "fontSize": 28,
"fontFamily": 1, "fontFamily": 1,
"text": "Filesystem", "text": "Filesystem",
"baseline": 25, "baseline": 25,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "top" "verticalAlign": "top",
"containerId": null,
"originalText": "Filesystem"
}, },
{ {
"type": "rectangle", "type": "rectangle",
"version": 896, "version": 1163,
"versionNonce": 585884398, "versionNonce": 1149481794,
"isDeleted": false, "isDeleted": false,
"id": "La6f87LDZ0uEIZB947bXo", "id": "La6f87LDZ0uEIZB947bXo",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -200,21 +229,24 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1329.0839233398438, "x": 1375.0136108398438,
"y": 264.9097213745117, "y": 626.2495651245117,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#12b886", "backgroundColor": "#12b886",
"width": 346.5517578125, "width": 452.76554361979186,
"height": 96.3990020751953, "height": 96.3990020751953,
"seed": 2005637801, "seed": 2005637801,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
"updated": 1650567157857,
"link": null,
"locked": false
}, },
{ {
"type": "text", "type": "text",
"version": 1186, "version": 1371,
"versionNonce": 1013615346, "versionNonce": 1552918366,
"isDeleted": false, "isDeleted": false,
"id": "aOgRPVQ81jhOfkvzjWTMF", "id": "aOgRPVQ81jhOfkvzjWTMF",
"fillStyle": "hachure", "fillStyle": "hachure",
@@ -223,286 +255,512 @@
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1392.300048828125, "x": 1490.9330512152778,
"y": 294.1288604736328, "y": 654.3717736138237,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"width": 223, "width": 224,
"height": 35, "height": 36,
"seed": 1284472935, "seed": 1284472935,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567166643,
"link": null,
"locked": false,
"fontSize": 28, "fontSize": 28,
"fontFamily": 1, "fontFamily": 1,
"text": "Git Repositories", "text": "Git Repositories",
"baseline": 25, "baseline": 25,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "top" "verticalAlign": "top",
"containerId": null,
"originalText": "Git Repositories"
}, },
{ {
"type": "rectangle", "type": "rectangle",
"version": 974, "version": 2340,
"versionNonce": 1011959534, "versionNonce": 1952732126,
"isDeleted": false, "isDeleted": false,
"id": "BYJwfkhd1BilbLQGc973f", "id": "p8fn5gPx8DfP8QE1lN98-",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1141.5093994140625, "x": 1064.5642678676506,
"y": 171.09759140014648, "y": 537.71609717149,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#82c91e", "backgroundColor": "#82c91e",
"width": 169.93957519531259, "width": 144.5880126953128,
"height": 77.80606079101562, "height": 77.80606079101562,
"seed": 1923498546, "seed": 684019996,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
}, "updated": 1650567155021,
{ "link": null,
"type": "text", "locked": false
"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"
}, },
{ {
"type": "rectangle", "type": "rectangle",
"version": 1035, "version": 2497,
"versionNonce": 1646453614, "versionNonce": 832692482,
"isDeleted": false, "isDeleted": false,
"id": "SPkrBrH6DGvkgQXtZQjIJ", "id": "kFTL0HnUdDs_ngg2xVFbn",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 959.9851989746094, "x": 600.7947366176511,
"y": 170.4835319519043, "y": 538.67312842149,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#fa5252", "backgroundColor": "#fa5252",
"width": 169.93957519531259, "width": 144.5880126953128,
"height": 77.80606079101562, "height": 77.80606079101562,
"seed": 1896460914, "seed": 541443108,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [] "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
}, },
{ {
"type": "text", "type": "rectangle",
"version": 532, "version": 2345,
"versionNonce": 1887556210, "versionNonce": 1129796482,
"isDeleted": false, "isDeleted": false,
"id": "n06MNIqirDmVZBkDg_UPV", "id": "KIztJcYHiVtM-GMlZAXAE",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 988.8137817382812, "x": 753.0955178676511,
"y": 196.69753646850586, "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", "strokeColor": "#000000",
"backgroundColor": "#82c91e", "backgroundColor": "#82c91e",
"width": 114, "width": 114,
"height": 25, "height": 25,
"seed": 1131832750, "seed": 13297180,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20, "fontSize": 20,
"fontFamily": 1, "fontFamily": 1,
"text": "Vulnerability", "text": "Vulnerability",
"baseline": 18, "baseline": 18,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "middle" "verticalAlign": "middle",
}, "containerId": null,
{ "originalText": "Vulnerability"
"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": []
}, },
{ {
"type": "text", "type": "text",
"version": 496, "version": 655,
"versionNonce": 1027781682, "versionNonce": 1728345310,
"isDeleted": false, "isDeleted": false,
"id": "jRmlh5MZuRKm3FtbC6qdZ", "id": "Iq57wFRtO1a8AU0rT6lRD",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1517.237319946289, "x": 1533.6317117153076,
"y": 196.91789627075195, "y": 565.2714713169978,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#82c91e", "backgroundColor": "#82c91e",
"width": 155, "width": 136.25488281249991,
"height": 25, "height": 21.80078124999998,
"seed": 1950385586, "seed": 1329695396,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"fontSize": 20, "updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 17.440624999999976,
"fontFamily": 1, "fontFamily": 1,
"text": "Misconfiguration", "text": "Misconfiguration",
"baseline": 18, "baseline": 14.800781249999979,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "middle" "verticalAlign": "middle",
}, "containerId": null,
{ "originalText": "Misconfiguration"
"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": []
}, },
{ {
"type": "text", "type": "text",
"version": 569, "version": 666,
"versionNonce": 184638962, "versionNonce": 1364217858,
"isDeleted": false, "isDeleted": false,
"id": "_04YR8geM-ar9vZhNZtSj", "id": "gjnZl9nxrqzliwPk8sbK-",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 1356.213119506836, "x": 1067.4339578090576,
"y": 196.30383682250977, "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", "strokeColor": "#000000",
"backgroundColor": "#82c91e", "backgroundColor": "#82c91e",
"width": 114, "width": 114,
"height": 25, "height": 25,
"seed": 357105522, "seed": 807441828,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20, "fontSize": 20,
"fontFamily": 1, "fontFamily": 1,
"text": "Vulnerability", "text": "Vulnerability",
"baseline": 18, "baseline": 18,
"textAlign": "center", "textAlign": "center",
"verticalAlign": "middle" "verticalAlign": "middle",
}, "containerId": null,
{ "originalText": "Vulnerability"
"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": []
}, },
{ {
"type": "text", "type": "text",
"version": 680, "version": 635,
"versionNonce": 113561522, "versionNonce": 2022375362,
"isDeleted": false, "isDeleted": false,
"id": "3Z5w3RXdgpvP43dlHqq26", "id": "An4-igVUkLzCwSdvDmtZl",
"fillStyle": "hachure", "fillStyle": "hachure",
"strokeWidth": 1, "strokeWidth": 1,
"strokeStyle": "solid", "strokeStyle": "solid",
"roughness": 1, "roughness": 1,
"opacity": 100, "opacity": 100,
"angle": 0, "angle": 0,
"x": 700.3721618652344, "x": 925.6707742153073,
"y": 190.79421615600586, "y": 560.4550650669978,
"strokeColor": "#000000", "strokeColor": "#000000",
"backgroundColor": "#82c91e", "backgroundColor": "#82c91e",
"width": 160, "width": 114,
"height": 35, "height": 25,
"seed": 1077804654, "seed": 1262859164,
"groupIds": [], "groupIds": [],
"strokeSharpness": "sharp", "strokeSharpness": "sharp",
"boundElementIds": [], "boundElements": [],
"fontSize": 28, "updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1, "fontFamily": 1,
"text": "Vulnerability", "text": "Vulnerability",
"baseline": 25, "baseline": 18,
"textAlign": "center", "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": { "appState": {
"gridSize": null, "gridSize": null,
"viewBackgroundColor": "#ffffff" "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 } ![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. `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.
A software vulnerability is a glitch, flaw, or weakness present in the software or in an Operating System.
`Trivy` detects vulnerabilities of [OS packages][os] (Alpine, RHEL, CentOS, etc.) and [language-specific packages][lang] (Bundler, Composer, npm, yarn, etc.). `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. 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. `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. 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 [vulnerability]: docs/vulnerability/scanning/index.md
[misconf]: docs/misconfiguration/index.md [misconf]: docs/misconfiguration/index.md
[secret]: docs/secret/scanning.md
[os]: docs/vulnerability/detection/os.md [os]: docs/vulnerability/detection/os.md
[lang]: docs/vulnerability/detection/language.md [lang]: docs/vulnerability/detection/language.md
[iac]: docs/misconfiguration/iac.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/Masterminds/sprig/v3 v3.2.2
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 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-dep-parser v0.0.0-20220412145205-d0501f906d90
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 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/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 h1:TQOc6oTNT1943KbsivdxBnNHlZbp6cF3AcMzVLJCotg=
github.com/aquasecurity/defsec v0.28.5-0.20220416075528-0f0c8fdf63b8/go.mod h1:vUdThwusBM7y1gJ7CVX3+h3bsPvpmOIEp3NEdzTDkhA= 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-20220421211205-bf8b076b6bf1 h1:57WZwnbs6hC38uhHu88T6WYqEsbmgvhsA+r81a5P13Q=
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/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 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-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= 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 namespaces []string
listAllPkgs bool listAllPkgs bool
input string input string
secretConfig string
} }
tests := []struct { tests := []struct {
name string name string
@@ -100,6 +101,15 @@ func TestFilesystem(t *testing.T) {
}, },
golden: "testdata/dockerfile-custom-policies.json.golden", 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 // Set up testing DB
@@ -143,6 +153,10 @@ func TestFilesystem(t *testing.T) {
osArgs = append(osArgs, "--list-all-pkgs") 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, "--output", outputFile)
osArgs = append(osArgs, tt.args.input) 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 Conftest: docs/misconfiguration/comparison/conftest.md
- vs tfsec: docs/misconfiguration/comparison/tfsec.md - vs tfsec: docs/misconfiguration/comparison/tfsec.md
- vs cfsec: docs/misconfiguration/comparison/cfsec.md - vs cfsec: docs/misconfiguration/comparison/cfsec.md
- Secret:
- Scanning: docs/secret/scanning.md
- Configuration: docs/secret/configuration.md
- SBOM: - SBOM:
- Overview: docs/sbom/index.md - Overview: docs/sbom/index.md
- CycloneDX: docs/sbom/cyclonedx.md - CycloneDX: docs/sbom/cyclonedx.md

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ func imageScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, fun
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, s, cleanup, err := initializeDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
dockerOpt, conf.ArtifactOption, conf.MisconfOption) dockerOpt, conf.ArtifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err) 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 // archiveScanner initializes an image archive scanner in standalone mode
// $ trivy image --input alpine.tar // $ trivy image --input alpine.tar
func archiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) { func archiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, s, err := initializeArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption)
conf.ArtifactOption, conf.MisconfOption)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err) 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, s, cleanup, err := initializeRemoteDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption,
dockerOpt, conf.ArtifactOption, conf.MisconfOption) dockerOpt, conf.ArtifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err) 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 // $ trivy image --server localhost:4954 --input alpine.tar
func remoteArchiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) { func remoteArchiveScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
// Scan tar file // Scan tar file
s, err := initializeRemoteArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption, s, err := initializeRemoteArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption, conf.ArtifactOption)
conf.ArtifactOption, conf.MisconfOption)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err) 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/google/wire"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/artifact" "github.com/aquasecurity/fanal/artifact"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/types" "github.com/aquasecurity/fanal/types"
@@ -24,8 +23,8 @@ import (
// initializeDockerScanner is for container image scanning in standalone mode // initializeDockerScanner is for container image scanning in standalone mode
// e.g. dockerd, container registry, podman, etc. // e.g. dockerd, container registry, podman, etc.
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option, localArtifactCache cache.LocalArtifactCache, dockerOpt types.DockerOption, artifactOption artifact.Option) (
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) { scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet) wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil 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 // initializeArchiveScanner is for container image archive scanning in standalone mode
// e.g. docker save -o alpine.tar alpine:3.15 // e.g. docker save -o alpine.tar alpine:3.15
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, error) {
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet) wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil return scanner.Scanner{}, nil
} }
// initializeFilesystemScanner is for filesystem scanning in standalone mode // initializeFilesystemScanner is for filesystem scanning in standalone mode
func initializeFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, func initializeFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneFilesystemSet) wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneRepositorySet) wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
@@ -66,8 +62,7 @@ func initializeResultClient() result.Client {
// initializeRemoteDockerScanner is for container image scanning in client/server mode // initializeRemoteDockerScanner is for container image scanning in client/server mode
// e.g. dockerd, container registry, podman, etc. // e.g. dockerd, container registry, podman, etc.
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option, remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option) (
configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) { scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteDockerSet) wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil, nil 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 // initializeRemoteArchiveScanner is for container image archive scanning in client/server mode
// e.g. docker save -o alpine.tar alpine:3.15 // e.g. docker save -o alpine.tar alpine:3.15
func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) ( remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, error) {
scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet) wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil return scanner.Scanner{}, nil
} }
// initializeRemoteFilesystemScanner is for filesystem scanning in client/server mode // initializeRemoteFilesystemScanner is for filesystem scanning in client/server mode
func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache,
remoteScanOptions client.ScannerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) ( remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteFilesystemSet) wire.Build(scanner.RemoteFilesystemSet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }

View File

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

View File

@@ -13,8 +13,7 @@ import (
// filesystemStandaloneScanner initializes a repository scanner in standalone mode // filesystemStandaloneScanner initializes a repository scanner in standalone mode
func repositoryScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) { func repositoryScanner(ctx context.Context, conf scannerConfig) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeRepositoryScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, s, cleanup, err := initializeRepositoryScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption)
conf.ArtifactOption, conf.MisconfOption)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err) 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"
"github.com/aquasecurity/fanal/analyzer/config" "github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/analyzer/secret"
"github.com/aquasecurity/fanal/artifact" "github.com/aquasecurity/fanal/artifact"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/db"
@@ -41,9 +42,6 @@ type scannerConfig struct {
// Artifact options // Artifact options
ArtifactOption artifact.Option ArtifactOption artifact.Option
// Misconfiguration scanning options
MisconfOption config.ScannerOption
} }
// InitializeScanner defines the initialize function signature of scanner // InitializeScanner defines the initialize function signature of scanner
@@ -189,11 +187,16 @@ func disabledAnalyzers(opt Option) []analyzer.Type {
analyzers = append(analyzers, analyzer.TypeApkCommand) 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) { if !slices.Contains(opt.VulnType, types.VulnTypeLibrary) {
analyzers = append(analyzers, analyzer.TypeLanguages...) 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 return analyzers
} }
@@ -246,8 +249,15 @@ func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner,
InsecureSkipTLS: opt.Insecure, InsecureSkipTLS: opt.Insecure,
Offline: opt.OfflineScan, Offline: opt.OfflineScan,
NoProgress: opt.NoProgress || opt.Quiet, NoProgress: opt.NoProgress || opt.Quiet,
// For misconfiguration scanning
MisconfScannerOption: configScannerOptions,
// For secret scanning
SecretScannerOption: secret.ScannerOption{
ConfigPath: opt.SecretConfigPath,
},
}, },
MisconfOption: configScannerOptions,
}) })
if err != nil { if err != nil {
return types.Report{}, xerrors.Errorf("unable to initialize a scanner: %w", err) 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 == "" { if opt.RemoteAddr == "" {
resultClient.FillVulnerabilityInfo(results[i].Vulnerabilities, results[i].Type) 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) opt.Severities, opt.IgnoreUnfixed, opt.IncludeNonFailures, opt.IgnoreFile, opt.IgnorePolicy)
if err != nil { if err != nil {
return types.Report{}, xerrors.Errorf("unable to filter vulnerabilities: %w", err) 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].Vulnerabilities = vulns
results[i].Misconfigurations = misconfs results[i].Misconfigurations = misconfs
results[i].MisconfSummary = misconfSummary results[i].MisconfSummary = misconfSummary
results[i].Secrets = secrets
} }
return report, nil return report, nil
} }

View File

@@ -8,7 +8,6 @@ package artifact
import ( import (
"context" "context"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/applier" "github.com/aquasecurity/fanal/applier"
"github.com/aquasecurity/fanal/artifact" "github.com/aquasecurity/fanal/artifact"
image2 "github.com/aquasecurity/fanal/artifact/image" image2 "github.com/aquasecurity/fanal/artifact/image"
@@ -29,7 +28,7 @@ import (
// initializeDockerScanner is for container image scanning in standalone mode // initializeDockerScanner is for container image scanning in standalone mode
// e.g. dockerd, container registry, podman, etc. // 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector) localScanner := local.NewScanner(applierApplier, detector)
@@ -37,7 +36,7 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption) artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil { if err != nil {
cleanup() cleanup()
return scanner.Scanner{}, nil, err 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 // initializeArchiveScanner is for container image archive scanning in standalone mode
// e.g. docker save -o alpine.tar alpine:3.15 // 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector) localScanner := local.NewScanner(applierApplier, detector)
@@ -58,7 +57,7 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
if err != nil { if err != nil {
return scanner.Scanner{}, err return scanner.Scanner{}, err
} }
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption) artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, err return scanner.Scanner{}, err
} }
@@ -67,11 +66,11 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
} }
// initializeFilesystemScanner is for filesystem scanning in standalone mode // 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector) localScanner := local.NewScanner(applierApplier, detector)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption, configScannerOption) artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
@@ -80,11 +79,11 @@ func initializeFilesystemScanner(ctx context.Context, path string, artifactCache
}, 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) { func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, 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 { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
@@ -95,20 +94,21 @@ func initializeRepositoryScanner(ctx context.Context, url string, artifactCache
} }
func initializeResultClient() result.Client { func initializeResultClient() result.Client {
dbConfig := db.Config{} config := db.Config{}
client := result.NewClient(dbConfig) client := result.NewClient(config)
return client return client
} }
// initializeRemoteDockerScanner is for container image scanning in client/server mode // initializeRemoteDockerScanner is for container image scanning in client/server mode
// e.g. dockerd, container registry, podman, etc. // 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) { func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, dockerOpt types.DockerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
clientScanner := client.NewScanner(remoteScanOptions) v := _wireValue
clientScanner := client.NewScanner(remoteScanOptions, v...)
typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt) typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption) artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil { if err != nil {
cleanup() cleanup()
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
@@ -119,15 +119,20 @@ func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifa
}, nil }, nil
} }
var (
_wireValue = []client.Option(nil)
)
// initializeRemoteArchiveScanner is for container image archive scanning in client/server mode // initializeRemoteArchiveScanner is for container image archive scanning in client/server mode
// e.g. docker save -o alpine.tar alpine:3.15 // 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) { func initializeRemoteArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, error) {
clientScanner := client.NewScanner(remoteScanOptions) v := _wireValue
clientScanner := client.NewScanner(remoteScanOptions, v...)
typesImage, err := image.NewArchiveImage(filePath) typesImage, err := image.NewArchiveImage(filePath)
if err != nil { if err != nil {
return scanner.Scanner{}, err return scanner.Scanner{}, err
} }
artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption, configScannerOption) artifactArtifact, err := image2.NewArtifact(typesImage, artifactCache, artifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, err 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 // 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) { func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
clientScanner := client.NewScanner(remoteScanOptions) v := _wireValue
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption, configScannerOption) clientScanner := client.NewScanner(remoteScanOptions, v...)
artifactArtifact, err := local2.NewArtifact(path, artifactCache, artifactOption)
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
@@ -148,7 +154,7 @@ func initializeRemoteFilesystemScanner(ctx context.Context, path string, artifac
} }
func initializeRemoteResultClient() result.Client { func initializeRemoteResultClient() result.Client {
dbConfig := db.Config{} config := db.Config{}
resultClient := result.NewClient(dbConfig) resultClient := result.NewClient(config)
return resultClient 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" "github.com/olekukonko/tablewriter"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types" dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
@@ -42,33 +43,41 @@ func (tw TableWriter) write(result types.Result) {
table := tablewriter.NewWriter(tw.Output) table := tablewriter.NewWriter(tw.Output)
var severityCount map[string]int var severityCount map[string]int
if len(result.Vulnerabilities) != 0 { switch {
case len(result.Vulnerabilities) != 0:
severityCount = tw.writeVulnerabilities(table, result.Vulnerabilities) severityCount = tw.writeVulnerabilities(table, result.Vulnerabilities)
} else if len(result.Misconfigurations) != 0 { case len(result.Misconfigurations) != 0:
severityCount = tw.writeMisconfigurations(table, result.Misconfigurations) severityCount = tw.writeMisconfigurations(table, result.Misconfigurations)
case len(result.Secrets) != 0:
severityCount = tw.writeSecrets(table, result.Secrets)
} }
total, summaries := tw.summary(severityCount) total, summaries := tw.summary(severityCount)
target := result.Target 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) target += fmt.Sprintf(" (%s)", result.Type)
} }
fmt.Printf("\n%s\n", target) fmt.Printf("\n%s\n", target)
fmt.Println(strings.Repeat("=", len(target))) fmt.Println(strings.Repeat("=", len(target)))
if result.MisconfSummary != nil { if result.Class == types.ClassConfig {
// for misconfigurations // for misconfigurations
summary := result.MisconfSummary summary := result.MisconfSummary
fmt.Printf("Tests: %d (SUCCESSES: %d, FAILURES: %d, EXCEPTIONS: %d)\n", fmt.Printf("Tests: %d (SUCCESSES: %d, FAILURES: %d, EXCEPTIONS: %d)\n",
summary.Successes+summary.Failures+summary.Exceptions, summary.Successes, summary.Failures, summary.Exceptions) summary.Successes+summary.Failures+summary.Exceptions, summary.Successes, summary.Failures, summary.Exceptions)
fmt.Printf("Failures: %d (%s)\n\n", total, strings.Join(summaries, ", ")) fmt.Printf("Failures: %d (%s)\n\n", total, strings.Join(summaries, ", "))
} else { } else {
// for vulnerabilities // for vulnerabilities and secrets
fmt.Printf("Total: %d (%s)\n\n", total, strings.Join(summaries, ", ")) 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 return
} }
@@ -134,6 +143,20 @@ func (tw TableWriter) writeMisconfigurations(table *tablewriter.Table, misconfs
return severityCount 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 { func (tw TableWriter) setVulnerabilityRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
severityCount := map[string]int{} severityCount := map[string]int{}
for _, v := range vulns { for _, v := range vulns {
@@ -212,6 +235,24 @@ func (tw TableWriter) setMisconfRows(table *tablewriter.Table, misconfs []types.
return severityCount 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) { func (tw TableWriter) outputTrace(result types.Result) {
blue := color.New(color.FgBlue).SprintFunc() blue := color.New(color.FgBlue).SprintFunc()
green := color.New(color.FgGreen).SprintfFunc() green := color.New(color.FgGreen).SprintfFunc()

View File

@@ -14,6 +14,7 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"golang.org/x/xerrors" "golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types" dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "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 // 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) ( 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) ignoredIDs := getIgnoredIDs(ignoreFile)
filteredVulns := filterVulnerabilities(vulns, severities, ignoreUnfixed, ignoredIDs) filteredVulns := filterVulnerabilities(vulns, severities, ignoreUnfixed, ignoredIDs)
misconfSummary, filteredMisconfs := filterMisconfigurations(misconfs, severities, includeNonFailures, ignoredIDs) misconfSummary, filteredMisconfs := filterMisconfigurations(misconfs, severities, includeNonFailures, ignoredIDs)
filteredSecrets := filterSecrets(secrets, severities)
if policyFile != "" { if policyFile != "" {
var err error var err error
filteredVulns, filteredMisconfs, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, policyFile) filteredVulns, filteredMisconfs, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, policyFile)
if err != nil { 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)) sort.Sort(types.BySeverity(filteredVulns))
return filteredVulns, misconfSummary, filteredMisconfs, nil return filteredVulns, misconfSummary, filteredMisconfs, filteredSecrets, nil
} }
func filterVulnerabilities(vulns []types.DetectedVulnerability, severities []dbTypes.Severity, func filterVulnerabilities(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
@@ -215,6 +217,20 @@ func filterMisconfigurations(misconfs []types.DetectedMisconfiguration, severiti
return summary, filtered 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) { func summarize(status types.MisconfStatus, summary *types.MisconfSummary) {
switch status { switch status {
case types.StatusFailure: case types.StatusFailure:

View File

@@ -363,6 +363,7 @@ func TestClient_Filter(t *testing.T) {
type args struct { type args struct {
vulns []types.DetectedVulnerability vulns []types.DetectedVulnerability
misconfs []types.DetectedMisconfiguration misconfs []types.DetectedMisconfiguration
secrets []ftypes.SecretFinding
severities []dbTypes.Severity severities []dbTypes.Severity
ignoreUnfixed bool ignoreUnfixed bool
ignoreFile string ignoreFile string
@@ -374,6 +375,7 @@ func TestClient_Filter(t *testing.T) {
wantVulns []types.DetectedVulnerability wantVulns []types.DetectedVulnerability
wantMisconfSummary *types.MisconfSummary wantMisconfSummary *types.MisconfSummary
wantMisconfs []types.DetectedMisconfiguration wantMisconfs []types.DetectedMisconfiguration
wantSecrets []ftypes.SecretFinding
}{ }{
{ {
name: "happy path", name: "happy path",
@@ -443,6 +445,24 @@ func TestClient_Filter(t *testing.T) {
Status: types.StatusPassed, 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}, severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false, ignoreUnfixed: false,
}, },
@@ -499,6 +519,16 @@ func TestClient_Filter(t *testing.T) {
Status: types.StatusFailure, 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", name: "happy path with ignore-unfixed",
@@ -770,12 +800,13 @@ func TestClient_Filter(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := Client{} 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) tt.args.severities, tt.args.ignoreUnfixed, false, tt.args.ignoreFile, tt.args.policyFile)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tt.wantVulns, gotVulns) assert.Equal(t, tt.wantVulns, gotVulns)
assert.Equal(t, tt.wantMisconfSummary, gotMisconfSummary) assert.Equal(t, tt.wantMisconfSummary, gotMisconfSummary)
assert.Equal(t, tt.wantMisconfs, gotMisconfs) assert.Equal(t, tt.wantMisconfs, gotMisconfs)
assert.Equal(t, tt.wantSecrets, gotSecrets)
}) })
} }
} }

View File

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

View File

@@ -104,10 +104,16 @@ func (s Scanner) Scan(target string, artifactKey string, blobKeys []string, opti
// Scan IaC config files // Scan IaC config files
if slices.Contains(options.SecurityChecks, types.SecurityCheckConfig) { if slices.Contains(options.SecurityChecks, types.SecurityCheckConfig) {
configResults := s.misconfsToResults(artifactDetail.Misconfigurations, options) configResults := s.misconfsToResults(artifactDetail.Misconfigurations)
results = append(results, configResults...) 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 return results, artifactDetail.OS, nil
} }
@@ -236,7 +242,7 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
return results, nil 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)) log.Logger.Infof("Detected config files: %d", len(misconfs))
var results types.Results var results types.Results
for _, misconf := range misconfs { for _, misconf := range misconfs {
@@ -272,6 +278,20 @@ func (s Scanner) misconfsToResults(misconfs []ftypes.Misconfiguration, options t
return results 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, func toDetectedMisconfiguration(res ftypes.MisconfResult, defaultSeverity dbTypes.Severity,
status types.MisconfStatus, layer ftypes.Layer) types.DetectedMisconfiguration { 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 // RemoteSuperSet is used in the client mode
var RemoteSuperSet = wire.NewSet( var RemoteSuperSet = wire.NewSet(
client.NewScanner, client.NewScanner,
wire.Value([]client.Option(nil)),
wire.Bind(new(Driver), new(client.Scanner)), wire.Bind(new(Driver), new(client.Scanner)),
NewScanner, NewScanner,
) )

View File

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

View File

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