feat(misconf): Added fs.FS based scanning via latest defsec (#2084)

Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
Liam Galvin
2022-05-10 13:05:00 +01:00
committed by GitHub
parent fbb83c42d9
commit 5a58e41476
44 changed files with 516 additions and 2129 deletions

View File

@@ -53,54 +53,7 @@ $ trivy image --skip-update --offline-scan alpine:3.12
## Air-Gapped Environment for misconfigurations
### Download misconfiguration policies
At first, you need to download misconfiguration policies for use in air-gapped environments.
Please follow [oras installation instruction][oras].
Download `bundle.tar.gz`:
```
$ oras pull ghcr.io/aquasecurity/appshield:latest -a
```
### Transfer misconfiguration policies into the air-gapped environment
The way of transfer depends on the environment.
```
$ rsync -av -e ssh /path/to/bundle.tar.gz [user]@[host]:dst
```
### Put the misconfiguration policies in Trivy's cache directory
You have to know where to put the misconfiguration policies file. The following command shows the default cache directory.
```
$ ssh user@host
$ trivy -h | grep cache
--cache-dir value cache directory (default: "/home/myuser/.cache/trivy") [$TRIVY_CACHE_DIR]
```
Put the misconfiguration policies file in the cache directory + `/policy/content`.
```
$ mkdir -p /home/myuser/.cache/trivy/policy/content
$ cd /home/myuser/.cache/trivy/policy/content
$ mv /path/to/bundle.tar.gz .
```
Then, decompress it.
`bundle.tar.gz ` file includes two folders: `docker`, `kubernetes` and file: `.manifest`.
```
$ tar xvf bundle.tar.gz
x ./docker/
...
x ./kubernetes/
...
x ./.manifest
$ rm bundle.tar.gz
```
In an air-gapped environment it is your responsibility to update policies on a regular basis, so that the scanner can detect recently-identified misconfigurations.
No special measures are required to detect misconfigurations in an air-gapped environment.
### Run Trivy with --skip-policy-update option
In an air-gapped environment, specify `--skip-policy-update` so that Trivy doesn't attempt to download the latest misconfiguration policies.

View File

@@ -63,7 +63,7 @@ Please see [LICENSE][license] for Trivy licensing information.
[installation]: ../getting-started/installation.md
[vuln]: ../docs/vulnerability/scanning/index.md
[misconf]: ../docs/misconfiguration/index.md
[misconf]: ../docs/misconfiguration/scanning.md
[container]: ../docs/vulnerability/scanning/image.md
[rootfs]: ../docs/vulnerability/scanning/rootfs.md
[filesystem]: ../docs/vulnerability/scanning/filesystem.md

View File

@@ -3,20 +3,19 @@
Trivy uses cfsec internally to scan both JSON and YAML configuration files, but Trivy doesn't support some features provided by cfsec.
This section describes the differences between Trivy and cfsec.
| Feature | Trivy | cfsec |
| --------------------------- | --------------------------------------- | -------------------- |
| Built-in Policies | :material-check: | :material-check: |
| Custom Policies | Rego[^1] | :material-close: |
| Policy Metadata[^2] | :material-check: | :material-check: |
| Show Successes | :material-check: | :material-check: |
| Disable Policies | :material-check: | :material-check: |
| Show Issue Lines | :material-close: | :material-check: |
| View Statistics | :material-close: | :material-check: |
| Filtering by Severity | :material-check: | :material-close: |
| Supported Formats | Dockerfile, JSON, YAML, Terraform, etc. | CloudFormation JSON and YAML |
| Feature | Trivy | cfsec |
|-----------------------|--------------------------------------------------------|------------------------------|
| Built-in Policies | :material-check: | :material-check: |
| Custom Policies | :material-check: | :material-close: |
| Policy Metadata[^1] | :material-check: | :material-check: |
| Show Successes | :material-check: | :material-check: |
| Disable Policies | :material-check: | :material-check: |
| Show Issue Lines | :material-check: | :material-check: |
| View Statistics | :material-close: | :material-check: |
| Filtering by Severity | :material-check: | :material-close: |
| Supported Formats | Dockerfile, JSON, YAML, Terraform, CloudFormation etc. | CloudFormation JSON and YAML |
[^1]: CloudFormation files are not supported
[^2]: To enrich the results such as ID, Title, Description, Severity, etc.
[^1]: To enrich the results such as ID, Title, Description, Severity, etc.
cfsec is designed for CloudFormation.
People who use only want to scan their CloudFormation templates should use cfsec.

View File

@@ -3,21 +3,20 @@
Trivy uses tfsec internally to scan Terraform HCL files, but Trivy doesn't support some features provided by tfsec.
This section describes the differences between Trivy and tfsec.
| Feature | Trivy | tfsec |
| --------------------------- | --------------------------------------- | -------------------- |
| Built-in Policies | :material-check: | :material-check: |
| Custom Policies | Rego[^1] | JSON and YAML |
| Policy Metadata[^2] | :material-check: | :material-check: |
| Show Successes | :material-check: | :material-check: |
| Disable Policies | :material-check: | :material-check: |
| Show Issue Lines | :material-close: | :material-check: |
| Support .tfvars | :material-close: | :material-check: |
| View Statistics | :material-close: | :material-check: |
| Filtering by Severity | :material-check: | :material-close: |
| Supported Formats | Dockerfile, JSON, YAML, Terraform, etc. | Terraform |
| Feature | Trivy | tfsec |
|-----------------------|--------------------------------------------------------|----------------------|
| Built-in Policies | :material-check: | :material-check: |
| Custom Policies | Rego | Rego, JSON, and YAML |
| Policy Metadata[^1] | :material-check: | :material-check: |
| Show Successes | :material-check: | :material-check: |
| Disable Policies | :material-check: | :material-check: |
| Show Issue Lines | :material-check: | :material-check: |
| Support .tfvars | :material-close: | :material-check: |
| View Statistics | :material-close: | :material-check: |
| Filtering by Severity | :material-check: | :material-check: |
| Supported Formats | Dockerfile, JSON, YAML, Terraform, CloudFormation etc. | Terraform |
[^1]: Terraform HCL files are not supported.
[^2]: To enrich the results such as ID, Title, Description, Severity, etc.
[^1]: To enrich the results such as ID, Title, Description, Severity, etc.
tfsec is designed for Terraform.
People who use only Terraform should use tfsec.

View File

@@ -1,56 +0,0 @@
# Filesystem
## Quick start
Trivy scans a filesystem such as a virtual machine to detect misconfigurations.
You have to specify `--security-checks config` to enable misconfiguration detection.
```bash
$ trivy fs --security-checks config /path/to/dir
```
Internally, it is the same as [config subcommand](iac.md).
## Vulnerability and Misconfiguration scanning
The difference between `fs` and `config` subcommand is that `fs` can detect both vulnerabilities and misconfiguration at the same time.
You have to specify `--security-checks vuln,config` to enable vulnerability and misconfiguration detection.
``` bash
$ ls myapp/
Dockerfile Pipfile.lock
$ trivy fs --security-checks vuln,config --severity HIGH,CRITICAL myapp/
2021-07-09T12:03:27.564+0300 INFO Detected OS: unknown
2021-07-09T12:03:27.564+0300 INFO Number of language-specific files: 1
2021-07-09T12:03:27.564+0300 INFO Detecting pipenv vulnerabilities...
2021-07-09T12:03:27.566+0300 INFO Detected config files: 1
Pipfile.lock (pipenv)
=====================
Total: 1 (HIGH: 1, CRITICAL: 0)
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
| httplib2 | CVE-2021-21240 | HIGH | 0.12.1 | 0.19.0 | python-httplib2: Regular |
| | | | | | expression denial of |
| | | | | | service via malicious header |
| | | | | | -->avd.aquasec.com/nvd/cve-2021-21240 |
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
Dockerfile (dockerfile)
=======================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)
+---------------------------+------------+----------------------+----------+------------------------------------------+
| TYPE | MISCONF ID | CHECK | SEVERITY | MESSAGE |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Dockerfile Security Check | DS002 | Image user is 'root' | HIGH | Last USER command in |
| | | | | Dockerfile should not be 'root' |
| | | | | -->avd.aquasec.com/appshield/ds002 |
+---------------------------+------------+----------------------+----------+------------------------------------------+
```
In the above example, Trivy detected vulnerabilities of Python dependencies and misconfigurations in Dockerfile.

View File

@@ -1,8 +0,0 @@
# Misconfiguration Scanning
Trivy provides built-in policies to detect configuration issues in Docker, Kubernetes and Terraform.
Also, you can write your own policies in [Rego][rego] to scan JSON, YAML, HCL, etc, like [Conftest][conftest].
![misconf](../../imgs/misconf.png)
[rego]: https://www.openpolicyagent.org/docs/latest/policy-language/
[conftest]: https://github.com/open-policy-agent/conftest/

View File

@@ -2,40 +2,21 @@
## Policy Sources
Built-in policies are mainly written in [Rego][rego].
Those policies are managed under [AppShield repository][appshield].
Terraform policies are currently powered by [tfsec][tfsec] and CloudFormation policies are powered by [cfsec][cfsec].
Built-in policies are mainly written in [Rego][rego] and Go.
Those policies are managed under [defsec repository][defsec].
| Config type | Source |
| ------------------------- | ----------------------- |
| Kubernetes | [AppShield][kubernetes] |
| Dockerfile, Containerfile | [AppShield][docker] |
| Terraform | [tfsec][tfsec-checks] |
| CloudFormation | [cfsec][cfsec-checks] |
| Config type | Source |
|---------------------------|----------------------|
| Kubernetes | [defsec][kubernetes] |
| Dockerfile, Containerfile | [defsec][docker] |
| Terraform | [defsec][defsec] |
| CloudFormation | [defsec][defsec] |
For suggestions or issues regarding policy content, please open an issue under [AppShield][appshield], [tfsec][tfsec] or [cfsec][cfsec] repository.
For suggestions or issues regarding policy content, please open an issue under the [defsec][defsec] repository.
Ansible are coming soon.
## Policy Distribution
AppShield policies are distributed as an OPA bundle on [GitHub Container Registry][ghcr] (GHCR).
When misconfiguration detection is enabled, Trivy pulls the OPA bundle from GHCR as an OCI artifact and stores it in the cache.
Those policies are then loaded into Trivy OPA engine and used for detecting misconfigurations.
## Update Interval
Trivy checks for updates to OPA bundle on GHCR every 24 hours and pulls it if there are any updates.
[rego]: https://www.openpolicyagent.org/docs/latest/policy-language/
[appshield]: https://github.com/aquasecurity/appshield
[kubernetes]: https://github.com/aquasecurity/appshield/tree/master/kubernetes
[docker]: https://github.com/aquasecurity/appshield/tree/master/docker
[tfsec-checks]: https://tfsec.dev/
[tfsec]: https://github.com/aquasecurity/tfsec
[cfsec-checks]: https://cfsec.dev/
[cfsec]: https://github.com/aquasecurity/cfsec
[ghcr]: https://github.com/aquasecurity/appshield/pkgs/container/appshield
[dockerfile-bestpractice]: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
[pss]: https://kubernetes.io/docs/concepts/security/pod-security-standards/
[azure]: https://docs.microsoft.com/en-us/azure/security/fundamentals/network-best-practices
[kics]: https://github.com/Checkmarx/kics/
[defsec]: https://github.com/aquasecurity/defsec
[kubernetes]: https://github.com/aquasecurity/defsec/tree/master/internal/rules/kubernetes
[docker]: https://github.com/aquasecurity/appshield/tree/master/internal/rules/docker

View File

@@ -1,5 +1,5 @@
# Exceptions
Exceptions lets you to specify cases where you allow policy violations.
Exceptions let you specify cases where you allow policy violations.
Trivy supports two types of exceptions.
!!! info
@@ -22,7 +22,7 @@ The `exception` rule must be defined under `namespace.exceptions`.
exception[ns] {
ns := data.namespaces[_]
startswith(ns, "appshield")
startswith(ns, "builtin.kubernetes")
}
```
@@ -79,7 +79,7 @@ If you want to apply rule-based exceptions to built-in policies, you have to def
!!! example
``` rego
package appshield.kubernetes.KSV012
package builtin.kubernetes.KSV012
exception[rules] {
input.metadata.name == "can-run-as-root"
@@ -87,12 +87,12 @@ If you want to apply rule-based exceptions to built-in policies, you have to def
}
```
This exception is applied to [KSV012][ksv012] in AppShield.
You can get the package names in [AppShield repository][appshield] or the JSON output from Trivy.
This exception is applied to [KSV012][ksv012] in defsec.
You can get the package names in the [defsec repository][defsec] or the JSON output from Trivy.
For more details, see [an example][rule-example].
[ns-example]: https://github.com/aquasecurity/trivy/tree/{{ git.commit }}/examples/misconf/namespace-exception
[rule-example]: https://github.com/aquasecurity/trivy/tree/{{ git.commit }}/examples/misconf/rule-exception
[ksv012]: https://github.com/aquasecurity/appshield/blob/57bccc1897b2500a731415bda3990b0d4fbc959e/kubernetes/policies/pss/restricted/3_runs_as_root.rego
[appshield]: https://github.com/aquasecurity/appshield/
[ksv012]: https://github.com/aquasecurity/defsec/blob/master/internal/rules/kubernetes/policies/pss/restricted/3_runs_as_root.rego
[defsec]: https://github.com/aquasecurity/defsec/

View File

@@ -1,4 +1,8 @@
# Infrastructure as Code (IaC)
# Misconfiguration Scanning
Trivy provides built-in policies to detect configuration issues in Docker, Kubernetes, Terraform and CloudFormation.
Also, you can write your own policies in [Rego][rego] to scan JSON, YAML, etc, like [Conftest][conftest].
![misconf](../../imgs/misconf.png)
## Quick start
@@ -8,7 +12,6 @@ Simply specify a directory containing IaC files such as Terraform, CloudFormatio
$ trivy config [YOUR_IaC_DIRECTORY]
```
Trivy will automatically fetch the managed policies and will keep them up-to-date in future scans.
!!! example
```
@@ -32,7 +35,62 @@ Trivy will automatically fetch the managed policies and will keep them up-to-dat
| | | | | -->avd.aquasec.com/appshield/ds002 |
+---------------------------+------------+----------------------+----------+------------------------------------------+
```
You can also enable misconfiguration detection in container image, filesystem and git repository scanning via `--security-checks config`.
```bash
$ trivy image --security-checks config IMAGE_NAME
```
```bash
$ trivy fs --security-checks config /path/to/dir
```
!!! note
Misconfiguration detection is not enabled by default in `image`, `fs` and `repo` subcommands.
Unlike the `config` subcommand, `image`, `fs` and `repo` subcommands can also scan for vulnerabilities and secrets at the same time.
You can specify `--security-checks vuln,config,secret` to enable vulnerability and secret detection as well as misconfiguration detection.
!!! example
``` bash
$ ls myapp/
Dockerfile Pipfile.lock
$ trivy fs --security-checks vuln,config,secret --severity HIGH,CRITICAL myapp/
2021-07-09T12:03:27.564+0300 INFO Number of language-specific files: 1
2021-07-09T12:03:27.564+0300 INFO Detecting pipenv vulnerabilities...
2021-07-09T12:03:27.566+0300 INFO Detected config files: 1
Pipfile.lock (pipenv)
=====================
Total: 1 (HIGH: 1, CRITICAL: 0)
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
| httplib2 | CVE-2021-21240 | HIGH | 0.12.1 | 0.19.0 | python-httplib2: Regular |
| | | | | | expression denial of |
| | | | | | service via malicious header |
| | | | | | -->avd.aquasec.com/nvd/cve-2021-21240 |
+----------+------------------+----------+-------------------+---------------+---------------------------------------+
Dockerfile (dockerfile)
=======================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)
+---------------------------+------------+----------------------+----------+------------------------------------------+
| TYPE | MISCONF ID | CHECK | SEVERITY | MESSAGE |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Dockerfile Security Check | DS002 | Image user is 'root' | HIGH | Last USER command in |
| | | | | Dockerfile should not be 'root' |
| | | | | -->avd.aquasec.com/appshield/ds002 |
+---------------------------+------------+----------------------+----------+------------------------------------------+
```
In the above example, Trivy detected vulnerabilities of Python dependencies and misconfigurations in Dockerfile.
## Type detection
The specified directory can contain mixed types of IaC files.
Trivy automatically detects config types and applies relevant policies.
@@ -125,39 +183,42 @@ Failures: 9 (HIGH: 6, CRITICAL: 1)
+------------------------------------------+------------+------------------------------------------+----------+--------------------------------------------------------+
```
</details>
</details>
You can see the config type next to each file name.
!!! example
``` bash
Dockerfile (dockerfile)
=======================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)
...
deployment.yaml (kubernetes)
============================
Tests: 28 (SUCCESSES: 15, FAILURES: 13, EXCEPTIONS: 0)
Failures: 13 (HIGH: 1, CRITICAL: 0)
...
main.tf (terraform)
===================
Tests: 23 (SUCCESSES: 14, FAILURES: 9, EXCEPTIONS: 0)
Failures: 9 (HIGH: 6, CRITICAL: 1)
``` bash
Dockerfile (dockerfile)
=======================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)
...
...
bucket.yaml (cloudformation)
============================
Tests: 9 (SUCCESSES: 3, FAILURES: 6, EXCEPTIONS: 0)
Failures: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 4, CRITICAL: 0)
deployment.yaml (kubernetes)
============================
Tests: 28 (SUCCESSES: 15, FAILURES: 13, EXCEPTIONS: 0)
Failures: 13 (HIGH: 1, CRITICAL: 0)
```
...
main.tf (terraform)
===================
Tests: 23 (SUCCESSES: 14, FAILURES: 9, EXCEPTIONS: 0)
Failures: 9 (HIGH: 6, CRITICAL: 1)
...
bucket.yaml (cloudformation)
============================
Tests: 9 (SUCCESSES: 3, FAILURES: 6, EXCEPTIONS: 0)
Failures: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 4, CRITICAL: 0)
```
## Examples
See [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/misconf/mixed)
[rego]: https://www.openpolicyagent.org/docs/latest/policy-language/
[conftest]: https://github.com/open-policy-agent/conftest/
## Example
See [here](https://github.com/aquasecurity/trivy/tree/125c457517f05b6498bc68eaeec6e683dd36c49a/examples/misconf/mixed)

View File

@@ -30,7 +30,7 @@ See [Integrations][integrations] for details.
[os]: ../docs/vulnerability/detection/os.md
[lang]: ../docs/vulnerability/detection/language.md
[misconf]: ../docs/misconfiguration/index.md
[misconf]: ../docs/misconfiguration/scanning.md
[secret]: ../docs/secret/scanning.md

View File

@@ -80,5 +80,5 @@ Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
For more details, see [here][misconf].
[vulnerability]: ../docs/vulnerability/scanning/index.md
[misconf]: ../docs/misconfiguration/index.md
[misconf]: ../docs/misconfiguration/scanning.md
[secret]: ../docs/secret/scanning.md

View File

@@ -5,8 +5,8 @@
"elements": [
{
"type": "rectangle",
"version": 573,
"versionNonce": 2034670720,
"version": 791,
"versionNonce": 1617738570,
"isDeleted": false,
"id": "BkXuq_6BxgqZGZWc8oCtu",
"fillStyle": "hachure",
@@ -24,12 +24,15 @@
"seed": 1632394695,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1652181399352,
"link": null,
"locked": false
},
{
"type": "text",
"version": 572,
"versionNonce": 425683840,
"version": 716,
"versionNonce": 910620246,
"isDeleted": false,
"id": "YQURTHNPSe05RPSlYRcok",
"fillStyle": "hachure",
@@ -47,18 +50,23 @@
"seed": 891391049,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1652181399352,
"link": null,
"locked": false,
"fontSize": 36,
"fontFamily": 1,
"text": "Trivy",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Trivy"
},
{
"type": "text",
"version": 822,
"versionNonce": 1061096576,
"version": 1310,
"versionNonce": 1854587402,
"isDeleted": false,
"id": "6dpF2EyZBtYgO6MrvGj0-",
"fillStyle": "hachure",
@@ -67,27 +75,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 771.2554931640625,
"y": 469.7777099609375,
"x": 731.2024841308594,
"y": 467.7408447265625,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 507,
"height": 35,
"width": 612,
"height": 36,
"seed": 687997545,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1652181399352,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Vulnerability/Misconfiguration Scanner",
"text": "Vulnerability/Misconfiguration/Secret Scanner",
"baseline": 25,
"textAlign": "left",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Vulnerability/Misconfiguration/Secret Scanner"
},
{
"type": "rectangle",
"version": 763,
"versionNonce": 560331648,
"version": 1129,
"versionNonce": 1307232406,
"isDeleted": false,
"id": "cpnTMy7L2AUg9IDJppF4H",
"fillStyle": "hachure",
@@ -96,21 +109,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 602.28369140625,
"y": 258.8445587158203,
"x": 647.7814331054688,
"y": 188.2161407470703,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 397.493408203125,
"height": 103.28388977050778,
"width": 248.74017333984375,
"height": 77.74984135828628,
"seed": 77164935,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1652181524751,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1072,
"versionNonce": 212434048,
"version": 1279,
"versionNonce": 1197686422,
"isDeleted": false,
"id": "9-blmNVtLesthMSY_f60t",
"fillStyle": "hachure",
@@ -119,27 +135,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 630.9301147460938,
"y": 292.4002990722656,
"x": 664.1268412090633,
"y": 211.45297413880115,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 331,
"height": 35,
"width": 214.77717842516898,
"height": 35.96269034095853,
"seed": 860091815,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 28,
"boundElements": [],
"updated": 1652181521001,
"link": null,
"locked": false,
"fontSize": 27.970981376301072,
"fontFamily": 1,
"text": "Infrastructure as Code",
"baseline": 25,
"text": "Container image",
"baseline": 24.96269034095853,
"textAlign": "center",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Container image"
},
{
"type": "rectangle",
"version": 1194,
"versionNonce": 131337088,
"version": 1482,
"versionNonce": 773738186,
"isDeleted": false,
"id": "gugZxhi7ThlcjWY_MFO7q",
"fillStyle": "hachure",
@@ -148,21 +169,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1021.5928955078125,
"y": 261.56090545654297,
"x": 907.46728515625,
"y": 189.15813446044922,
"strokeColor": "#000000",
"backgroundColor": "#be4bdb",
"width": 441.0702514648438,
"height": 99.05134582519533,
"width": 255.61367797851565,
"height": 77.69438171386717,
"seed": 1232790121,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1652181510952,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1270,
"versionNonce": 591785088,
"version": 1444,
"versionNonce": 80370390,
"isDeleted": false,
"id": "K48gtpesBxIGJxLTnI2CB",
"fillStyle": "hachure",
@@ -171,8 +195,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1173.3179931640625,
"y": 294.12510681152344,
"x": 963.9347534179688,
"y": 212.18040466308594,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 137,
@@ -180,70 +204,23 @@
"seed": 449264361,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1652181510952,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Filesystem",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top"
"verticalAlign": "top",
"containerId": null,
"originalText": "Filesystem"
},
{
"type": "rectangle",
"version": 1319,
"versionNonce": 1264839808,
"isDeleted": false,
"id": "BYJwfkhd1BilbLQGc973f",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1233.3157348632812,
"y": 168.29967880249023,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 229.03393554687523,
"height": 77.80606079101562,
"seed": 1923498546,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
},
{
"type": "text",
"version": 659,
"versionNonce": 2122259328,
"isDeleted": false,
"id": "eedUyCpr8i1aY_3PHsHAB",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1241.1352844238281,
"y": 191.2939567565918,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 216,
"height": 35,
"seed": 595309038,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 28,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"type": "rectangle",
"version": 1397,
"versionNonce": 20077696,
"version": 1545,
"versionNonce": 819004246,
"isDeleted": false,
"id": "SPkrBrH6DGvkgQXtZQjIJ",
"fillStyle": "hachure",
@@ -252,21 +229,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1023.8157043457031,
"y": 168.7816276550293,
"x": 1174.8193054199219,
"y": 190.58329391479492,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 200.7496337890626,
"backgroundColor": "#12b886",
"width": 250.91937255859386,
"height": 77.80606079101562,
"seed": 1896460914,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1652181500681,
"link": null,
"locked": false
},
{
"type": "text",
"version": 840,
"versionNonce": 655338368,
"version": 1036,
"versionNonce": 1127482634,
"isDeleted": false,
"id": "n06MNIqirDmVZBkDg_UPV",
"fillStyle": "hachure",
@@ -275,27 +255,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1040.526611328125,
"y": 194.3111228942871,
"x": 1200.8165283203125,
"y": 212.65081405639648,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 160,
"height": 35,
"width": 201,
"height": 36,
"seed": 1131832750,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1652181500682,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Vulnerability",
"text": "Git Repository",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle"
"verticalAlign": "middle",
"containerId": null,
"originalText": "Git Repository"
},
{
"type": "rectangle",
"version": 1441,
"versionNonce": 1393118080,
"version": 1826,
"versionNonce": 405213130,
"isDeleted": false,
"id": "8SHSNGf7PNddFLi2ZA3Vi",
"fillStyle": "hachure",
@@ -304,21 +289,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 673.6463928222656,
"y": 167.8159294128418,
"x": 642.0580139160156,
"y": 280.92316818237305,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 278.48516845703136,
"width": 788.2601318359375,
"height": 77.80606079101562,
"seed": 1986948530,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": []
"boundElements": [],
"updated": 1652181559488,
"link": null,
"locked": false
},
{
"type": "text",
"version": 851,
"versionNonce": 2114620544,
"version": 1093,
"versionNonce": 573822154,
"isDeleted": false,
"id": "3Z5w3RXdgpvP43dlHqq26",
"fillStyle": "hachure",
@@ -327,26 +315,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 701.5146789550781,
"y": 189.60757064819336,
"x": 851.4863586425781,
"y": 300.9854393005371,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 216,
"height": 35,
"width": 339,
"height": 36,
"seed": 1077804654,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"boundElements": [],
"updated": 1652181415054,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Misconfiguration",
"text": "Misconfiguration scanning",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle"
"verticalAlign": "middle",
"containerId": null,
"originalText": "Misconfiguration scanning"
}
],
"appState": {
"gridSize": null,
"viewBackgroundColor": "#ffffff"
}
},
"files": {}
}

View File

@@ -5,8 +5,8 @@
"elements": [
{
"type": "rectangle",
"version": 787,
"versionNonce": 1318065410,
"version": 788,
"versionNonce": 555477386,
"isDeleted": false,
"id": "BkXuq_6BxgqZGZWc8oCtu",
"fillStyle": "hachure",
@@ -25,14 +25,14 @@
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567161407,
"updated": 1652177570112,
"link": null,
"locked": false
},
{
"type": "text",
"version": 653,
"versionNonce": 1863936606,
"version": 713,
"versionNonce": 44400470,
"isDeleted": false,
"id": "YQURTHNPSe05RPSlYRcok",
"fillStyle": "hachure",
@@ -41,8 +41,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1137.7821926540798,
"y": 764.0207858615452,
"x": 1118.2101508246528,
"y": 763.5906914605034,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 88,
@@ -51,7 +51,7 @@
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567336690,
"updated": 1652177702292,
"link": null,
"locked": false,
"fontSize": 36,
@@ -65,8 +65,8 @@
},
{
"type": "text",
"version": 1139,
"versionNonce": 1994750018,
"version": 1191,
"versionNonce": 1166344150,
"isDeleted": false,
"id": "6dpF2EyZBtYgO6MrvGj0-",
"fillStyle": "hachure",
@@ -75,8 +75,8 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 900.5941772460938,
"y": 819.7337171766493,
"x": 875.3033447265625,
"y": 820.7327100965712,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 612,
@@ -85,7 +85,7 @@
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567334181,
"updated": 1652177705177,
"link": null,
"locked": false,
"fontSize": 28,
@@ -99,8 +99,8 @@
},
{
"type": "rectangle",
"version": 805,
"versionNonce": 1609410334,
"version": 858,
"versionNonce": 1118008458,
"isDeleted": false,
"id": "cpnTMy7L2AUg9IDJppF4H",
"fillStyle": "hachure",
@@ -110,23 +110,23 @@
"opacity": 100,
"angle": 0,
"x": 600.9835205078125,
"y": 627.2060089111328,
"y": 635.5783640543619,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 298.6342163085937,
"height": 96.74092102050778,
"width": 335.3091227213542,
"height": 82.36856587727866,
"seed": 77164935,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177872265,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1011,
"versionNonce": 477782466,
"version": 1077,
"versionNonce": 1122201878,
"isDeleted": false,
"id": "9-blmNVtLesthMSY_f60t",
"fillStyle": "hachure",
@@ -135,32 +135,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 628.7854614257812,
"y": 658.9062805175781,
"x": 649.8531494140625,
"y": 660.223378499349,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 238,
"height": 35,
"width": 224,
"height": 36,
"seed": 860091815,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177872265,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Container Images",
"text": "Container Image",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "Container Images"
"originalText": "Container Image"
},
{
"type": "rectangle",
"version": 1051,
"versionNonce": 1210520414,
"version": 1118,
"versionNonce": 1679315786,
"isDeleted": false,
"id": "gugZxhi7ThlcjWY_MFO7q",
"fillStyle": "hachure",
@@ -169,24 +169,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 911.257568359375,
"y": 625.7697677612305,
"x": 954.3485412597656,
"y": 635.849225362142,
"strokeColor": "#000000",
"backgroundColor": "#be4bdb",
"width": 452.44976806640636,
"height": 99.05134582519533,
"width": 409.35879516601574,
"height": 82.97188822428383,
"seed": 1232790121,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177872265,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1202,
"versionNonce": 842114,
"version": 1300,
"versionNonce": 1187044950,
"isDeleted": false,
"id": "K48gtpesBxIGJxLTnI2CB",
"fillStyle": "hachure",
@@ -195,17 +195,17 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1065.672119140625,
"y": 656.4816131591797,
"x": 1084.4311319986978,
"y": 660.9795074462891,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 137,
"height": 35,
"width": 139,
"height": 36,
"seed": 449264361,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177872265,
"link": null,
"locked": false,
"fontSize": 28,
@@ -219,8 +219,8 @@
},
{
"type": "rectangle",
"version": 1163,
"versionNonce": 1149481794,
"version": 1204,
"versionNonce": 688085514,
"isDeleted": false,
"id": "La6f87LDZ0uEIZB947bXo",
"fillStyle": "hachure",
@@ -230,23 +230,23 @@
"opacity": 100,
"angle": 0,
"x": 1375.0136108398438,
"y": 626.2495651245117,
"y": 636.5654322306316,
"strokeColor": "#000000",
"backgroundColor": "#12b886",
"width": 452.76554361979186,
"height": 96.3990020751953,
"height": 80.08313496907543,
"seed": 2005637801,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567157857,
"updated": 1652177872265,
"link": null,
"locked": false
},
{
"type": "text",
"version": 1371,
"versionNonce": 1552918366,
"version": 1432,
"versionNonce": 1593746326,
"isDeleted": false,
"id": "aOgRPVQ81jhOfkvzjWTMF",
"fillStyle": "hachure",
@@ -255,162 +255,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1490.9330512152778,
"y": 654.3717736138237,
"x": 1498.8465237087673,
"y": 658.0244835747612,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 224,
"width": 201,
"height": 36,
"seed": 1284472935,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567166643,
"updated": 1652177872265,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 1,
"text": "Git Repositories",
"text": "Git Repository",
"baseline": 25,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "Git Repositories"
"originalText": "Git Repository"
},
{
"type": "rectangle",
"version": 2340,
"versionNonce": 1952732126,
"isDeleted": false,
"id": "p8fn5gPx8DfP8QE1lN98-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1064.5642678676506,
"y": 537.71609717149,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 684019996,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2497,
"versionNonce": 832692482,
"isDeleted": false,
"id": "kFTL0HnUdDs_ngg2xVFbn",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 600.7947366176511,
"y": 538.67312842149,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 541443108,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2345,
"versionNonce": 1129796482,
"isDeleted": false,
"id": "KIztJcYHiVtM-GMlZAXAE",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 753.0955178676511,
"y": 538.61062842149,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 145,
"height": 77.80606079101562,
"seed": 424425892,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567234562,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2329,
"versionNonce": 1518497986,
"isDeleted": false,
"id": "IWq_LcOearBV5cvqbJ_-o",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 911.3064553676511,
"y": 537.97781592149,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 468230812,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2318,
"versionNonce": 1504408670,
"isDeleted": false,
"id": "TXeK066NA0hvyPSeZl1a5",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1218.075986617651,
"y": 536.86062842149,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 144.5880126953128,
"height": 77.80606079101562,
"seed": 1408574372,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2364,
"versionNonce": 3868802,
"version": 2792,
"versionNonce": 183831882,
"isDeleted": false,
"id": "10WjipxoLx2zzSI91pXbR",
"fillStyle": "hachure",
@@ -419,24 +289,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1375.544736617651,
"y": 537.04812842149,
"x": 599.7894943723566,
"y": 905.6027750791251,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 144.5880126953128,
"height": 77.80606079101562,
"width": 344.482180700969,
"height": 83.67398764683533,
"seed": 1813731484,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177825759,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2355,
"versionNonce": 2067347614,
"version": 2771,
"versionNonce": 617525398,
"isDeleted": false,
"id": "M7Cngti6H0_kawKRN8yJ6",
"fillStyle": "hachure",
@@ -445,58 +315,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1528.868955367651,
"y": 535.89187842149,
"x": 963.2554264391833,
"y": 904.2447769132434,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 144.5880126953128,
"height": 77.80606079101562,
"width": 402.42137951281796,
"height": 86.03696372105414,
"seed": 1260603804,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177777585,
"link": null,
"locked": false
},
{
"type": "text",
"version": 597,
"versionNonce": 1519036482,
"isDeleted": false,
"id": "GHDrLyBOErQtv_WT5Lx3p",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 614.2293679653073,
"y": 565.5605338169978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 114,
"height": 25,
"seed": 13297180,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Vulnerability"
},
{
"type": "text",
"version": 655,
"versionNonce": 1728345310,
"version": 1057,
"versionNonce": 405881110,
"isDeleted": false,
"id": "Iq57wFRtO1a8AU0rT6lRD",
"fillStyle": "hachure",
@@ -505,23 +341,23 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1533.6317117153076,
"y": 565.2714713169978,
"x": 1046.152429428344,
"y": 930.8676815998951,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 136.25488281249991,
"height": 21.80078124999998,
"width": 218,
"height": 36,
"seed": 1329695396,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177655817,
"link": null,
"locked": false,
"fontSize": 17.440624999999976,
"fontSize": 28,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 14.800781249999979,
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
@@ -529,42 +365,8 @@
},
{
"type": "text",
"version": 666,
"versionNonce": 1364217858,
"isDeleted": false,
"id": "gjnZl9nxrqzliwPk8sbK-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1067.4339578090576,
"y": 565.8827994419978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 136.25488281249991,
"height": 21.80078124999998,
"seed": 290336932,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 17.440624999999976,
"fontFamily": 1,
"text": "Misconfiguration",
"baseline": 14.800781249999979,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Misconfiguration"
},
{
"type": "text",
"version": 613,
"versionNonce": 1721641246,
"version": 883,
"versionNonce": 969949898,
"isDeleted": false,
"id": "_cm6xpfcL9Yv2XBK5MBZF",
"fillStyle": "hachure",
@@ -573,134 +375,32 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1390.1199929653073,
"y": 563.8456900669978,
"x": 681.3134368986982,
"y": 931.5212932384402,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 114,
"height": 25,
"width": 161,
"height": 36,
"seed": 807441828,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177624726,
"link": null,
"locked": false,
"fontSize": 20,
"fontSize": 28,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 18,
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,
"originalText": "Vulnerability"
},
{
"type": "text",
"version": 635,
"versionNonce": 2022375362,
"isDeleted": false,
"id": "An4-igVUkLzCwSdvDmtZl",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 925.6707742153073,
"y": 560.4550650669978,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 114,
"height": 25,
"seed": 1262859164,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "Vulnerability",
"baseline": 18,
"textAlign": "center",
"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,
"version": 2874,
"versionNonce": 1934391254,
"isDeleted": false,
"id": "Fq7meULupm1A9leboPlko",
"fillStyle": "hachure",
@@ -709,24 +409,24 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1684.2588079543348,
"y": 536.1830067815082,
"x": 1389.3043677318824,
"y": 903.8533384764222,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 144.5880126953128,
"height": 77.80606079101562,
"width": 437.15079032010976,
"height": 84.42746665074158,
"seed": 230693534,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567155021,
"updated": 1652177785481,
"link": null,
"locked": false
},
{
"type": "text",
"version": 760,
"versionNonce": 503898690,
"version": 1121,
"versionNonce": 110517002,
"isDeleted": false,
"id": "OUGk8nZzvgcKUHhKUcQov",
"fillStyle": "hachure",
@@ -735,23 +435,23 @@
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1723.2672240242127,
"y": 561.8650424396028,
"x": 1556.0451356485157,
"y": 930.8040952304675,
"strokeColor": "#000000",
"backgroundColor": "#82c91e",
"width": 65,
"height": 25,
"width": 91,
"height": 36,
"seed": 2044527454,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1650567240607,
"updated": 1652177636085,
"link": null,
"locked": false,
"fontSize": 20,
"fontSize": 28,
"fontFamily": 1,
"text": "Secret",
"baseline": 18,
"baseline": 25,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": null,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -8,7 +8,7 @@ hide:
`Trivy` (`tri` pronounced like **tri**gger, `vy` pronounced like en**vy**) is a simple and comprehensive [vulnerability][vulnerability]/[misconfiguration][misconf]/[secret][secret] scanner for containers and other artifacts.
`Trivy` detects vulnerabilities of [OS packages][os] (Alpine, RHEL, CentOS, etc.) and [language-specific packages][lang] (Bundler, Composer, npm, yarn, etc.).
In addition, `Trivy` scans [Infrastructure as Code (IaC) files][iac] such as Terraform and Kubernetes, to detect potential configuration issues that expose your deployments to the risk of attack.
In addition, `Trivy` scans [Infrastructure as Code (IaC) files][misconf] such as Terraform and Kubernetes, to detect potential configuration issues that expose your deployments to the risk of attack.
`Trivy` also scans [hardcoded secrets][secret] like passwords, API keys and tokens.
`Trivy` is easy to use. Just install the binary and you're ready to scan.
All you need to do for scanning is to specify a target such as an image name of the container.
@@ -44,11 +44,10 @@ Learn about our open source work and portfolio [here][oss].
Contact us about any matter by opening a GitHub Discussion [here][discussions]
[vulnerability]: docs/vulnerability/scanning/index.md
[misconf]: docs/misconfiguration/index.md
[misconf]: docs/misconfiguration/scanning.md
[secret]: docs/secret/scanning.md
[os]: docs/vulnerability/detection/os.md
[lang]: docs/vulnerability/detection/language.md
[iac]: docs/misconfiguration/iac.md
[aquasec]: https://aquasec.com
[oss]: https://www.aquasec.com/products/open-source-projects/

57
go.mod
View File

@@ -7,8 +7,8 @@ require (
github.com/Masterminds/sprig/v3 v3.2.2
github.com/NYTimes/gziphandler v1.1.1
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/fanal v0.0.0-20220503160602-873c6de8f5ae
github.com/aquasecurity/go-dep-parser v0.0.0-20220422134844-880747206031
github.com/aquasecurity/fanal v0.0.0-20220510103759-9ad40432d340
github.com/aquasecurity/go-dep-parser v0.0.0-20220503151658-d316f5cc2cff
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-pep440-version v0.0.0-20210121094942-22b2f8951d46
@@ -30,6 +30,7 @@ require (
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/olekukonko/tablewriter v0.0.5
github.com/open-policy-agent/opa v0.40.0
github.com/owenrumney/go-sarif/v2 v2.1.1
@@ -46,7 +47,7 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b
k8s.io/utils v0.0.0-20211116205334-6203023598ed
)
require (
@@ -68,7 +69,6 @@ require (
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
@@ -76,23 +76,21 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-cidr v1.1.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/aquasecurity/defsec v0.28.5-0.20220426090908-0df04f1fa28b // indirect
github.com/aquasecurity/tfsec v1.8.0 // indirect
github.com/aws/aws-sdk-go v1.43.31 // indirect
github.com/aquasecurity/defsec v0.56.0 // indirect
github.com/aws/aws-sdk-go v1.44.5 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bmatcuk/doublestar v1.3.4 // indirect
github.com/briandowns/spinner v1.12.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.2 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
github.com/containerd/containerd v1.6.3-0.20220401172941-5ff8fce1fcc6 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.11.3 // indirect
github.com/containerd/typeurl v1.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.11+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/cli v20.10.13+incompatible // indirect
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
@@ -111,40 +109,40 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.11.1 // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/hashicorp/hcl/v2 v2.12.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/klauspost/compress v1.14.2 // indirect
github.com/klauspost/compress v1.15.1 // indirect
github.com/knqyf263/go-rpmdb v0.0.0-20220209103220-0f7a6d951a6d // indirect
github.com/knqyf263/nested v0.0.1 // indirect
github.com/liamg/iamgo v0.0.6 // indirect
github.com/liamg/jfather v0.0.7 // indirect
github.com/liamg/memoryfs v1.4.1 // indirect
github.com/liamg/tml v0.6.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/moby/buildkit v0.9.3 // indirect
github.com/moby/sys/mount v0.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/buildkit v0.10.3 // indirect
github.com/moby/sys/mount v0.3.0 // indirect
github.com/moby/sys/mountinfo v0.6.0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.0 // indirect
github.com/owenrumney/squealer v0.3.2 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/opencontainers/runc v1.1.1 // indirect
github.com/owenrumney/squealer v1.0.1-0.20220510063705-c0be93f0edea // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
@@ -158,7 +156,6 @@ require (
github.com/spdx/tools-golang v0.3.0
github.com/spf13/cast v1.4.1 // indirect
github.com/stretchr/objx v0.3.0 // indirect
github.com/tmccombs/hcl2json v0.3.4 // indirect
github.com/ulikunitz/xz v0.5.8 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
@@ -196,10 +193,10 @@ require (
modernc.org/sqlite v1.14.5 // indirect
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
require github.com/mitchellh/hashstructure/v2 v2.0.2
// To resolve CVE-2022-23648
replace github.com/containerd/containerd v1.5.9 => github.com/containerd/containerd v1.5.10
// See https://github.com/moby/moby/issues/42939#issuecomment-1114255529
replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible

611
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -68,8 +68,8 @@ func TestFilesystem(t *testing.T) {
name: "dockerfile",
args: args{
securityChecks: "config",
policyPaths: []string{"testdata/fixtures/fs/dockerfile/policy"},
input: "testdata/fixtures/fs/dockerfile",
namespaces: []string{"testing"},
},
golden: "testdata/dockerfile.json.golden",
},

View File

@@ -20,34 +20,42 @@
"Class": "config",
"Type": "dockerfile",
"MisconfSummary": {
"Successes": 0,
"Successes": 23,
"Failures": 2,
"Exceptions": 0
},
"Misconfigurations": [
{
"Type": "N/A",
"Type": "Dockerfile Security Check",
"ID": "N/A",
"Title": "N/A",
"Description": "Rego module: data.user.bar",
"Message": "something bad: bar",
"Namespace": "user.bar",
"Query": "data.user.bar.deny",
"Severity": "UNKNOWN",
"Status": "FAIL",
"Layer": {},
"IacMetadata": {}
"IacMetadata": {
"Provider": "Generic",
"Service": "general"
}
},
{
"Type": "N/A",
"Type": "Dockerfile Security Check",
"ID": "N/A",
"Title": "N/A",
"Description": "Rego module: data.user.foo",
"Message": "something bad: foo",
"Namespace": "user.foo",
"Query": "data.user.foo.deny",
"Severity": "UNKNOWN",
"Status": "FAIL",
"Layer": {},
"IacMetadata": {}
"IacMetadata": {
"Provider": "Generic",
"Service": "general"
}
}
]
}

View File

@@ -22,7 +22,7 @@
"MisconfSummary": {
"Successes": 0,
"Failures": 0,
"Exceptions": 1
"Exceptions": 23
}
}
]

View File

@@ -20,7 +20,7 @@
"Class": "config",
"Type": "dockerfile",
"MisconfSummary": {
"Successes": 0,
"Successes": 22,
"Failures": 0,
"Exceptions": 1
}

View File

@@ -20,7 +20,7 @@
"Class": "config",
"Type": "dockerfile",
"MisconfSummary": {
"Successes": 0,
"Successes": 22,
"Failures": 1,
"Exceptions": 0
},
@@ -29,20 +29,23 @@
"Type": "Dockerfile Security Check",
"ID": "DS002",
"Title": "Image user should not be 'root'",
"Description": "It is a good practice to run the container as a non-root user.",
"Message": "Specify at least 1 USER command in Dockerfile",
"Namespace": "appshield.dockerfile.DS002",
"Query": "data.appshield.dockerfile.DS002.deny",
"Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
"Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument",
"Namespace": "builtin.dockerfile.DS002",
"Query": "data.builtin.dockerfile.DS002.deny",
"Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile",
"Severity": "HIGH",
"PrimaryURL": "https://avd.aquasec.com/appshield/ds002",
"PrimaryURL": "https://avd.aquasec.com/misconfig/ds002",
"References": [
"https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
"https://avd.aquasec.com/appshield/ds002"
"https://avd.aquasec.com/misconfig/ds002"
],
"Status": "FAIL",
"Layer": {},
"IacMetadata": {}
"IacMetadata": {
"Provider": "Dockerfile",
"Service": "general"
}
}
]
}

View File

@@ -1 +1,3 @@
FROM alpine:3.13
FROM alpine:3.13
USER nobody

View File

@@ -1,49 +0,0 @@
package appshield.dockerfile.DS002
import data.lib.docker
__rego_metadata__ := {
"id": "DS002",
"title": "Image user should not be 'root'",
"version": "v1.0.0",
"severity": "HIGH",
"type": "Dockerfile Security Check",
"description": "It is a good practice to run the container as a non-root user.",
"recommended_actions": "Add 'USER <non root user name>' line to the Dockerfile",
"url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"
}
__rego_input__ := {
"combine": false,
"selector": [{"type": "dockerfile"}],
}
# get_user returns all the usernames from
# the USER command.
get_user[username] {
user := docker.user[_]
username := user.Value[_]
}
# fail_user_count is true if there is no USER command.
fail_user_count {
count(get_user) < 1
}
# fail_last_user_root is true if the last USER command
# value is "root"
fail_last_user_root {
user := cast_array(get_user)
len := count(get_user)
user[minus(len, 1)] == "root"
}
deny[msg] {
fail_user_count
msg = "Specify at least 1 USER command in Dockerfile"
}
deny[res] {
fail_last_user_root
res := "Last USER command in Dockerfile should not be root"
}

View File

@@ -4,5 +4,5 @@ import data.namespaces
exception[ns] {
ns := data.namespaces[_]
startswith(ns, "appshield")
startswith(ns, "builtin")
}

View File

@@ -1,48 +0,0 @@
package appshield.dockerfile.DS002
import data.lib.docker
__rego_metadata__ := {
"id": "DS002",
"title": "Image user should not be 'root'",
"version": "v1.0.0",
"severity": "HIGH",
"type": "Dockerfile Security Check",
"description": "It is a good practice to run the container as a non-root user.",
"recommended_actions": "Add 'USER <non root user name>' line to the Dockerfile",
}
__rego_input__ := {
"combine": false,
"selector": [{"type": "dockerfile"}],
}
# get_user returns all the usernames from
# the USER command.
get_user[username] {
user := docker.user[_]
username := user.Value[_]
}
# fail_user_count is true if there is no USER command.
fail_user_count {
count(get_user) < 1
}
# fail_last_user_root is true if the last USER command
# value is "root"
fail_last_user_root {
user := cast_array(get_user)
len := count(get_user)
user[minus(len, 1)] == "root"
}
deny[msg] {
fail_user_count
msg = "Specify at least 1 USER command in Dockerfile"
}
deny[res] {
fail_last_user_root
res := "Last USER command in Dockerfile should not be root"
}

View File

@@ -1,4 +1,4 @@
package appshield.dockerfile.DS002
package builtin.dockerfile.DS002
exception[rules] {
instruction := input.stages[_][_]

View File

@@ -1,48 +0,0 @@
package appshield.dockerfile.DS002
import data.lib.docker
__rego_metadata__ := {
"id": "DS002",
"title": "Image user should not be 'root'",
"version": "v1.0.0",
"severity": "HIGH",
"type": "Dockerfile Security Check",
"description": "It is a good practice to run the container as a non-root user.",
"recommended_actions": "Add 'USER <non root user name>' line to the Dockerfile",
}
__rego_input__ := {
"combine": false,
"selector": [{"type": "dockerfile"}],
}
# get_user returns all the usernames from
# the USER command.
get_user[username] {
user := docker.user[_]
username := user.Value[_]
}
# fail_user_count is true if there is no USER command.
fail_user_count {
count(get_user) < 1
}
# fail_last_user_root is true if the last USER command
# value is "root"
fail_last_user_root {
user := cast_array(get_user)
len := count(get_user)
user[minus(len, 1)] == "root"
}
deny[msg] {
fail_user_count
msg = "Specify at least 1 USER command in Dockerfile"
}
deny[res] {
fail_last_user_root
res := "Last USER command in Dockerfile should not be root"
}

View File

@@ -158,4 +158,4 @@
]
}
]
}
}

View File

@@ -37,10 +37,7 @@ nav:
- Languages:
- Go: docs/vulnerability/languages/golang.md
- Misconfiguration:
- Scanning:
- Overview: docs/misconfiguration/index.md
- Infrastructure as Code: docs/misconfiguration/iac.md
- Filesystem: docs/misconfiguration/filesystem.md
- Scanning: docs/misconfiguration/scanning.md
- Policy:
- Built-in Policies: docs/misconfiguration/policy/builtin.md
- Exceptions: docs/misconfiguration/policy/exceptions.md

View File

@@ -25,7 +25,7 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
const defaultPolicyNamespace = "appshield"
var defaultPolicyNamespaces = []string{"appshield", "defsec", "builtin"}
var errSkipScan = errors.New("skip subsequent processes")
@@ -223,16 +223,10 @@ func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner,
// ScannerOption is filled only when config scanning is enabled.
var configScannerOptions config.ScannerOption
if slices.Contains(opt.SecurityChecks, types.SecurityCheckConfig) {
noProgress := opt.Quiet || opt.NoProgress
builtinPolicyPaths, err := operation.InitBuiltinPolicies(ctx, opt.CacheDir, noProgress, opt.SkipPolicyUpdate)
if err != nil {
return types.Report{}, xerrors.Errorf("failed to initialize built-in policies: %w", err)
}
configScannerOptions = config.ScannerOption{
Trace: opt.Trace,
Namespaces: append(opt.PolicyNamespaces, defaultPolicyNamespace),
PolicyPaths: append(opt.PolicyPaths, builtinPolicyPaths...),
Namespaces: append(opt.PolicyNamespaces, defaultPolicyNamespaces...),
PolicyPaths: opt.PolicyPaths,
DataPaths: opt.DataPaths,
FilePatterns: opt.FilePatterns,
}

View File

@@ -15,7 +15,6 @@ import (
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/utils"
)
@@ -123,40 +122,6 @@ func DownloadDB(appVersion, cacheDir, dbRepository string, quiet, skipUpdate boo
return nil
}
// InitBuiltinPolicies downloads the built-in policies and loads them
func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate bool) ([]string, error) {
client, err := policy.NewClient(cacheDir, quiet)
if err != nil {
return nil, xerrors.Errorf("policy client error: %w", err)
}
needsUpdate := false
if !skipUpdate {
needsUpdate, err = client.NeedsUpdate()
if err != nil {
return nil, xerrors.Errorf("unable to check if built-in policies need to be updated: %w", err)
}
}
if needsUpdate {
log.Logger.Info("Need to update the built-in policies")
log.Logger.Info("Downloading the built-in policies...")
if err = client.DownloadBuiltinPolicies(ctx); err != nil {
return nil, xerrors.Errorf("failed to download built-in policies: %w", err)
}
}
policyPaths, err := client.LoadBuiltinPolicies()
if err != nil {
if skipUpdate {
log.Logger.Info("No built-in policies were loaded")
return nil, nil
}
return nil, xerrors.Errorf("policy load error: %w", err)
}
return policyPaths, nil
}
func showDBInfo(cacheDir string) error {
m := metadata.NewClient(cacheDir)
meta, err := m.Get()

View File

@@ -1,213 +0,0 @@
package policy
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/open-policy-agent/opa/bundle"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
bundleVersion = 1
bundleRepository = "ghcr.io/aquasecurity/appshield"
policyMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
updateInterval = 24 * time.Hour
)
type options struct {
artifact *oci.Artifact
clock clock.Clock
}
// Option is a functional option
type Option func(*options)
// WithOCIArtifact takes an OCI artifact
func WithOCIArtifact(art *oci.Artifact) Option {
return func(opts *options) {
opts.artifact = art
}
}
// WithClock takes a clock
func WithClock(clock clock.Clock) Option {
return func(opts *options) {
opts.clock = clock
}
}
// Metadata holds default policy metadata
type Metadata struct {
Digest string
DownloadedAt time.Time
}
// Client implements policy operations
type Client struct {
*options
policyDir string
quiet bool
}
// NewClient is the factory method for policy client
func NewClient(cacheDir string, quiet bool, opts ...Option) (*Client, error) {
o := &options{
clock: clock.RealClock{},
}
for _, opt := range opts {
opt(o)
}
return &Client{
options: o,
policyDir: filepath.Join(cacheDir, "policy"),
quiet: quiet,
}, nil
}
// LoadBuiltinPolicies loads default policies
func (c *Client) LoadBuiltinPolicies() ([]string, error) {
f, err := os.Open(c.manifestPath())
if err != nil {
return nil, xerrors.Errorf("manifest file open error (%s): %w", c.manifestPath(), err)
}
var manifest bundle.Manifest
if err = json.NewDecoder(f).Decode(&manifest); err != nil {
return nil, xerrors.Errorf("json decode error (%s): %w", c.manifestPath(), err)
}
// If the "roots" field is not included in the manifest it defaults to [""]
// which means that ALL data and policy must come from the bundle.
if manifest.Roots == nil || len(*manifest.Roots) == 0 {
return []string{c.contentDir()}, nil
}
var policyPaths []string
for _, root := range *manifest.Roots {
policyPaths = append(policyPaths, filepath.Join(c.contentDir(), root))
}
return policyPaths, nil
}
// NeedsUpdate returns if the default policy should be updated
func (c *Client) NeedsUpdate() (bool, error) {
f, err := os.Open(c.metadataPath())
if err != nil {
log.Logger.Debugf("Failed to open the policy metadata: %s", err)
return true, nil
}
var meta Metadata
if err = json.NewDecoder(f).Decode(&meta); err != nil {
log.Logger.Warnf("Policy metadata decode error: %s", err)
return true, nil
}
// No need to update if it's been within a day since the last update.
if c.clock.Now().Before(meta.DownloadedAt.Add(updateInterval)) {
return false, nil
}
if err = c.populateOCIArtifact(); err != nil {
return false, xerrors.Errorf("OPA bundle error: %w", err)
}
digest, err := c.artifact.Digest()
if err != nil {
return false, xerrors.Errorf("digest error: %w", err)
}
if meta.Digest != digest {
return true, nil
}
// Update DownloadedAt with the current time.
// Otherwise, if there are no updates in the remote registry,
// the digest will be fetched every time even after this.
if err = c.updateMetadata(meta.Digest, time.Now()); err != nil {
return false, xerrors.Errorf("unable to update the policy metadata: %w", err)
}
return false, nil
}
func (c *Client) populateOCIArtifact() error {
if c.artifact == nil {
repo := fmt.Sprintf("%s:%d", bundleRepository, bundleVersion)
art, err := oci.NewArtifact(repo, policyMediaType, c.quiet)
if err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
c.artifact = art
}
return nil
}
// DownloadBuiltinPolicies download default policies from GitHub Pages
func (c *Client) DownloadBuiltinPolicies(ctx context.Context) error {
if err := c.populateOCIArtifact(); err != nil {
return xerrors.Errorf("OPA bundle error: %w", err)
}
dst := c.contentDir()
if err := c.artifact.Download(ctx, dst); err != nil {
return xerrors.Errorf("download error: %w", err)
}
digest, err := c.artifact.Digest()
if err != nil {
return xerrors.Errorf("digest error: %w", err)
}
log.Logger.Debugf("Digest of the built-in policies: %s", digest)
// Update metadata.json with the new digest and the current date
if err = c.updateMetadata(digest, c.clock.Now()); err != nil {
return xerrors.Errorf("unable to update the policy metadata: %w", err)
}
return nil
}
func (c *Client) updateMetadata(digest string, now time.Time) error {
f, err := os.Create(c.metadataPath())
if err != nil {
return xerrors.Errorf("failed to open a policy manifest: %w", err)
}
defer f.Close()
meta := Metadata{
Digest: digest,
DownloadedAt: now,
}
if err = json.NewEncoder(f).Encode(meta); err != nil {
return xerrors.Errorf("json encode error: %w", err)
}
return nil
}
func (c *Client) contentDir() string {
return filepath.Join(c.policyDir, "content")
}
func (c *Client) metadataPath() string {
return filepath.Join(c.policyDir, "metadata.json")
}
func (c *Client) manifestPath() string {
return filepath.Join(c.contentDir(), bundle.ManifestExt)
}

View File

@@ -1,331 +0,0 @@
package policy_test
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"testing"
"time"
v1 "github.com/google/go-containerregistry/pkg/v1"
fakei "github.com/google/go-containerregistry/pkg/v1/fake"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/utils/clock"
fake "k8s.io/utils/clock/testing"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/aquasecurity/trivy/pkg/policy"
)
type fakeLayer struct {
v1.Layer
}
func (f fakeLayer) MediaType() (types.MediaType, error) {
return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil
}
func newFakeLayer(t *testing.T) v1.Layer {
layer, err := tarball.LayerFromFile("testdata/bundle.tar.gz")
require.NoError(t, err)
return fakeLayer{layer}
}
type brokenLayer struct {
v1.Layer
}
func (b brokenLayer) MediaType() (types.MediaType, error) {
return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil
}
func (b brokenLayer) Compressed() (io.ReadCloser, error) {
return nil, fmt.Errorf("compressed error")
}
func newBrokenLayer(t *testing.T) v1.Layer {
layer, err := tarball.LayerFromFile("testdata/bundle.tar.gz")
require.NoError(t, err)
return brokenLayer{layer}
}
func TestClient_LoadBuiltinPolicies(t *testing.T) {
tests := []struct {
name string
cacheDir string
want []string
wantErr string
}{
{
name: "happy path",
cacheDir: "testdata/happy",
want: []string{
"testdata/happy/policy/content/kubernetes",
"testdata/happy/policy/content/docker",
},
},
{
name: "empty roots",
cacheDir: "testdata/empty",
want: []string{
"testdata/empty/policy/content",
},
},
{
name: "broken manifest",
cacheDir: "testdata/broken",
want: []string{},
wantErr: "json decode error",
},
{
name: "no such file",
cacheDir: "testdata/unknown",
want: []string{},
wantErr: "manifest file open error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil)
// Mock OCI artifact
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tt.cacheDir, true, policy.WithOCIArtifact(art))
require.NoError(t, err)
got, err := c.LoadBuiltinPolicies()
if tt.wantErr != "" {
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestClient_NeedsUpdate(t *testing.T) {
type digestReturns struct {
h v1.Hash
err error
}
tests := []struct {
name string
clock clock.Clock
digestReturns digestReturns
metadata interface{}
want bool
wantErr bool
}{
{
name: "recent download",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
},
{
name: "same digest",
clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)),
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
},
{
name: "different digest",
clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)),
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: true,
},
{
name: "sad: Digest returns an error",
clock: fake.NewFakeClock(time.Date(2021, 1, 2, 1, 0, 0, 0, time.UTC)),
digestReturns: digestReturns{
err: fmt.Errorf("error"),
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
wantErr: true,
},
{
name: "sad: non-existent metadata",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
want: true,
},
{
name: "sad: broken metadata",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
metadata: `"foo"`,
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up a temporary directory
tmpDir := t.TempDir()
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil)
img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err)
// Create a policy directory
err := os.MkdirAll(filepath.Join(tmpDir, "policy"), os.ModePerm)
require.NoError(t, err)
if tt.metadata != nil {
b, err := json.Marshal(tt.metadata)
require.NoError(t, err)
// Write a metadata file
metadataPath := filepath.Join(tmpDir, "policy", "metadata.json")
err = os.WriteFile(metadataPath, b, os.ModePerm)
require.NoError(t, err)
}
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tmpDir, true, policy.WithOCIArtifact(art), policy.WithClock(tt.clock))
require.NoError(t, err)
// Assert results
got, err := c.NeedsUpdate()
assert.Equal(t, tt.wantErr, err != nil)
assert.Equal(t, tt.want, got)
})
}
}
func TestClient_DownloadBuiltinPolicies(t *testing.T) {
type digestReturns struct {
h v1.Hash
err error
}
type layersReturns struct {
layers []v1.Layer
err error
}
tests := []struct {
name string
clock clock.Clock
layersReturns layersReturns
digestReturns digestReturns
want *policy.Metadata
wantErr string
}{
{
name: "happy path",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{newFakeLayer(t)},
},
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
},
{
name: "sad: broken layer",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{newBrokenLayer(t)},
},
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
wantErr: "compressed error",
},
{
name: "sad: Digest returns an error",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{newFakeLayer(t)},
},
digestReturns: digestReturns{
err: fmt.Errorf("error"),
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
wantErr: "digest error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
// Mock image
img := new(fakei.FakeImage)
img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err)
img.LayersReturns(tt.layersReturns.layers, tt.layersReturns.err)
// Mock OCI artifact
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tempDir, true, policy.WithClock(tt.clock), policy.WithOCIArtifact(art))
require.NoError(t, err)
err = c.DownloadBuiltinPolicies(context.Background())
if tt.wantErr != "" {
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
assert.NoError(t, err)
// Assert metadata.json
metadata := filepath.Join(tempDir, "policy", "metadata.json")
b, err := os.ReadFile(metadata)
require.NoError(t, err)
got := new(policy.Metadata)
err = json.Unmarshal(b, got)
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,3 +0,0 @@
{
"revision": 1
}

Binary file not shown.

View File

@@ -1,3 +0,0 @@
{
"revision": "1"
}

View File

@@ -1,4 +0,0 @@
{
"revision": "1",
"roots": ["kubernetes", "docker"]
}

View File

@@ -54,6 +54,8 @@ type sarifData struct {
artifactLocation string
message string
cvssScore string
startLine int
endLine int
}
func (sw *SarifWriter) addSarifRule(data *sarifData) {
@@ -85,9 +87,14 @@ func (sw *SarifWriter) addSarifRule(data *sarifData) {
func (sw *SarifWriter) addSarifResult(data *sarifData) {
sw.addSarifRule(data)
region := sarif.NewRegion().WithStartLine(1)
if data.startLine > 0 {
region = sarif.NewSimpleRegion(data.startLine, data.endLine)
}
location := sarif.NewPhysicalLocation().
WithArtifactLocation(sarif.NewSimpleArtifactLocation(data.artifactLocation).WithUriBaseId("ROOTPATH")).
WithRegion(sarif.NewRegion().WithStartLine(1))
WithRegion(region)
result := sarif.NewRuleResult(data.vulnerabilityId).
WithRuleIndex(data.resultIndex).
WithMessage(sarif.NewTextMessage(data.message)).
@@ -153,6 +160,8 @@ func (sw SarifWriter) Write(report types.Report) error {
url: misconf.PrimaryURL,
resourceClass: string(res.Class),
artifactLocation: toPathUri(res.Target),
startLine: misconf.IacMetadata.StartLine,
endLine: misconf.IacMetadata.EndLine,
resultIndex: getRuleIndex(misconf.ID, ruleIndexes),
fullDescription: html.EscapeString(misconf.Description),
helpText: fmt.Sprintf("Misconfiguration %v\nType: %s\nSeverity: %v\nCheck: %v\nMessage: %v\nLink: [%v](%v)\n%s",

View File

@@ -109,24 +109,24 @@ func TestWriter_Write(t *testing.T) {
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
},
Packages: map[spdx.ElementID]*spdx.Package2_2{
spdx.ElementID("e27d088813d330a4"): {
PackageSPDXIdentifier: spdx.ElementID("e27d088813d330a4"),
spdx.ElementID("65e3655ffcc41ab9"): {
PackageSPDXIdentifier: spdx.ElementID("65e3655ffcc41ab9"),
PackageName: "actioncontroller",
PackageVersion: "7.0.0",
PackageLicenseConcluded: "NONE",
PackageLicenseDeclared: "NONE",
IsFilesAnalyzedTagPresent: true,
},
spdx.ElementID("163ff5a6292fef8c"): {
PackageSPDXIdentifier: spdx.ElementID("163ff5a6292fef8c"),
spdx.ElementID("97cf5c89611089c6"): {
PackageSPDXIdentifier: spdx.ElementID("97cf5c89611089c6"),
PackageName: "actionpack",
PackageVersion: "7.0.0",
PackageLicenseConcluded: "NONE",
PackageLicenseDeclared: "NONE",
IsFilesAnalyzedTagPresent: true,
},
spdx.ElementID("a4aded544ebeda0a"): {
PackageSPDXIdentifier: spdx.ElementID("a4aded544ebeda0a"),
spdx.ElementID("3ee76dba6a695d6d"): {
PackageSPDXIdentifier: spdx.ElementID("3ee76dba6a695d6d"),
PackageName: "binutils",
PackageVersion: "2.30",
PackageLicenseConcluded: "GPLv3+",
@@ -220,24 +220,24 @@ func TestWriter_Write(t *testing.T) {
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
},
Packages: map[spdx.ElementID]*spdx.Package2_2{
spdx.ElementID("d963712012fcbc8c"): {
PackageSPDXIdentifier: spdx.ElementID("d963712012fcbc8c"),
spdx.ElementID("40d016db96700ecb"): {
PackageSPDXIdentifier: spdx.ElementID("40d016db96700ecb"),
PackageName: "acl",
PackageVersion: "2.2.53",
PackageLicenseConcluded: "GPLv2+",
PackageLicenseDeclared: "GPLv2+",
IsFilesAnalyzedTagPresent: true,
},
spdx.ElementID("17ba95e087440896"): {
PackageSPDXIdentifier: spdx.ElementID("17ba95e087440896"),
spdx.ElementID("ff543ca421929db5"): {
PackageSPDXIdentifier: spdx.ElementID("ff543ca421929db5"),
PackageName: "actionpack",
PackageVersion: "7.0.0",
PackageLicenseConcluded: "NONE",
PackageLicenseDeclared: "NONE",
IsFilesAnalyzedTagPresent: true,
},
spdx.ElementID("50ac94ac1875540"): {
PackageSPDXIdentifier: spdx.ElementID("50ac94ac1875540"),
spdx.ElementID("639cce3bbd87450f"): {
PackageSPDXIdentifier: spdx.ElementID("639cce3bbd87450f"),
PackageName: "actionpack",
PackageVersion: "7.0.1",
PackageLicenseConcluded: "NONE",
@@ -285,8 +285,8 @@ func TestWriter_Write(t *testing.T) {
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
},
Packages: map[spdx.ElementID]*spdx.Package2_2{
spdx.ElementID("5c11ed655628960c"): {
PackageSPDXIdentifier: spdx.ElementID("5c11ed655628960c"),
spdx.ElementID("9572b967bcbc8ea2"): {
PackageSPDXIdentifier: spdx.ElementID("9572b967bcbc8ea2"),
PackageName: "actioncable",
PackageVersion: "6.1.4.1",
PackageLicenseConcluded: "NONE",
@@ -334,8 +334,8 @@ func TestWriter_Write(t *testing.T) {
ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
},
Packages: map[spdx.ElementID]*spdx.Package2_2{
spdx.ElementID("983b94af8413fe04"): {
PackageSPDXIdentifier: spdx.ElementID("983b94af8413fe04"),
spdx.ElementID("1275fe237f4887b3"): {
PackageSPDXIdentifier: spdx.ElementID("1275fe237f4887b3"),
PackageName: "ruby-typeprof",
PackageVersion: "0.20.1",
PackageLicenseConcluded: "MIT",

View File

@@ -21,7 +21,7 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
_ "github.com/aquasecurity/fanal/analyzer/all"
_ "github.com/aquasecurity/fanal/hook/all"
_ "github.com/aquasecurity/fanal/handler/all"
)
var (
@@ -309,8 +309,8 @@ func toDetectedMisconfiguration(res ftypes.MisconfResult, defaultSeverity dbType
}
var primaryURL string
if strings.HasPrefix(res.Namespace, "appshield.") {
primaryURL = fmt.Sprintf("https://avd.aquasec.com/appshield/%s", strings.ToLower(res.ID))
if strings.HasPrefix(res.Namespace, "builtin.") {
primaryURL = fmt.Sprintf("https://avd.aquasec.com/misconfig/%s", strings.ToLower(res.ID))
res.References = append(res.References, primaryURL)
} else if strings.Contains(res.Type, "tfsec") {
for _, ref := range res.References {

View File

@@ -687,7 +687,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/configs/deployment.yaml",
Successes: []ftypes.MisconfResult{
{
Namespace: "appshield.kubernetes.id200",
Namespace: "builtin.kubernetes.id200",
PolicyMetadata: ftypes.PolicyMetadata{
ID: "ID200",
Type: "Kubernetes Security Check",
@@ -739,11 +739,11 @@ func TestScanner_Scan(t *testing.T) {
ID: "ID200",
Title: "Bad Deployment",
Message: "No issues found",
Namespace: "appshield.kubernetes.id200",
Namespace: "builtin.kubernetes.id200",
Severity: "MEDIUM",
PrimaryURL: "https://avd.aquasec.com/appshield/id200",
PrimaryURL: "https://avd.aquasec.com/misconfig/id200",
References: []string{
"https://avd.aquasec.com/appshield/id200",
"https://avd.aquasec.com/misconfig/id200",
},
Status: types.StatusPassed,
Layer: ftypes.Layer{