Compare commits

..

57 Commits

Author SHA1 Message Date
Teppei Fukuda
bc0836623c fix(cli): pass integer to exit-on-eol (#3716) 2023-03-01 12:18:11 +02:00
Itay Shakury
23cdac02ee feat: add kubernetes pss compliance (#3498)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-03-01 12:10:34 +02:00
Kalyana Krishna Varanasi
302c8ae24c feat: Adding --module-dir and --enable-modules (#3677)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-03-01 12:09:53 +02:00
Teppei Fukuda
34120f4201 feat: add special IDs for filtering secrets (#3702) 2023-03-01 09:51:11 +02:00
simar7
e399ed8439 chore(deps): Update defsec (#3713)
* chore(deps): Update defsec

* fix tests
2023-03-01 08:10:03 +02:00
simar7
ef7b762e48 docs(misconf): Add guide on input schema (#3692)
* docs(misconf): Add guide on input schema

* Update docs/docs/misconfiguration/custom/schema.md

Co-authored-by: Itay Shakury <itay@itaysk.com>

* make schema usage more descriptive

* docs: point to the full page

* update docs

Signed-off-by: Simar <simar@linux.com>

---------

Signed-off-by: Simar <simar@linux.com>
Co-authored-by: Itay Shakury <itay@itaysk.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-28 15:55:49 -08:00
Teppei Fukuda
00daebc161 feat(go): support dependency graph and show only direct dependencies in the tree (#3691) 2023-02-28 13:24:53 +02:00
chenk
98d1031552 feat: docker multi credential support (#3631)
Signed-off-by: chenk <hen.keinan@gmail.com>
2023-02-28 11:42:37 +02:00
Teppei Fukuda
b791362871 feat: summarize vulnerabilities in compliance reports (#3651) 2023-02-28 00:09:00 +02:00
Teppei Fukuda
719fdb1b11 feat(python): parse pyproject.toml alongside poetry.lock (#3695)
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
2023-02-27 20:48:55 +02:00
DmitriyLewen
3ff5699b4b feat(python): add dependency tree for poetry lock file (#3665)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-26 16:15:00 +02:00
Masahiro331
33909d9df3 fix(cyclonedx): incompliant affect ref (#3679) 2023-02-26 16:04:29 +02:00
Manuel Morejón
d85a3e087b chore(helm): update skip-db-update environment variable (#3657)
Signed-off-by: Manuel Morejon <manuel@mmorejon.io>
2023-02-26 14:16:17 +02:00
Masahiro331
551899c24e fix(spdx): change CreationInfo timestamp format RFC3336Nano to RFC3336 (#3675) 2023-02-26 10:11:47 +02:00
Teppei Fukuda
3aaa2cfb75 fix(sbom): export empty dependencies in CycloneDX (#3664) 2023-02-25 18:33:59 +02:00
Dmitry Ivankov
9d1300c3e7 docs: java-db air-gap doc tweaks (#3561)
Downloaded file name is `javadb.tar.gz` rather than `db.tar.gz`.
Also `--skip-update` is deprecated in favor of `--skip-db-update` and `--skip-java-db-update`.
2023-02-24 17:54:29 +02:00
Teppei Fukuda
793cc43d4c feat(go): license support (#3683) 2023-02-24 17:52:35 +02:00
AndreyLevchenko
6a3294e476 feat(ruby): add dependency tree/location support for Gemfile.lock (#3669)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-23 23:47:28 +02:00
chenk
e9dc21d88a fix(k8s): k8s label size (#3678)
Signed-off-by: chenk <hen.keinan@gmail.com>
2023-02-23 15:09:27 +02:00
Masahiro331
12976d42df fix(cyclondx): fix array empty value, null to [] (#3676) 2023-02-23 13:35:59 +02:00
Teppei Fukuda
1dc2b349c6 refactor: rewrite gomod analyzer as post-analyzer (#3674) 2023-02-23 13:35:08 +02:00
chenk
92eaf636ca feat: config outdated-api result filtered by k8s version (#3578)
Signed-off-by: chenk <hen.keinan@gmail.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-22 15:41:37 +02:00
Alexander Lauster
9af436b999 fix: Update to Alpine 3.17.2 (#3655)
Fix CVE-2023-0286
2023-02-21 19:38:20 +02:00
Teppei Fukuda
88ee68d0c6 feat: add support for virtual files (#3654) 2023-02-20 17:20:57 +02:00
Teppei Fukuda
75c96bd968 feat: add post-analyzers (#3640)
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
2023-02-20 13:08:26 +02:00
Edoardo Vacchi
baea3997d2 chore(deps): updates wazero to 1.0.0-pre.9 (#3653)
Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
2023-02-20 13:03:28 +02:00
dependabot[bot]
7ca0db17ea chore(deps): bump github.com/go-openapi/runtime from 0.24.2 to 0.25.0 (#3528)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-20 13:01:33 +02:00
dependabot[bot]
866999e454 chore(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.18 (#3633)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-20 10:11:01 +02:00
DmitriyLewen
b7bfb9a207 feat(python): add dependency locations for Pipfile.lock (#3614) 2023-02-20 09:51:42 +02:00
dependabot[bot]
9badef27ac chore(deps): bump golang.org/x/net from 0.5.0 to 0.7.0 (#3648)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-19 15:51:12 +02:00
DmitriyLewen
d856595b8e fix(java): fix groupID selection by ArtifactID for jar files. (#3644) 2023-02-18 09:07:08 +02:00
dependabot[bot]
fe7c26a741 chore(deps): bump github.com/aws/aws-sdk-go-v2/service/ec2 from 1.63.1 to 1.85.0 (#3607)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-17 12:44:55 +02:00
Gio Rodriguez
f251dfc5ce fix(aws): Adding a fix for update-cache flag that is not applied on AWS scans. (#3619)
* adding a fix for update-cache that was not applied on AWS scans.

* removing unneeded code

---------

Co-authored-by: Gio Rodriguez <giovanni.rodriguez@aquasec.com>
2023-02-16 22:49:20 +02:00
didiermichel
9be8062c10 feat(cli): add command completion (#3061)
Co-authored-by: congbang-le <lecongbang314@gmail.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-15 13:29:03 +02:00
Duy Nguyen
370098dbf4 docs(misconf): update dockerfile link (#3627) 2023-02-15 11:54:56 +02:00
Jack Lin
32acd293fd feat(flag): add exit-on-eosl option (#3423)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-15 10:51:15 +02:00
dependabot[bot]
aa8e185e03 chore(deps): bump github.com/go-git/go-git/v5 from 5.4.2 to 5.5.2 (#3533)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-14 16:17:28 +02:00
Alexej Disterhoft
86603bb9c5 fix(cli): make java db repository configurable (#3595)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-14 15:01:15 +02:00
chenk
7b1e173f51 chore: bump trivy-kubernetes (#3613)
Signed-off-by: chenk <hen.keinan@gmail.com>
2023-02-14 13:23:45 +02:00
Helge Eichelberg
85d5d61bc7 chore(helm): update Trivy from v0.36.1 to v0.37.2 (#3574)
* chore(helm): update Trivy from v0.36.1 to v0.37.1

Signed-off-by: elchenberg <elchenberg@users.noreply.github.com>

* chore(helm): bump Trivy to v0.37.2

Signed-off-by: elchenberg <elchenberg@users.noreply.github.com>

---------

Signed-off-by: elchenberg <elchenberg@users.noreply.github.com>
2023-02-14 13:10:07 +02:00
dependabot[bot]
2c17260ba8 chore(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#3536)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-14 13:09:43 +02:00
Teppei Fukuda
c54f1aa8f0 chore(deps): bump golang/x/mod to v0.8.0 (#3606) 2023-02-14 07:02:26 +02:00
dependabot[bot]
625ea58122 chore(deps): bump golang.org/x/crypto from 0.3.0 to 0.5.0 (#3529)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-13 16:15:12 +02:00
dependabot[bot]
623c7f9432 chore(deps): bump helm.sh/helm/v3 from 3.10.3 to 3.11.1 (#3580)
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-13 16:14:25 +02:00
DmitriyLewen
d291c34f51 ci: quote pros in c++ for semantic pr (#3605) 2023-02-13 14:05:35 +02:00
DmitriyLewen
6cac6c917f fix(image): check proxy settings from env for remote images (#3604) 2023-02-13 12:54:38 +02:00
DmitriyLewen
12b563b974 BREAKING: use normalized trivy-java-db (#3583)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-10 02:16:37 +02:00
DmitriyLewen
72a14c67af fix(image): add timeout for remote images (#3582)
* add timeout for remote image

* fix linter error
2023-02-09 14:19:17 +02:00
dependabot[bot]
4c01d73fb7 chore(deps): bump golang.org/x/mod from 0.6.0 to 0.7.0 (#3532)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-02-07 08:38:25 +02:00
dependabot[bot]
10dd5d1a95 chore(deps): bump golang.org/x/text from 0.5.0 to 0.6.0 (#3534)
Co-authored-by: Teppei Fukuda <knqyf263@gmail.com>
2023-02-07 06:51:49 +02:00
simar7
439c541fd3 fix(misconf): handle dot files better (#3550) 2023-02-05 09:10:10 +09:00
Teppei Fukuda
200e04a767 chore: bump Go to 1.19 (#3551) 2023-02-03 15:08:01 +09:00
dependabot[bot]
a533ca87e6 chore(deps): bump alpine from 3.17.0 to 3.17.1 (#3522) 2023-02-03 04:21:25 +02:00
dependabot[bot]
4bccbe6e1c chore(deps): bump docker/build-push-action from 3 to 4 (#3523) 2023-02-03 04:20:52 +02:00
dependabot[bot]
d0562085df chore(deps): bump actions/cache from 3.2.2 to 3.2.4 (#3524) 2023-02-03 04:20:15 +02:00
dependabot[bot]
f5e65749b4 chore(deps): bump golangci/golangci-lint-action from 3.3.0 to 3.4.0 (#3525) 2023-02-03 04:17:39 +02:00
dependabot[bot]
d3da459d45 chore(deps): bump aquaproj/aqua-installer from 1.2.0 to 2.0.2 (#3526) 2023-02-03 04:15:56 +02:00
157 changed files with 8740 additions and 2645 deletions

View File

@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Restore Trivy binaries from cache
uses: actions/cache@v3.2.2
uses: actions/cache@v3.2.4
with:
path: dist/
key: ${{ runner.os }}-bins-${{github.workflow}}-${{github.sha}}

View File

@@ -24,7 +24,7 @@ jobs:
fetch-depth: 0
- name: Restore Trivy binaries from cache
uses: actions/cache@v3.2.2
uses: actions/cache@v3.2.4
with:
path: dist/
key: ${{ runner.os }}-bins-${{github.workflow}}-${{github.sha}}

View File

@@ -87,7 +87,7 @@ jobs:
## only for canary build
- name: Build and push
if: ${{ inputs.goreleaser_config == 'goreleaser-canary.yml' }}
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
platforms: linux/amd64, linux/arm64
file: ./Dockerfile.canary # path to Dockerfile
@@ -99,7 +99,7 @@ jobs:
public.ecr.aws/aquasecurity/trivy:canary
- name: Cache Trivy binaries
uses: actions/cache@v3.2.2
uses: actions/cache@v3.2.4
with:
path: dist/
# use 'github.sha' to create a unique cache folder for each run.

View File

@@ -69,7 +69,7 @@ jobs:
java
go
c
c++
c\+\+
elixir
dart

View File

@@ -21,14 +21,13 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
go-version: [stable, oldstable]
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
go-version: oldstable
- name: go mod tidy
run: |
@@ -40,7 +39,7 @@ jobs:
if: matrix.operating-system == 'ubuntu-latest'
- name: Lint
uses: golangci/golangci-lint-action@v3.3.0
uses: golangci/golangci-lint-action@v3.4.0
with:
version: v1.49
args: --deadline=30m
@@ -48,7 +47,7 @@ jobs:
if: matrix.operating-system == 'ubuntu-latest'
# Install tools
- uses: aquaproj/aqua-installer@v1.2.0
- uses: aquaproj/aqua-installer@v2.0.2
with:
aqua_version: v1.25.0
@@ -83,7 +82,7 @@ jobs:
go-version-file: go.mod
# Install tools
- uses: aquaproj/aqua-installer@v1.1.2
- uses: aquaproj/aqua-installer@v2.0.2
with:
aqua_version: v1.25.0

View File

@@ -1,4 +1,4 @@
FROM alpine:3.17.0
FROM alpine:3.17.2
RUN apk --no-cache add ca-certificates git
COPY trivy /usr/local/bin/trivy
COPY contrib/*.tpl contrib/

View File

@@ -1,4 +1,4 @@
FROM alpine:3.17.0
FROM alpine:3.17.1
RUN apk --no-cache add ca-certificates git
# binaries were created with GoReleaser

View File

@@ -9,6 +9,8 @@ import (
"github.com/aquasecurity/trivy/pkg/commands"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/plugin"
_ "modernc.org/sqlite" // sqlite driver for RPM DB and Java DB
)
var (

View File

@@ -52,7 +52,7 @@ Java users also need to download the Java index database for use in air-gapped e
=== "oras >= v0.13.0"
Please follow [oras installation instruction][oras].
Download `db.tar.gz`:
Download `javadb.tar.gz`:
```
$ oras pull ghcr.io/aquasecurity/trivy-java-db:1
@@ -61,7 +61,7 @@ Java users also need to download the Java index database for use in air-gapped e
=== "oras < v0.13.0"
Please follow [oras installation instruction][oras].
Download `db.tar.gz`:
Download `javadb.tar.gz`:
```
$ oras pull -a ghcr.io/aquasecurity/trivy-java-db:1
@@ -122,7 +122,7 @@ In an air-gapped environment, you have to specify `--skip-db-update` and `--skip
In addition, if you want to scan `pom.xml` dependencies, you need to specify `--offline-scan` since Trivy tries to issue API requests for scanning Java applications by default.
```
$ trivy image --skip-update --skip-java-db-update --offline-scan alpine:3.12
$ trivy image --skip-db-update --skip-java-db-update --offline-scan alpine:3.12
```
## Air-Gapped Environment for misconfigurations
@@ -139,4 +139,4 @@ $ trivy conf --skip-policy-update /path/to/conf
[allowlist]: ../references/troubleshooting.md
[oras]: https://oras.land/cli/
[^1]: This is only required to scan `jar` files. More information about `Java index db` [here](../vulnerability/languages/java.md)
[^1]: This is only required to scan `jar` files. More information about `Java index db` [here](../vulnerability/languages/java.md)

View File

@@ -40,7 +40,7 @@ A single package must contain only one policy.
# title: Deployment not allowed
# description: Deployments are not allowed because of some reasons.
# schemas:
# - input: schema.input
# - input: schema["kubernetes"]
# custom:
# id: ID001
# severity: LOW
@@ -124,16 +124,16 @@ All fields are optional. The `schemas` field should be used to enable policy val
schema that will be used is based on the input document type. It is recommended to use this to ensure your policies are
correct and do not reference incorrect properties/values.
| Field name | Allowed values | Default value | In table | In JSON |
|----------------------------|------------------------------------------|:----------------------------:|:----------------:|:----------------:|
| title | Any characters | N/A | :material-check: | :material-check: |
| description | Any characters | | :material-close: | :material-check: |
| schemas.input | `schema.input` | (applied to all input types) | :material-close: | :material-close: |
| custom.id | Any characters | N/A | :material-check: | :material-check: |
| custom.severity | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | UNKNOWN | :material-check: | :material-check: |
| custom.recommended_actions | Any characters | | :material-close: | :material-check: |
| custom.input.selector.type | Any item(s) in [this list][source-types] | | :material-close: | :material-check: |
| url | Any characters | | :material-close: | :material-check: |
| Field name | Allowed values | Default value | In table | In JSON |
|----------------------------|-------------------------------------------------------------------|:----------------------------:|:----------------:|:----------------:|
| title | Any characters | N/A | :material-check: | :material-check: |
| description | Any characters | | :material-close: | :material-check: |
| schemas.input | `schema["kubernetes"]`, `schema["dockerfile"]`, `schema["cloud"]` | (applied to all input types) | :material-close: | :material-close: |
| custom.id | Any characters | N/A | :material-check: | :material-check: |
| custom.severity | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | UNKNOWN | :material-check: | :material-check: |
| custom.recommended_actions | Any characters | | :material-close: | :material-check: |
| custom.input.selector.type | Any item(s) in [this list][source-types] | | :material-close: | :material-check: |
| url | Any characters | | :material-close: | :material-check: |
Some fields are displayed in scan results.
@@ -196,13 +196,7 @@ You can specify input format via the `custom.input` annotation.
`type` accepts `kubernetes`, `dockerfile`, `cloudformation`, `terraform`, `terraformplan`, `json`, or `yaml`.
### Schemas
You can explore the format of input documents by browsing the schema for the relevant input type:
- [Cloud](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/cloud.json)
- [Dockerfile](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/dockerfile.json)
- [Kubernetes](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/kubernetes.json)
- [RBAC](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/rbac.json)
See [here](./schema.md) for the detail.
[rego]: https://www.openpolicyagent.org/docs/latest/policy-language/
[package]: https://www.openpolicyagent.org/docs/latest/policy-language/#packages

View File

@@ -0,0 +1,93 @@
# Input Schema
## Overview
Policies can be defined with custom schemas that allow inputs to be verified against them. Adding a policy schema
enables Trivy to show more detailed error messages when an invalid input is encountered.
In Trivy we have been able to define a schema for a [Dockerfile](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/dockerfile.json).
Without input schemas, a policy would be as follows:
!!! example
```
# METADATA
package mypackage
deny {
input.evil == "foo bar"
}
```
If this policy is run against offending Dockerfile(s), there will not be any issues as the policy will fail to evaluate.
Although the policy's failure to evaluate is legitimate, this should not result in a positive result for the scan.
For instance if we have a policy that checks for misconfigurations in a `Dockerfile`, we could define the
schema as such
!!! example
```
# METADATA
# schemas:
# - input: schema["dockerfile"]
package mypackage
deny {
input.evil == "foo bar"
}
```
Here `input: schema["dockerfile"]` points to a schema that expects a valid `Dockerfile` as input. An example of this
can be found [here](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/dockerfile.json)
Now if this policy is evaluated against, a more descriptive error will be available to help fix the problem.
```bash
1 error occurred: testpolicy.rego:8: rego_type_error: undefined ref: input.evil
input.evil
^
have: "evil"
want (one of): ["Stages"]
```
Currently, out of the box the following schemas are supported natively:
1. [Docker](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/dockerfile.json)
2. [Kubernetes](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/kubernetes.json)
3. [Cloud](https://github.com/aquasecurity/defsec/blob/master/pkg/rego/schemas/cloud.json)
## Custom Policies with Custom Schemas
You can also bring a custom policy that defines one or more custom schema.
!!! example
```
# METADATA
# schemas:
# - input: schema["fooschema"]
# - input: schema["barschema"]
package mypackage
deny {
input.evil == "foo bar"
}
```
The policies can be placed in a structure as follows
!!! example
```
/Users/user/my-custom-policies
├── my_policy.rego
└── schemas
└── fooschema.json
└── barschema.json
```
To use such a policy with Trivy, use the `--config-policy` flag that points to the directory where the schemas and policies
are contained.
```bash
$ trivy --config-policy=/Users/user/my-custom-policies <path/to/iac>
```
For more details on how to define schemas within Rego policies, please see the [OPA guide](https://www.openpolicyagent.org/docs/latest/schemas/#schema-annotations) that describes it in more detail.

View File

@@ -19,8 +19,6 @@ For suggestions or issues regarding policy content, please open an issue under t
Helm Chart scanning will resolve the chart to Kubernetes manifests then run the [kubernetes][kubernetes] checks.
Ansible scanning is coming soon.
## Policy Distribution
defsec 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.
@@ -34,5 +32,5 @@ Trivy checks for updates to OPA bundle on GHCR every 24 hours and pulls it if th
[defsec]: https://github.com/aquasecurity/defsec
[kubernetes]: https://github.com/aquasecurity/defsec/tree/master/internal/rules/kubernetes
[kubernetes]: https://github.com/aquasecurity/defsec/tree/master/internal/rules/rbac
[docker]: https://github.com/aquasecurity/defsec/tree/master/internal/rules/docker
[docker]: https://github.com/aquasecurity/defsec/tree/master/internal/rules/policies/docker
[ghcr]: https://github.com/aquasecurity/defsec/pkgs/container/defsec

View File

@@ -16,6 +16,7 @@ Scan Flags
Report Flags
--dependency-tree show dependency origin tree (EXPERIMENTAL)
--exit-code int specify exit code when any security issues are found
--exit-on-eol int exit with the specified code when the os of image ends of service/life
-f, --format string format (table, json, sarif, template, cyclonedx, spdx, spdx-json, github, cosign-vuln) (default "table")
--ignore-policy string specify the Rego file path to evaluate each vulnerability
--ignorefile string specify .trivyignore file (default ".trivyignore")
@@ -37,6 +38,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -0,0 +1,28 @@
# Completion
```bash
Generate the autocompletion script for trivy for the specified shell.
See each sub-command's help for details on how to use the generated script.
Usage:
trivy completion [command]
Available Commands:
bash Generate the autocompletion script for bash
fish Generate the autocompletion script for fish
powershell Generate the autocompletion script for powershell
zsh Generate the autocompletion script for zsh
Flags:
-h, --help help for completion
Global Flags:
--cache-dir string cache directory (default "/Users/didier/Library/Caches/trivy")
-c, --config string config path (default "trivy.yaml")
-d, --debug debug mode
--generate-default-config write the default config to trivy-default.yaml
--insecure allow insecure server connections when using TLS
-q, --quiet suppress progress bar and log output
--timeout duration timeout (default 5m0s)
-v, --version show version
```

View File

@@ -45,6 +45,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -39,6 +39,7 @@ Scan Flags
Report Flags
--exit-code int specify exit code when any security issues are found
--exit-on-eol int exit with the specified code when the os of image ends of service/life
-f, --format string format (table, json, sarif, template, cyclonedx, spdx, spdx-json, github, cosign-vuln) (default "table")
--ignore-policy string specify the Rego file path to evaluate each vulnerability
--ignorefile string specify .trivyignore file (default ".trivyignore")
@@ -59,6 +60,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -21,6 +21,7 @@ Examples:
$ trivy server
Available Commands:
completion Generate the autocompletion script for the specified shell
config Scan config files for misconfigurations
filesystem Scan local filesystem
help Help about any command

View File

@@ -42,6 +42,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -48,6 +48,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -45,6 +45,7 @@ DB Flags
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update java indexes database but don't run a scan
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db")
--no-progress suppress progress bar
--reset remove all caches and database
--skip-db-update skip updating vulnerability database

View File

@@ -41,7 +41,7 @@ report: all
# Same as '--template'
# Default is empty
template:
template:
# Same as '--dependency-tree'
# Default is false
@@ -63,6 +63,10 @@ ignore-policy:
# Default is 0
exit-code: 0
# Same as '--exit-on-eol'
# Default is 0
exit-on-eol: 0
# Same as '--output'
# Default is empty (stdout)
output:
@@ -92,16 +96,16 @@ scan:
skip-dirs:
- usr/local/
- etc/
# Same as '--skip-files'
# Default is empty
skip-files:
- package-dev.json
# Same as '--offline-scan'
# Default is false
offline-scan: false
# Same as '--scanners'
# Default depends on subcommand
scanners:
@@ -115,23 +119,23 @@ scan:
```yaml
cache:
# Same as '--cache-backend'
# Default is 'fs'
# Default is 'fs'
backend: 'fs'
# Same as '--cache-ttl'
# Default is 0 (no ttl)
# Default is 0 (no ttl)
ttl: 0
# Redis options
redis:
# Same as '--redis-ca'
# Default is empty
ca:
# Same as '--redis-cert'
# Default is empty
cert:
# Same as '--redis-key'
# Default is empty
key:
@@ -144,14 +148,18 @@ db:
# Same as '--skip-db-update'
# Default is false
skip-update: false
# Same as '--no-progress'
# Default is false
no-progress: false
# Same as '--db-repository'
# Default is 'github.com/aquasecurity-trivy-repo'
repository: github.com/aquasecurity-trivy-repo
# Default is 'ghcr.io/aquasecurity/trivy-db'
repository: ghcr.io/aquasecurity/trivy-db
# Same as '--java-db-repository'
# Default is 'ghcr.io/aquasecurity/trivy-java-db'
java-repository: ghcr.io/aquasecurity/trivy-java-db
```
## Image Options
@@ -162,7 +170,7 @@ image:
# Same as '--input' (available with 'trivy image')
# Default is empty
input:
# Same as '--removed-pkgs'
# Default is false
removed-pkgs: false
@@ -178,7 +186,7 @@ vulnerability:
type:
- os
- library
# Same as '--ignore-unfixed'
# Default is false
ignore-unfixed: false
@@ -265,25 +273,25 @@ kubernetes:
# Same as '--context'
# Default is empty
context:
# Same as '--namespace'
# Default is empty
namespace:
```
## Repository Options
Available with git repository scanning (`trivy repo`)
Available with git repository scanning (`trivy repo`)
```yaml
repository:
# Same as '--branch'
# Default is empty
branch:
# Same as '--commit'
# Default is empty
commit:
# Same as '--tag'
# Default is empty
tag:
@@ -297,21 +305,21 @@ server:
# Same as '--server' (available in client mode)
# Default is empty
addr: http://localhost:4954
# Same as '--token'
# Default is empty
token: "something-secret"
# Same as '--token-header'
# Default is 'Trivy-Token'
token-header: 'My-Token-Header'
# Same as '--custom-headers'
# Default is empty
custom-headers:
- scanner: trivy
- x-api-token: xxx
# Same as '--listen' (available in server mode)
# Default is 'localhost:4954'
listen: 0.0.0.0:10000
@@ -325,18 +333,18 @@ Available for cloud scanning (currently only `trivy aws`)
cloud:
# whether to force a cache update for every scan
update-cache: false
# how old cached results can be before being invalidated
max-cache-age: 24h
# aws-specific cloud settings
aws:
# the aws region to use
region: us-east-1
# the aws endpoint to use (not required for general use)
endpoint: https://my.custom.aws.endpoint
# the aws account to use (this will be determined from your environment when not set)
account: 123456789012
```

View File

@@ -291,10 +291,12 @@ For an overview of Trivy's Compliance feature, including working with custom com
The following reports are available out of the box:
| Compliance | Name for command | More info
--- | --- | ---
NSA, CISA Kubernetes Hardening Guidance v1.2 | `k8s-nsa` | [Link](https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF)
CIS Benchmark for Kubernetes v1.23 | `k8s-cis` | [Link](https://www.cisecurity.org/benchmark/kubernetes)
| Compliance | Name for command | More info |
|----------------------------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------|
| NSA, CISA Kubernetes Hardening Guidance v1.2 | `k8s-nsa` | [Link](https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF) |
| CIS Benchmark for Kubernetes v1.23 | `k8s-cis` | [Link](https://www.cisecurity.org/benchmark/kubernetes) |
| Pod Security Standards, Baseline | `k8s-pss-baseline` | [Link](https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline) |
| Pod Security Standards, Restricted | `k8s-pss-restricted` | [Link](https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted) |
#### Examples

View File

@@ -6,7 +6,7 @@
|----------|--------------------------------------------------------------------------------------------|:---------:|:----------:|:---------------:|:---------------:|------------------|:------------------------:|
| Ruby | Gemfile.lock | - | - | ✅ | ✅ | included | - |
| | gemspec | ✅ | ✅ | - | - | included | - |
| Python | Pipfile.lock | - | - | ✅ | ✅ | excluded | - |
| Python | Pipfile.lock | - | - | ✅ | ✅ | excluded | |
| | poetry.lock | - | - | ✅ | ✅ | excluded | - |
| | requirements.txt | - | - | ✅ | ✅ | included | - |
| | egg package[^1] | ✅ | ✅ | - | - | excluded | - |

View File

@@ -43,3 +43,13 @@ $ trivy image --download-db-only
```
$ trivy image --db-repository registry.gitlab.com/gitlab-org/security-products/dependencies/trivy-db
```
## Java Vulnerability DB
The same options are also available for the Java index DB, which is used for scanning Java applications.
Skipping an update can be done by using the `--skip-java-db-update` option, while `--download-java-db-only` can be used to only download the Java index DB.
Downloading the Java index DB from an external OCI registry can be done by using the `--java-db-repository` option.
```
$ trivy image --java-db-repository registry.gitlab.com/gitlab-org/security-products/dependencies/trivy-java-db --download-java-db-only
```

View File

@@ -68,6 +68,57 @@ $ trivy image --exit-code 0 --severity MEDIUM,HIGH ruby:2.4.0
$ trivy image --exit-code 1 --severity CRITICAL ruby:2.4.0
```
## Exit on EOL
Sometimes you may surprisingly get 0 vulnerabilities in an old image:
- Enabling `--ignore-unfixed` option while all packages have no fixed versions.
- Scanning a rather outdated OS (e.g. Ubuntu 10.04).
An OS at the end of service/life (EOL) usually gets into this situation, which is definitely full of vulnerabilities.
`--exit-on-eol` can fail scanning on EOL OS with a non-zero code.
This flag is available with the following targets.
- Container images (`trivy image`)
- Virtual machine images (`trivy vm`)
- SBOM (`trivy sbom`)
- Root filesystem (`trivy rootfs`)
```
$ trivy image --exit-on-eol 1 alpine:3.10
```
<details>
<summary>Result</summary>
```
2023-03-01T11:07:15.455+0200 INFO Vulnerability scanning is enabled
...
2023-03-01T11:07:17.938+0200 WARN This OS version is no longer supported by the distribution: alpine 3.10.9
2023-03-01T11:07:17.938+0200 WARN The vulnerability detection may be insufficient because security updates are not provided
alpine:3.10 (alpine 3.10.9)
===========================
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1)
┌───────────┬────────────────┬──────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
├───────────┼────────────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│ apk-tools │ CVE-2021-36159 │ CRITICAL │ 2.10.6-r0 │ 2.10.7-r0 │ libfetch before 2021-07-26, as used in apk-tools, xbps, and │
│ │ │ │ │ │ other products, mishandles... │
│ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2021-36159 │
└───────────┴────────────────┴──────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────────┘
2023-03-01T11:07:17.941+0200 ERROR Detected EOL OS: alpine 3.10.9
```
</details>
This option is useful for CI/CD.
The following example will fail when a critical vulnerability is found or the OS is EOSL:
```
$ trivy image --exit-code 1 --exit-on-eol 1 --severity CRITICAL alpine:3.16.3
```
## Reset
The `--reset` option removes all caches and database.
After this, it takes a long time as the vulnerability database needs to be rebuilt locally.

View File

@@ -19,10 +19,23 @@ This flag is only available with the `--format table` flag.
The following packages/languages are currently supported:
- OS packages (apk, dpkg and rpm)
- Node.js (package-lock.json and yarn.lock)
- Nuget lock files (packages.lock.json)
- Rust Binaries built with [cargo-auditable][cargo-auditable]
- OS packages
- apk
- dpkg
- rpm
- Node.js
- npm: package-lock.json
- yarn: yarn.lock
- .NET
- NuGet: packages.lock.json
- Python
- Poetry: poetry.lock
- Ruby
- Bundler: Gemfile.lock
- Rust
- Binaries built with [cargo-auditable][cargo-auditable]
- Go
- Modules: go.mod
This tree is the reverse of the npm list command.
However, if you want to resolve a vulnerability in a particular indirect dependency, the reversed tree is useful to know where that dependency comes from and identify which package you actually need to update.
@@ -47,8 +60,8 @@ Total: 2 (HIGH: 1, CRITICAL: 1)
│ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-28469 │
└──────────────────┴────────────────┴──────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘
Dependency Origin Tree
======================
Dependency Origin Tree (Reversed)
=================================
package-lock.json
├── follow-redirects@1.14.6, (HIGH: 1, CRITICAL: 0)
│ └── axios@0.21.4

View File

@@ -4,10 +4,10 @@
Trivy supports two types of Go scanning, Go Modules and binaries built by Go.
The following table provides an outline of the features Trivy offers.
| Artifact | Offline[^1] | Dev dependencies |
|----------|:-----------:|:-----------------|
| Modules | ✓ | Include |
| Binaries | ✓ | Exclude |
| Artifact | Offline[^1] | Dev dependencies | License | Dependency graph |
|----------|:-----------:|:-----------------|:-------:|:----------------:|
| Modules | ✓ | Include | ✓[^2] | ✓[^2] |
| Binaries | ✓ | Exclude | - | - |
!!! note
Trivy scans only dependencies of the Go project.
@@ -17,10 +17,10 @@ The following table provides an outline of the features Trivy offers.
### Go Modules
Depending on Go versions, the required files are different.
| Version | Required files | Offline | License |
|---------|:--------------:|:-------:|:-------:|
| \>=1.17 | go.mod | ✓ | - |
| <1.17 | go.mod, go.sum | ✓ | - |
| Version | Required files | Offline |
|---------|:--------------:|:-------:|
| \>=1.17 | go.mod | ✓ |
| <1.17 | go.mod, go.sum | ✓ |
In Go 1.17+ projects, Trivy uses `go.mod` for direct/indirect dependencies.
On the other hand, it uses `go.mod` for direct dependencies and `go.sum` for indirect dependencies in Go 1.16 or less.
@@ -49,6 +49,10 @@ If you want to have better detection, please consider updating the Go version in
$ go mod tidy -go=1.18
```
To identify licenses and dependency relationships, you need to download modules to local cache beforehand,
such as `go mod download`, `go mod tidy`, etc.
Trivy traverses `$GOPATH/pkg/mod` and collect those extra information.
### Go binaries
Trivy scans binaries built by Go.
If there is a Go binary in your container image, Trivy automatically finds and scans it.
@@ -59,4 +63,5 @@ Also, you can scan your local binaries.
$ trivy fs ./your_binary
```
[^1]: It doesn't require the Internet access.
[^1]: It doesn't require the Internet access.
[^2]: Need to download modules to local cache beforehand

View File

@@ -0,0 +1,55 @@
# Python
Trivy supports three types of Python package managers: `pip`, `Pipenv` and `Poetry`.
The following table provides an outline of the features Trivy offers.
| Package Manager | File | Transitive dependencies | Dev dependencies | Dependency graph | Position | License |
|-----------------|------------------|:-----------------------:|:----------------:|:----------------:|:--------:|:-------:|
| pip | requirements.txt | - | Include | - | - | - |
| Pipenv | Pipfile.lock | ✅ | Include | - | ✅ | - |
| Poetry | poetry.lock | ✅ | Exclude | ✅ | | - |
In addition, Trivy supports two formats of Python packages: `egg` and `wheel`.
| Packaging | License |
|-----------|:-------:|
| Egg | ✅ |
| Wheel | ✅ |
These may be enabled or disabled depending on the target.
See [here](../detection/language.md) for the detail.
## Package managers
Trivy parses your files generated by package managers in filesystem/repository scanning.
### pip
`requirements.txt` files contain only the direct dependencies and not contain the transitive dependencies.
Therefore, Trivy scans only for the direct dependencies with `requirements.txt`.
Also, `requirements.txt` files don't contain information about dependencies used for development.
Trivy could detect vulnerabilities on the development packages, which not affect your production environment.
License detection is not supported for `pip`.
### Pipenv
Trivy parses `Pipfile.lock`.
`Pipfile.lock` files don't contain information about dependencies used for development.
Trivy could detect vulnerabilities on the development packages, which not affect your production environment.
License detection is not supported for `Pipenv`.
### Poetry
Trivy uses `poetry.lock` to identify dependencies and find vulnerabilities.
To build the correct dependency graph, `pyproject.toml` also needs to be present next to `poetry.lock`.
License detection is not supported for `Poetry`.
## Packaging
Trivy parses the manifest files of installed packages in container image scanning and so on.
See [here](https://packaging.python.org/en/latest/discussions/wheel-vs-egg/) for the detail.
### Egg
Trivy looks for `*.egg-info`, `*.egg-info/PKG-INFO`, `*.egg` and `EGG-INFO/PKG-INFO` to identify Python packages.
### Wheel
Trivy looks for `.dist-info/META-DATA` to identify Python packages.

View File

@@ -0,0 +1,66 @@
# Enable shell completion
Below is example steps to enable shell completion feature for `trivy` cli:
### 1. Know your current shell
```bash
$ echo $SHELL
/bin/zsh # For this example it is zsh, but will be vary depend on your $SHELL, maybe /bin/bash or /bin/fish
```
### 2. Run `completion` command to get sub-commands
``` bash
$ trivy completion zsh -h
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need
to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions in your current shell session:
source <(trivy completion zsh); compdef _trivy trivy
To load completions for every new session, execute once:
#### Linux:
trivy completion zsh > "${fpath[1]}/_trivy"
#### macOS:
trivy completion zsh > $(brew --prefix)/share/zsh/site-functions/_trivy
You will need to start a new shell for this setup to take effect.
```
### 3. Run the sub-commands following the instruction
```bash
echo "autoload -U compinit; compinit" >> ~/.zshrc
source <(trivy completion zsh); compdef _trivy trivy
trivy completion zsh > "${fpath[1]}/_trivy"
```
### 4. Start a new shell and you can see the shell completion
```bash
$ trivy [tab]
aws -- scan aws account
completion -- Generate the autocompletion script for the specified shell
config -- Scan config files for misconfigurations
filesystem -- Scan local filesystem
help -- Help about any command
image -- Scan a container image
kubernetes -- scan kubernetes cluster
module -- Manage modules
plugin -- Manage plugins
repository -- Scan a remote repository
rootfs -- Scan rootfs
sbom -- Scan SBOM for vulnerabilities
server -- Server mode
version -- Print the version
```

159
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/aquasecurity/trivy
go 1.18
go 1.19
require (
github.com/CycloneDX/cyclonedx-go v0.7.0
@@ -8,8 +8,8 @@ require (
github.com/NYTimes/gziphandler v1.1.1
github.com/alicebob/miniredis/v2 v2.23.0
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/defsec v0.82.9
github.com/aquasecurity/go-dep-parser v0.0.0-20230130190635-5e31092b0621
github.com/aquasecurity/defsec v0.84.0
github.com/aquasecurity/go-dep-parser v0.0.0-20230228091112-63a15cdc6bc3
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
@@ -20,21 +20,21 @@ require (
github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da
github.com/aquasecurity/tml v0.6.1
github.com/aquasecurity/trivy-db v0.0.0-20230116084806-4bcdf1c414d0
github.com/aquasecurity/trivy-java-db v0.0.0-20230201134457-514a75965135
github.com/aquasecurity/trivy-kubernetes v0.3.1-0.20230124152305-a266786d8ded
github.com/aws/aws-sdk-go v1.44.171
github.com/aws/aws-sdk-go-v2 v1.17.3
github.com/aws/aws-sdk-go-v2/config v1.18.3
github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728
github.com/aquasecurity/trivy-kubernetes v0.3.1-0.20230223104408-c772810be7c3
github.com/aws/aws-sdk-go v1.44.210
github.com/aws/aws-sdk-go-v2 v1.17.5
github.com/aws/aws-sdk-go-v2/config v1.18.15
github.com/aws/aws-sdk-go-v2/service/ec2 v1.86.1
github.com/aws/aws-sdk-go-v2/service/sts v1.18.5
github.com/caarlos0/env/v6 v6.10.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cheggaaa/pb/v3 v3.1.0
github.com/containerd/containerd v1.6.14
github.com/containerd/containerd v1.6.18
github.com/docker/docker v23.0.0-rc.1+incompatible
github.com/docker/go-connections v0.4.0
github.com/fatih/color v1.13.0
github.com/go-openapi/runtime v0.24.2
github.com/go-openapi/runtime v0.25.0
github.com/go-openapi/strfmt v0.21.3
github.com/go-redis/redis/v8 v8.11.5
github.com/golang-jwt/jwt v3.2.2+incompatible
@@ -43,11 +43,11 @@ require (
github.com/google/licenseclassifier/v2 v2.0.0
github.com/google/uuid v1.3.0
github.com/google/wire v0.5.0
github.com/hashicorp/go-getter v1.6.2
github.com/hashicorp/go-getter v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.1
github.com/in-toto/in-toto-golang v0.5.0
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-deb-version v0.0.0-20230223133812-3ed183d23422
github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075
github.com/kylelemons/godebug v1.1.0
github.com/mailru/easyjson v0.7.7
@@ -61,16 +61,16 @@ require (
github.com/open-policy-agent/opa v0.44.1-0.20220927105354-00e835a7cc15
github.com/owenrumney/go-sarif/v2 v2.1.2
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170
github.com/samber/lo v1.36.0
github.com/samber/lo v1.37.0
github.com/secure-systems-lab/go-securesystemslib v0.4.0
github.com/sigstore/rekor v1.0.1
github.com/sosedoff/gitkit v0.3.0
github.com/spf13/cobra v1.6.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
github.com/testcontainers/testcontainers-go v0.17.0
github.com/tetratelabs/wazero v1.0.0-pre.8
github.com/tetratelabs/wazero v1.0.0-pre.9
github.com/twitchtv/twirp v8.1.2+incompatible
github.com/xlab/treeprint v1.1.0
go.etcd.io/bbolt v1.3.6
@@ -80,27 +80,24 @@ require (
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
k8s.io/utils v0.0.0-20230115233650-391b47cb4029
modernc.org/sqlite v1.20.3
)
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/glebarez/go-sqlite v1.20.3 // indirect
github.com/glebarez/sqlite v1.6.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
gorm.io/gorm v1.24.3 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
)
require (
cloud.google.com/go v0.104.0 // indirect
cloud.google.com/go/compute v1.12.1 // indirect
cloud.google.com/go/compute/metadata v0.2.1 // indirect
cloud.google.com/go/iam v0.5.0 // indirect
cloud.google.com/go/storage v1.23.0 // indirect
cloud.google.com/go v0.105.0 // indirect
cloud.google.com/go/compute v1.14.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
cloud.google.com/go/storage v1.27.0 // indirect
github.com/Azure/azure-sdk-for-go v67.1.0+incompatible
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
@@ -121,7 +118,7 @@ require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.9.6 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
@@ -132,11 +129,11 @@ require (
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.15 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 // indirect
github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.16.0 // indirect
github.com/aws/aws-sdk-go-v2/service/apigateway v1.15.24 // indirect
@@ -162,9 +159,9 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.19 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/kafka v1.17.19 // indirect
github.com/aws/aws-sdk-go-v2/service/kafka v1.19.4 // indirect
github.com/aws/aws-sdk-go-v2/service/kinesis v1.15.19 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.18.15 // indirect
github.com/aws/aws-sdk-go-v2/service/lambda v1.24.6 // indirect
@@ -176,8 +173,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sns v1.18.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect
github.com/aws/aws-sdk-go-v2/service/workspaces v1.23.0 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -204,16 +201,17 @@ require (
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-git/v5 v5.4.2
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/go-git/go-git/v5 v5.5.2
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
@@ -236,9 +234,8 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.6.0 // indirect
github.com/googleapis/go-type-adapters v1.0.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
@@ -258,7 +255,8 @@ require (
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/knqyf263/go-rpmdb v0.0.0-20230201142403-697bc51b3948
github.com/knqyf263/nested v0.0.1
@@ -267,27 +265,30 @@ require (
github.com/liamg/iamgo v0.0.9 // indirect
github.com/liamg/jfather v0.0.7 // indirect
github.com/liamg/memoryfs v1.4.3 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/lib/pq v1.10.7 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 // 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-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/buildkit v0.11.0
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
@@ -301,9 +302,8 @@ require (
github.com/opencontainers/runtime-spec v1.0.3-0.20220311020903-6969a0a09ab1 // indirect
github.com/opencontainers/selinux v1.10.2 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/owenrumney/squealer v1.0.1-0.20220510063705-c0be93f0edea // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/owenrumney/squealer v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -314,22 +314,23 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rubenv/sql-migrate v1.1.2 // indirect
github.com/rubenv/sql-migrate v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spdx/tools-golang v0.3.1-0.20230104082527-d6f58551be3f
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@@ -337,26 +338,25 @@ require (
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
github.com/zclconf/go-cty-yaml v1.0.2 // indirect
go.etcd.io/etcd/api/v3 v3.5.5 // indirect
go.mongodb.org/mongo-driver v1.10.0 // indirect
go.opencensus.io v0.23.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.3.0
golang.org/x/mod v0.6.0
golang.org/x/net v0.4.0 // indirect
golang.org/x/crypto v0.5.0
golang.org/x/mod v0.8.0
golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.1.0 // indirect
golang.org/x/sync v0.1.0
golang.org/x/sys v0.4.0 // indirect
golang.org/x/term v0.3.0
golang.org/x/text v0.5.0
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0
golang.org/x/text v0.7.0
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/api v0.102.0 // indirect
google.golang.org/api v0.107.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect
google.golang.org/grpc v1.50.1 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.52.0 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
@@ -364,22 +364,27 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools v2.2.0+incompatible
helm.sh/helm/v3 v3.10.3 // indirect
helm.sh/helm/v3 v3.11.1 // indirect
k8s.io/api v0.26.1 // indirect
k8s.io/apiextensions-apiserver v0.25.2 // indirect
k8s.io/apiextensions-apiserver v0.26.0 // indirect
k8s.io/apimachinery v0.26.1 // indirect
k8s.io/apiserver v0.25.2 // indirect
k8s.io/apiserver v0.26.0 // indirect
k8s.io/cli-runtime v0.26.1 // indirect
k8s.io/client-go v0.26.1 // indirect
k8s.io/component-base v0.26.1 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/klog/v2 v2.90.0 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/kubectl v0.26.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.2 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.20.3 // indirect
oras.land/oras-go v1.2.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
oras.land/oras-go v1.2.2 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect

527
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
apiVersion: v2
name: trivy
version: 0.5.0
appVersion: 0.36.1
version: 0.7.0
appVersion: 0.37.2
description: Trivy helm chart
keywords:
- scanner

View File

@@ -68,7 +68,7 @@ The following table lists the configurable parameters of the Trivy chart and the
| `trivy.registryPassword` | The password used to log in at dockerhub. More info: https://aquasecurity.github.io/trivy/dev/advanced/private-registries/docker-hub/ | |
| `trivy.registryCredentialsExistingSecret` | Name of Secret containing dockerhub credentials. Alternative to the 2 parameters above, has precedence if set. | |
| `trivy.serviceAccount.annotations` | Additional annotations to add to the Kubernetes service account resource | |
| `trivy.skipUpdate` | The flag to enable or disable Trivy DB downloads from GitHub | `false` |
| `trivy.skipDBUpdate` | The flag to enable or disable Trivy DB downloads from GitHub | `false` |
| `trivy.dbRepository` | OCI repository to retrieve the trivy vulnerability database from | `ghcr.io/aquasecurity/trivy-db` |
| `trivy.cache.redis.enabled` | Enable Redis as caching backend | `false` |
| `trivy.cache.redis.url` | Specify redis connection url, e.g. redis://redis.redis.svc:6379 | `` |

View File

@@ -12,7 +12,7 @@ data:
TRIVY_CACHE_TTL: {{ .Values.trivy.cache.redis.ttl | quote }}
{{- end }}
TRIVY_DEBUG: {{ .Values.trivy.debugMode | quote }}
TRIVY_SKIP_UPDATE: {{ .Values.trivy.skipUpdate | quote }}
TRIVY_SKIP_DB_UPDATE: {{ .Values.trivy.skipDBUpdate | quote }}
TRIVY_DB_REPOSITORY: {{ .Values.trivy.dbRepository | quote }}
{{- if .Values.httpProxy }}
HTTP_PROXY: {{ .Values.httpProxy | quote }}

View File

@@ -93,12 +93,12 @@ trivy:
# NOTE: When this is set the previous parameters are ignored.
#
# registryCredentialsExistingSecret: name-of-existing-secret
# skipUpdate the flag to enable or disable Trivy DB downloads from GitHub
# skipDBUpdate the flag to enable or disable Trivy DB downloads from GitHub
#
# You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues.
# If the flag is enabled you have to manually download the `trivy.db` file and mount it in the
# `/home/scanner/.cache/trivy/db/trivy.db` path (see `cacheDir`).
skipUpdate: false
skipDBUpdate: false
# OCI repository to retrieve the trivy vulnerability database from
dbRepository: ghcr.io/aquasecurity/trivy-db
# Trivy supports filesystem and redis as caching backend

View File

@@ -99,6 +99,24 @@ func TestFilesystem(t *testing.T) {
},
golden: "testdata/pip.json.golden",
},
{
name: "pipenv",
args: args{
scanner: types.VulnerabilityScanner,
listAllPkgs: true,
input: "testdata/fixtures/fs/pipenv",
},
golden: "testdata/pipenv.json.golden",
},
{
name: "poetry",
args: args{
scanner: types.VulnerabilityScanner,
listAllPkgs: true,
input: "testdata/fixtures/fs/poetry",
},
golden: "testdata/poetry.json.golden",
},
{
name: "pom",
args: args{

View File

@@ -25,6 +25,8 @@ import (
"github.com/aquasecurity/trivy/pkg/commands"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/types"
_ "modernc.org/sqlite"
)
var update = flag.Bool("update", false, "update golden files")
@@ -58,6 +60,7 @@ func initDB(t *testing.T) string {
})
require.NoError(t, err)
dbtest.InitJavaDB(t, cacheDir)
return cacheDir
}

View File

@@ -1,17 +1,14 @@
//go:build module_integration
package integration
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/module"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/scanner/post"
)
func TestModule(t *testing.T) {
@@ -35,21 +32,23 @@ func TestModule(t *testing.T) {
// Set up testing DB
cacheDir := initDB(t)
// Set up module dir
moduleDir := filepath.Join(cacheDir, module.RelativeDir)
err := os.MkdirAll(moduleDir, 0700)
require.NoError(t, err)
// Set up Spring4Shell module
t.Setenv("XDG_DATA_HOME", cacheDir)
_, err = utils.CopyFile(filepath.Join("../", "examples", "module", "spring4shell", "spring4shell.wasm"),
filepath.Join(moduleDir, "spring4shell.wasm"))
require.NoError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
osArgs := []string{"--cache-dir", cacheDir, "image", "--ignore-unfixed", "--format", "json",
"--skip-update", "--offline-scan", "--input", tt.input}
osArgs := []string{
"--cache-dir",
cacheDir,
"image",
"--ignore-unfixed",
"--format",
"json",
"--skip-db-update",
"--offline-scan",
"--quiet",
"--module-dir",
filepath.Join("../", "examples", "module", "spring4shell"),
"--input",
tt.input,
}
// Set up the output file
outputFile := filepath.Join(t.TempDir(), "output.json")
@@ -57,11 +56,18 @@ func TestModule(t *testing.T) {
outputFile = tt.golden
}
osArgs = append(osArgs, []string{"--output", outputFile}...)
osArgs = append(osArgs, []string{
"--output",
outputFile,
}...)
// Run Trivy
err = execute(osArgs)
assert.NoError(t, err)
err := execute(osArgs)
require.NoError(t, err)
defer func() {
analyzer.DeregisterAnalyzer("spring4shell")
post.DeregisterPostScanner("spring4shell")
}()
// Compare want and got
compareReports(t, tt.golden, outputFile)

29
integration/testdata/fixtures/fs/pipenv/Pipfile.lock generated vendored Normal file
View File

@@ -0,0 +1,29 @@
{
"_meta": {
"hash": {
"sha256": "06bf5e1462f5cf5abd8c226d9db597827c8fde5c6bbb0e9c87c2977720130c56"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.10"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"werkzeug": {
"hashes": [
"sha256:1e0dedc2acb1f46827daa2e399c1485c8fa17c0d8e70b6b875b4e7f54bf408d2",
"sha256:b353856d37dec59d6511359f97f6a4b2468442e454bd1c98298ddce53cac1f04"
],
"index": "pypi",
"version": "==0.11.1"
}
},
"develop": {}
}

50
integration/testdata/fixtures/fs/poetry/poetry.lock generated vendored Normal file
View File

@@ -0,0 +1,50 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "werkzeug"
version = "0.14"
description = "The comprehensive WSGI web application library."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "Werkzeug-0.14-py2.py3-none-any.whl", hash = "sha256:322b15deb0e503c3e96c267b676d47ca069edccbf6338549bea7916583822a55"},
{file = "Werkzeug-0.14.tar.gz", hash = "sha256:4aea27a9513b056346e9c8b49107f4ee7927f7bcf0be63024ecee39d5b87e9ef"},
]
[package.extras]
dev = ["coverage", "pytest", "sphinx", "tox"]
termcolor = ["termcolor"]
watchdog = ["watchdog"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "7bf54e5dc4ab511438271b965af1def5798ef80c82c39a3cdfe9308fd7881ff1"

View File

@@ -0,0 +1,15 @@
[tool.poetry]
name = "test"
version = "0.1.0"
description = ""
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
werkzeug = "0.14"
click = "8.1.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@@ -22,6 +22,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "GMS-2022-20",
"PkgID": "github.com/docker/distribution@v2.7.1+incompatible",
"PkgName": "github.com/docker/distribution",
"InstalledVersion": "2.7.1+incompatible",
"FixedVersion": "v2.8.0",
@@ -43,6 +44,7 @@
},
{
"VulnerabilityID": "CVE-2022-23628",
"PkgID": "github.com/open-policy-agent/opa@v0.35.0",
"PkgName": "github.com/open-policy-agent/opa",
"InstalledVersion": "0.35.0",
"FixedVersion": "0.37.0",
@@ -81,6 +83,7 @@
},
{
"VulnerabilityID": "CVE-2021-38561",
"PkgID": "golang.org/x/text@v0.3.6",
"PkgName": "golang.org/x/text",
"InstalledVersion": "0.3.6",
"FixedVersion": "0.3.7",
@@ -108,6 +111,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "GMS-2022-20",
"PkgID": "github.com/docker/distribution@v2.7.1+incompatible",
"PkgName": "github.com/docker/distribution",
"InstalledVersion": "2.7.1+incompatible",
"FixedVersion": "v2.8.0",

View File

@@ -22,6 +22,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "GMS-2022-20",
"PkgID": "github.com/docker/distribution@v2.7.1+incompatible",
"PkgName": "github.com/docker/distribution",
"InstalledVersion": "2.7.1+incompatible",
"FixedVersion": "v2.8.0",
@@ -43,6 +44,7 @@
},
{
"VulnerabilityID": "CVE-2022-23628",
"PkgID": "github.com/open-policy-agent/opa@v0.35.0",
"PkgName": "github.com/open-policy-agent/opa",
"InstalledVersion": "0.35.0",
"FixedVersion": "0.37.0",
@@ -81,6 +83,7 @@
},
{
"VulnerabilityID": "CVE-2021-38561",
"PkgID": "golang.org/x/text@v0.3.6",
"PkgName": "golang.org/x/text",
"InstalledVersion": "0.3.6",
"FixedVersion": "0.3.7",
@@ -108,6 +111,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "GMS-2022-20",
"PkgID": "github.com/docker/distribution@v2.7.1+incompatible",
"PkgName": "github.com/docker/distribution",
"InstalledVersion": "2.7.1+incompatible",
"FixedVersion": "v2.8.0",
@@ -136,6 +140,7 @@
"Vulnerabilities": [
{
"VulnerabilityID": "GMS-2022-20",
"PkgID": "github.com/docker/distribution@v2.7.1+incompatible",
"PkgName": "github.com/docker/distribution",
"InstalledVersion": "2.7.1+incompatible",
"FixedVersion": "v2.8.0",

129
integration/testdata/pipenv.json.golden vendored Normal file
View File

@@ -0,0 +1,129 @@
{
"SchemaVersion": 2,
"ArtifactName": "testdata/fixtures/fs/pipenv",
"ArtifactType": "filesystem",
"Metadata": {
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": "Pipfile.lock",
"Class": "lang-pkgs",
"Type": "pipenv",
"Packages": [
{
"Name": "werkzeug",
"Version": "0.11.1",
"Layer": {},
"Locations": [
{
"StartLine": 19,
"EndLine": 26
}
]
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-14806",
"PkgName": "werkzeug",
"InstalledVersion": "0.11.1",
"FixedVersion": "0.15.3",
"Layer": {},
"SeveritySource": "ghsa",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-14806",
"DataSource": {
"ID": "ghsa",
"Name": "GitHub Security Advisory Pip",
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip"
},
"Title": "python-werkzeug: insufficient debugger PIN randomness vulnerability",
"Description": "Pallets Werkzeug before 0.15.3, when used with Docker, has insufficient debugger PIN randomness because Docker containers share the same machine id.",
"Severity": "HIGH",
"CweIDs": [
"CWE-331"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"V2Score": 5,
"V3Score": 7.5
},
"redhat": {
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"V3Score": 7.5
}
},
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00034.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00047.html",
"https://access.redhat.com/security/cve/CVE-2019-14806",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14806",
"https://github.com/advisories/GHSA-gq9m-qvpx-68hc",
"https://github.com/pallets/werkzeug/blob/7fef41b120327d3912fbe12fb64f1951496fcf3e/src/werkzeug/debug/__init__.py#L168",
"https://github.com/pallets/werkzeug/commit/00bc43b1672e662e5e3b8cecd79e67fc968fa246",
"https://nvd.nist.gov/vuln/detail/CVE-2019-14806",
"https://palletsprojects.com/blog/werkzeug-0-15-3-released/",
"https://ubuntu.com/security/notices/USN-4655-1"
],
"PublishedDate": "2019-08-09T15:15:00Z",
"LastModifiedDate": "2019-09-11T00:15:00Z"
},
{
"VulnerabilityID": "CVE-2020-28724",
"PkgName": "werkzeug",
"InstalledVersion": "0.11.1",
"FixedVersion": "0.11.6",
"Layer": {},
"SeveritySource": "ghsa",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-28724",
"DataSource": {
"ID": "ghsa",
"Name": "GitHub Security Advisory Pip",
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip"
},
"Title": "python-werkzeug: open redirect via double slash in the URL",
"Description": "Open redirect vulnerability in werkzeug before 0.11.6 via a double slash in the URL.",
"Severity": "MEDIUM",
"CweIDs": [
"CWE-601"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:M/Au:N/C:P/I:P/A:N",
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
"V2Score": 5.8,
"V3Score": 6.1
},
"redhat": {
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
"V3Score": 5.4
}
},
"References": [
"https://access.redhat.com/security/cve/CVE-2020-28724",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28724",
"https://github.com/advisories/GHSA-3p3h-qghp-hvh2",
"https://github.com/pallets/flask/issues/1639",
"https://github.com/pallets/werkzeug/issues/822",
"https://github.com/pallets/werkzeug/pull/890/files",
"https://nvd.nist.gov/vuln/detail/CVE-2020-28724",
"https://ubuntu.com/security/notices/USN-4655-1"
],
"PublishedDate": "2020-11-18T15:15:00Z",
"LastModifiedDate": "2020-12-01T16:05:00Z"
}
]
}
]
}

97
integration/testdata/poetry.json.golden vendored Normal file
View File

@@ -0,0 +1,97 @@
{
"SchemaVersion": 2,
"ArtifactName": "testdata/fixtures/fs/poetry",
"ArtifactType": "filesystem",
"Metadata": {
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": "poetry.lock",
"Class": "lang-pkgs",
"Type": "poetry",
"Packages": [
{
"ID": "click@8.1.3",
"Name": "click",
"Version": "8.1.3",
"DependsOn": [
"colorama@0.4.6"
],
"Layer": {}
},
{
"ID": "colorama@0.4.6",
"Name": "colorama",
"Version": "0.4.6",
"Indirect": true,
"Layer": {}
},
{
"ID": "werkzeug@0.14",
"Name": "werkzeug",
"Version": "0.14",
"Layer": {}
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-14806",
"PkgID": "werkzeug@0.14",
"PkgName": "werkzeug",
"InstalledVersion": "0.14",
"FixedVersion": "0.15.3",
"Layer": {},
"SeveritySource": "ghsa",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-14806",
"DataSource": {
"ID": "ghsa",
"Name": "GitHub Security Advisory Pip",
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip"
},
"Title": "python-werkzeug: insufficient debugger PIN randomness vulnerability",
"Description": "Pallets Werkzeug before 0.15.3, when used with Docker, has insufficient debugger PIN randomness because Docker containers share the same machine id.",
"Severity": "HIGH",
"CweIDs": [
"CWE-331"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"V2Score": 5,
"V3Score": 7.5
},
"redhat": {
"V3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"V3Score": 7.5
}
},
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00034.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00047.html",
"https://access.redhat.com/security/cve/CVE-2019-14806",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14806",
"https://github.com/advisories/GHSA-gq9m-qvpx-68hc",
"https://github.com/pallets/werkzeug/blob/7fef41b120327d3912fbe12fb64f1951496fcf3e/src/werkzeug/debug/__init__.py#L168",
"https://github.com/pallets/werkzeug/commit/00bc43b1672e662e5e3b8cecd79e67fc968fa246",
"https://nvd.nist.gov/vuln/detail/CVE-2019-14806",
"https://palletsprojects.com/blog/werkzeug-0-15-3-released/",
"https://ubuntu.com/security/notices/USN-4655-1"
],
"PublishedDate": "2019-08-09T15:15:00Z",
"LastModifiedDate": "2019-09-11T00:15:00Z"
}
]
}
]
}

View File

@@ -22,13 +22,15 @@ nav:
- AWS CodePipeline: tutorials/integrations/aws-codepipeline.md
- AWS Security Hub: tutorials/integrations/aws-security-hub.md
- Azure: tutorials/integrations/azure-devops.md
- Signing:
- Vulnerability Scan Record Attestation: tutorials/signing/vuln-attestation.md
- Kubernetes:
- Cluster Scanning: tutorials/kubernetes/cluster-scanning.md
- Kyverno: tutorials/kubernetes/kyverno.md
- GitOps: tutorials/kubernetes/gitops.md
- Additional Resources:
- Signing:
- Vulnerability Scan Record Attestation: tutorials/signing/vuln-attestation.md
- Shell:
- Completion: tutorials/shell/shell-completion.md
- Additional Resources:
- Additional Resources: tutorials/additional-resources/references.md
- Community References: tutorials/additional-resources/community.md
- CKS Reference: tutorials/additional-resources/cks.md
@@ -58,6 +60,7 @@ nav:
- Languages:
- Go: docs/vulnerability/languages/golang.md
- Java: docs/vulnerability/languages/java.md
- Python: docs/vulnerability/languages/python.md
- Misconfiguration:
- Scanning: docs/misconfiguration/scanning.md
- Policy:
@@ -67,6 +70,7 @@ nav:
- Overview: docs/misconfiguration/custom/index.md
- Data: docs/misconfiguration/custom/data.md
- Combine: docs/misconfiguration/custom/combine.md
- Schemas: docs/misconfiguration/custom/schema.md
- Testing: docs/misconfiguration/custom/testing.md
- Debugging Policies: docs/misconfiguration/custom/debug.md
- Examples: docs/misconfiguration/custom/examples.md
@@ -122,6 +126,7 @@ nav:
- Plugin: docs/references/cli/plugin.md
- SBOM: docs/references/cli/sbom.md
- Module: docs/references/cli/module.md
- Completion: docs/references/cli/completion.md
- Modes:
- Standalone: docs/references/modes/standalone.md
- Client/Server: docs/references/modes/client-server.md

View File

@@ -96,7 +96,7 @@ func (s *AWSScanner) Scan(ctx context.Context, option flag.Options) (scan.Result
scanner := aws.New(scannerOpts...)
var freshState *state.State
if len(missing) > 0 {
if len(missing) > 0 || option.CloudOptions.UpdateCache {
var err error
freshState, err = scanner.CreateState(ctx)
if err != nil {

View File

@@ -151,9 +151,6 @@ func NewRootCommand(version string, globalFlags *flag.GlobalFlagGroup) *cobra.Co
# Run in server mode
$ trivy server`,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
Args: cobra.NoArgs,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
cmd.SetOut(outputWriter)
@@ -164,7 +161,7 @@ func NewRootCommand(version string, globalFlags *flag.GlobalFlagGroup) *cobra.Co
// viper.BindPFlag cannot be called in init().
// cf. https://github.com/spf13/cobra/issues/875
// https://github.com/spf13/viper/issues/233
if err := globalFlags.Bind(cmd.Root()); err != nil {
if err := globalFlags.Bind(cmd); err != nil {
return xerrors.Errorf("flag bind error: %w", err)
}
@@ -225,6 +222,7 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
ImageFlagGroup: flag.NewImageFlagGroup(), // container image specific
LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup,
@@ -293,12 +291,14 @@ func NewFilesystemCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup()
reportFlagGroup.ReportFormat = nil // TODO: support --report summary
reportFlagGroup.Compliance = nil // disable '--compliance'
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
fsFlags := &flag.Flags{
CacheFlagGroup: flag.NewCacheFlagGroup(),
DBFlagGroup: flag.NewDBFlagGroup(),
LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup,
@@ -353,6 +353,7 @@ func NewRootfsCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
DBFlagGroup: flag.NewDBFlagGroup(),
LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup,
@@ -402,12 +403,14 @@ func NewRepositoryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup()
reportFlagGroup.ReportFormat = nil // TODO: support --report summary
reportFlagGroup.Compliance = nil // disable '--compliance'
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
repoFlags := &flag.Flags{
CacheFlagGroup: flag.NewCacheFlagGroup(),
DBFlagGroup: flag.NewDBFlagGroup(),
LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
ReportFlagGroup: reportFlagGroup,
@@ -508,6 +511,7 @@ func NewServerCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
serverFlags := &flag.Flags{
CacheFlagGroup: flag.NewCacheFlagGroup(),
DBFlagGroup: flag.NewDBFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RemoteFlagGroup: flag.NewServerFlags(),
}
@@ -549,6 +553,7 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
reportFlagGroup.ReportFormat = nil // TODO: support --report summary
reportFlagGroup.Compliance = nil // disable '--compliance'
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
scanFlags := &flag.ScanFlagGroup{
// Enable only '--skip-dirs' and '--skip-files' and disable other flags
@@ -560,9 +565,14 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
configFlags := &flag.Flags{
CacheFlagGroup: flag.NewCacheFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RegoFlagGroup: flag.NewRegoFlagGroup(),
ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: scanFlags,
K8sFlagGroup: &flag.K8sFlagGroup{
// disable unneeded flags
K8sVersion: &flag.K8sVersionFlag,
},
ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: scanFlags,
}
cmd := &cobra.Command{
@@ -704,6 +714,10 @@ func NewPluginCommand() *cobra.Command {
}
func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
moduleFlags := &flag.Flags{
ModuleFlagGroup: flag.NewModuleFlagGroup(),
}
cmd := &cobra.Command{
Use: "module subcommand",
Aliases: []string{"m"},
@@ -719,14 +733,23 @@ func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
Aliases: []string{"i"},
Short: "Install a module",
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := moduleFlags.Bind(cmd); err != nil {
return xerrors.Errorf("flag bind error: %w", err)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return cmd.Help()
}
repo := args[0]
opts := globalFlags.ToOptions()
return module.Install(cmd.Context(), repo, opts.Quiet, opts.Insecure)
opts, err := moduleFlags.ToOptions(cmd.Version, args, globalFlags, outputWriter)
if err != nil {
return xerrors.Errorf("flag error: %w", err)
}
return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.Insecure)
},
},
&cobra.Command{
@@ -734,16 +757,27 @@ func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
Aliases: []string{"u"},
Short: "Uninstall a module",
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := moduleFlags.Bind(cmd); err != nil {
return xerrors.Errorf("flag bind error: %w", err)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return cmd.Help()
}
repo := args[0]
return module.Uninstall(cmd.Context(), repo)
opts, err := moduleFlags.ToOptions(cmd.Version, args, globalFlags, outputWriter)
if err != nil {
return xerrors.Errorf("flag error: %w", err)
}
return module.Uninstall(cmd.Context(), opts.ModuleDir, repo)
},
},
)
moduleFlags.AddFlags(cmd)
cmd.SetFlagErrorFunc(flagErrorFunc)
return cmd
}
@@ -762,8 +796,9 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup()
compliance := flag.ComplianceFlag
compliance.Usage += fmt.Sprintf(" (%s,%s)", types.ComplianceK8sNsa, types.ComplianceK8sCIS)
compliance.Usage += fmt.Sprintf(" (%s,%s, %s, %s)", types.ComplianceK8sNsa, types.ComplianceK8sCIS, types.ComplianceK8sPSSBaseline, types.ComplianceK8sPSSRestricted)
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
k8sFlags := &flag.Flags{
CacheFlagGroup: flag.NewCacheFlagGroup(),
@@ -826,6 +861,7 @@ func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
compliance := flag.ComplianceFlag
compliance.Usage += fmt.Sprintf(" (%s, %s)", types.ComplianceAWSCIS12, types.ComplianceAWSCIS14)
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
awsFlags := &flag.Flags{
AWSFlagGroup: flag.NewAWSFlagGroup(),
@@ -895,6 +931,7 @@ func NewVMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
DBFlagGroup: flag.NewDBFlagGroup(),
LicenseFlagGroup: flag.NewLicenseFlagGroup(),
MisconfFlagGroup: flag.NewMisconfFlagGroup(),
ModuleFlagGroup: flag.NewModuleFlagGroup(),
RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode
ReportFlagGroup: reportFlagGroup,
ScanFlagGroup: flag.NewScanFlagGroup(),

View File

@@ -29,7 +29,7 @@ import (
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
// TargetKind represents what kind of artifact Trivy scans
@@ -131,7 +131,10 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...runnerOptio
}
// Initialize WASM modules
m, err := module.NewManager(ctx)
m, err := module.NewManager(ctx, module.Options{
Dir: cliOptions.ModuleDir,
EnabledModules: cliOptions.EnabledModules,
})
if err != nil {
return nil, xerrors.Errorf("WASM module error: %w", err)
}
@@ -341,7 +344,7 @@ func (r *runner) initJavaDB(opts flag.Options) error {
// Update the Java DB
noProgress := opts.Quiet || opts.NoProgress
javadb.Init(opts.CacheDir, opts.SkipJavaDBUpdate, noProgress, opts.Insecure)
javadb.Init(opts.CacheDir, opts.JavaDBRepository, opts.SkipJavaDBUpdate, noProgress, opts.Insecure)
if opts.DownloadJavaDBOnly {
if err := javadb.Update(); err != nil {
return xerrors.Errorf("Java DB error: %w", err)
@@ -366,12 +369,12 @@ func (r *runner) initCache(opts flag.Options) error {
}
// standalone mode
utils.SetCacheDir(opts.CacheDir)
fsutils.SetCacheDir(opts.CacheDir)
cacheClient, err := operation.NewCache(opts.CacheOptions)
if err != nil {
return xerrors.Errorf("unable to initialize the cache: %w", err)
}
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
log.Logger.Debugf("cache dir: %s", fsutils.CacheDir())
if opts.Reset {
defer cacheClient.Close()
@@ -454,6 +457,7 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err
return xerrors.Errorf("report error: %w", err)
}
exitOnEOL(opts, report.Metadata)
Exit(opts, report.Results.Failed())
return nil
@@ -520,8 +524,11 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
opts.ImageConfigScanners = nil
// TODO: define image-config-scanners in the spec
if opts.Compliance.Spec.ID == "docker-cis" {
opts.Scanners = nil
opts.ImageConfigScanners = scanners
opts.Scanners = types.Scanners{types.VulnerabilityScanner}
opts.ImageConfigScanners = types.Scanners{
types.MisconfigScanner,
types.SecretScanner,
}
}
}
@@ -561,7 +568,6 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
log.Logger.Debug("Policies successfully loaded from disk")
disableEmbedded = true
}
configScannerOptions = config.ScannerOption{
Trace: opts.Trace,
Namespaces: append(opts.PolicyNamespaces, defaultPolicyNamespaces...),
@@ -572,6 +578,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
HelmFileValues: opts.HelmFileValues,
HelmStringValues: opts.HelmStringValues,
TerraformTFVars: opts.TerraformTFVars,
K8sVersion: opts.K8sVersion,
DisableEmbeddedPolicies: disableEmbedded,
}
}
@@ -638,12 +645,10 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner, cacheClient cache.Cache) (
types.Report, error) {
scannerConfig, scanOptions, err := initScannerConfig(opts, cacheClient)
if err != nil {
return types.Report{}, err
}
s, cleanup, err := initializeScanner(ctx, scannerConfig)
if err != nil {
return types.Report{}, xerrors.Errorf("unable to initialize a scanner: %w", err)
@@ -663,6 +668,13 @@ func Exit(opts flag.Options, failedResults bool) {
}
}
func exitOnEOL(opts flag.Options, m types.Metadata) {
if opts.ExitOnEOL != 0 && m.OS != nil && m.OS.Eosl {
log.Logger.Errorf("Detected EOL OS: %s %s", m.OS.Family, m.OS.Name)
os.Exit(opts.ExitOnEOL)
}
}
func canonicalVersion(ver string) string {
if ver == devVersion {
return ver

View File

@@ -3,24 +3,22 @@ package operation
import (
"context"
"crypto/tls"
"crypto/x509"
"os"
"strings"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/go-redis/redis/v8"
"github.com/google/wire"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
// SuperSet binds cache dependencies
@@ -45,7 +43,7 @@ func NewCache(c flag.CacheOptions) (Cache, error) {
}
if !lo.IsEmpty(c.RedisOptions) {
caCert, cert, err := utils.GetTLSConfig(c.RedisCACert, c.RedisCert, c.RedisKey)
caCert, cert, err := GetTLSConfig(c.RedisCACert, c.RedisCert, c.RedisKey)
if err != nil {
return Cache{}, err
}
@@ -66,7 +64,7 @@ func NewCache(c flag.CacheOptions) (Cache, error) {
}
// standalone mode
fsCache, err := cache.NewFSCache(utils.CacheDir())
fsCache, err := cache.NewFSCache(fsutils.CacheDir())
if err != nil {
return Cache{}, xerrors.Errorf("unable to initialize fs cache: %w", err)
}
@@ -87,8 +85,8 @@ func (c Cache) Reset() (err error) {
// ClearDB clears the DB cache
func (c Cache) ClearDB() (err error) {
log.Logger.Info("Removing DB file...")
if err = os.RemoveAll(utils.CacheDir()); err != nil {
return xerrors.Errorf("failed to remove the directory (%s) : %w", utils.CacheDir(), err)
if err = os.RemoveAll(fsutils.CacheDir()); err != nil {
return xerrors.Errorf("failed to remove the directory (%s) : %w", fsutils.CacheDir(), err)
}
return nil
}
@@ -172,3 +170,21 @@ func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate
}
return policyPaths, nil
}
// GetTLSConfig gets tls config from CA, Cert and Key file
func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool, cert, nil
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/module"
rpcServer "github.com/aquasecurity/trivy/pkg/rpc/server"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
// Run runs the scan
@@ -21,13 +21,13 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
}
// configure cache dir
utils.SetCacheDir(opts.CacheDir)
fsutils.SetCacheDir(opts.CacheDir)
cache, err := operation.NewCache(opts.CacheOptions)
if err != nil {
return xerrors.Errorf("server cache error: %w", err)
}
defer cache.Close()
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
log.Logger.Debugf("cache dir: %s", fsutils.CacheDir())
if opts.Reset {
return cache.ClearDB()
@@ -48,7 +48,10 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
}
// Initialize WASM modules
m, err := module.NewManager(ctx)
m, err := module.NewManager(ctx, module.Options{
Dir: opts.ModuleDir,
EnabledModules: opts.EnabledModules,
})
if err != nil {
return xerrors.Errorf("WASM module error: %w", err)
}

View File

@@ -96,6 +96,10 @@ func scannerByCheckID(checkID string) types.Scanner {
return types.VulnerabilityScanner
case strings.HasPrefix(checkID, "avd-"):
return types.MisconfigScanner
case strings.HasPrefix(checkID, "vuln-"): // custom id for filtering vulnerabilities by severity
return types.VulnerabilityScanner
case strings.HasPrefix(checkID, "secret-"): // custom id for filtering secrets by severity
return types.SecretScanner
default:
return types.UnknownScanner
}

View File

@@ -0,0 +1,75 @@
package spec
import (
"github.com/samber/lo"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
// We might be going to rewrite these functions in Rego,
// but we'll keep them for now until we need flexibility.
var customIDs = map[string]func(types.Result) types.Result{
"VULN-CRITICAL": filterCriticalVulns,
"VULN-HIGH": filterHighVulns,
"SECRET-CRITICAL": filterCriticalSecrets,
"SECRET-HIGH": filterHighSecrets,
}
func mapCustomIDsToFilteredResults(result types.Result, checkIDs map[types.Scanner][]string,
mapCheckByID map[string]types.Results) {
for _, ids := range checkIDs {
for _, id := range ids {
filterFunc, ok := customIDs[id]
if !ok {
continue
}
filtered := filterFunc(result)
if filtered.IsEmpty() {
continue
}
mapCheckByID[id] = types.Results{filtered}
}
}
}
func filterCriticalVulns(result types.Result) types.Result {
return filterVulns(result, dbTypes.SeverityCritical)
}
func filterHighVulns(result types.Result) types.Result {
return filterVulns(result, dbTypes.SeverityHigh)
}
func filterVulns(result types.Result, severity dbTypes.Severity) types.Result {
filtered := lo.Filter(result.Vulnerabilities, func(vuln types.DetectedVulnerability, _ int) bool {
return vuln.Severity == severity.String()
})
return types.Result{
Target: result.Target,
Class: result.Class,
Type: result.Type,
Vulnerabilities: filtered,
}
}
func filterCriticalSecrets(result types.Result) types.Result {
return filterSecrets(result, dbTypes.SeverityCritical)
}
func filterHighSecrets(result types.Result) types.Result {
return filterSecrets(result, dbTypes.SeverityHigh)
}
func filterSecrets(result types.Result, severity dbTypes.Severity) types.Result {
filtered := lo.Filter(result.Secrets, func(vuln ftypes.SecretFinding, _ int) bool {
return vuln.Severity == severity.String()
})
return types.Result{
Target: result.Target,
Class: result.Class,
Type: result.Type,
Secrets: filtered,
}
}

View File

@@ -35,6 +35,10 @@ func MapSpecCheckIDToFilteredResults(result types.Result, checkIDs map[types.Sca
Misconfigurations: []types.DetectedMisconfiguration{m},
})
}
// Evaluate custom IDs
mapCustomIDsToFilteredResults(result, checkIDs, mapCheckByID)
return mapCheckByID
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/trivy/pkg/compliance/spec"
"github.com/aquasecurity/trivy/pkg/fanal/secret"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -19,6 +20,10 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
},
types.VulnerabilityScanner: {
"CVE-9999-9999",
"VULN-CRITICAL",
},
types.SecretScanner: {
"SECRET-CRITICAL",
},
}
tests := []struct {
@@ -89,25 +94,62 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
},
},
{
name: "vulnerability",
name: "secret",
checkIDs: checkIDs,
result: types.Result{
Target: "target",
Class: types.ClassLangPkg,
Type: ftypes.GoModule,
Vulnerabilities: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-9999-0001"},
{VulnerabilityID: "CVE-9999-9999"},
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
{
RuleID: "aws-access-key-id",
Category: secret.CategoryAWS,
Severity: "CRITICAL",
Title: "AWS Access Key ID",
Code: ftypes.Code{
Lines: []ftypes.Line{
{
Number: 2,
Content: "AWS_ACCESS_KEY_ID=*****",
},
},
},
},
{
RuleID: "aws-account-id",
Category: secret.CategoryAWS,
Severity: "HIGH",
Title: "AWS Account ID",
Code: ftypes.Code{
Lines: []ftypes.Line{
{
Number: 1,
Content: "AWS_ACCOUNT_ID=*****",
},
},
},
},
},
},
want: map[string]types.Results{
"CVE-9999-9999": {
"SECRET-CRITICAL": {
{
Target: "target",
Class: types.ClassLangPkg,
Type: ftypes.GoModule,
Vulnerabilities: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-9999-9999"},
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
{
RuleID: "aws-access-key-id",
Category: secret.CategoryAWS,
Severity: "CRITICAL",
Title: "AWS Access Key ID",
Code: ftypes.Code{
Lines: []ftypes.Line{
{
Number: 2,
Content: "AWS_ACCESS_KEY_ID=*****",
},
},
},
},
},
},
},
@@ -117,7 +159,7 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := spec.MapSpecCheckIDToFilteredResults(tt.result, tt.checkIDs)
assert.Equalf(t, tt.want, got, "CheckIDs()")
assert.Equalf(t, tt.want, got, "MapSpecCheckIDToFilteredResults()")
})
}
}

View File

@@ -4,11 +4,13 @@ import (
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
fixtures "github.com/aquasecurity/bolt-fixtures"
"github.com/aquasecurity/trivy-db/pkg/db"
jdb "github.com/aquasecurity/trivy-java-db/pkg/db"
)
// InitDB initializes testing database.
@@ -36,3 +38,20 @@ func InitDB(t *testing.T, fixtureFiles []string) string {
func Close() error {
return db.Close()
}
func InitJavaDB(t *testing.T, cacheDir string) {
dbDir := filepath.Join(cacheDir, "java-db")
javaDB, err := jdb.New(dbDir)
require.NoError(t, err)
err = javaDB.Init()
require.NoError(t, err)
meta := jdb.Metadata{
Version: jdb.SchemaVersion,
NextUpdate: time.Now().Add(24 * time.Hour),
UpdatedAt: time.Now(),
}
metac := jdb.NewMetadata(dbDir)
err = metac.Update(meta)
require.NoError(t, err)
}

View File

@@ -113,22 +113,21 @@ func (s *Scanner) Detect(osVer string, repo *ftypes.Repository, pkgs []ftypes.Pa
return nil, xerrors.Errorf("failed to get alpine advisories: %w", err)
}
installed := utils.FormatSrcVersion(pkg)
installedVersion, err := version.NewVersion(installed)
sourceVersion, err := version.NewVersion(utils.FormatSrcVersion(pkg))
if err != nil {
log.Logger.Debugf("failed to parse Alpine Linux installed package version: %s", err)
continue
}
for _, adv := range advisories {
if !s.isVulnerable(installedVersion, adv) {
if !s.isVulnerable(sourceVersion, adv) {
continue
}
vulns = append(vulns, types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
InstalledVersion: utils.FormatVersion(pkg),
FixedVersion: adv.FixedVersion,
Layer: pkg.Layer,
Ref: pkg.Ref,

View File

@@ -85,8 +85,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
var vulns []types.DetectedVulnerability
for _, pkg := range pkgs {
installed := utils.FormatSrcVersion(pkg)
installedVersion, err := version.NewVersion(installed)
sourceVersion, err := version.NewVersion(utils.FormatSrcVersion(pkg))
if err != nil {
log.Logger.Debugf("Debian installed package version error: %s", err)
continue
@@ -103,7 +102,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
VendorIDs: adv.VendorIDs,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
InstalledVersion: utils.FormatVersion(pkg),
FixedVersion: adv.FixedVersion,
Ref: pkg.Ref,
Layer: pkg.Layer,
@@ -132,7 +131,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
continue
}
if installedVersion.LessThan(fixedVersion) {
if sourceVersion.LessThan(fixedVersion) {
vulns = append(vulns, vuln)
}
}

View File

@@ -45,14 +45,13 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
return nil, xerrors.Errorf("failed to get CBL-Mariner advisories: %w", err)
}
installed := utils.FormatSrcVersion(pkg)
installedVersion := version.NewVersion(installed)
sourceVersion := version.NewVersion(utils.FormatSrcVersion(pkg))
for _, adv := range advisories {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,
InstalledVersion: utils.FormatVersion(pkg),
Ref: pkg.Ref,
Layer: pkg.Layer,
DataSource: adv.DataSource,
@@ -66,7 +65,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
// Patched vulnerabilities
fixedVersion := version.NewVersion(adv.FixedVersion)
if installedVersion.LessThan(fixedVersion) {
if sourceVersion.LessThan(fixedVersion) {
vuln.FixedVersion = fixedVersion.String()
vulns = append(vulns, vuln)
}

View File

@@ -105,8 +105,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
return nil, xerrors.Errorf("failed to get Ubuntu advisories: %w", err)
}
installed := utils.FormatSrcVersion(pkg)
installedVersion, err := version.NewVersion(installed)
sourceVersion, err := version.NewVersion(utils.FormatSrcVersion(pkg))
if err != nil {
log.Logger.Debugf("failed to parse Ubuntu installed package version: %w", err)
continue
@@ -117,7 +116,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
VulnerabilityID: adv.VulnerabilityID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: installed,
InstalledVersion: utils.FormatVersion(pkg),
FixedVersion: adv.FixedVersion,
Ref: pkg.Ref,
Layer: pkg.Layer,
@@ -136,7 +135,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa
continue
}
if installedVersion.LessThan(fixedVersion) {
if sourceVersion.LessThan(fixedVersion) {
vulns = append(vulns, vuln)
}
}

View File

@@ -19,10 +19,13 @@ import (
aos "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/fanal/log"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/mapfs"
"github.com/aquasecurity/trivy/pkg/syncx"
)
var (
analyzers = map[Type]analyzer{}
analyzers = map[Type]analyzer{}
postAnalyzers = map[Type]postAnalyzerInitialize{}
// ErrUnknownOS occurs when unknown OS is analyzed.
ErrUnknownOS = xerrors.New("unknown OS")
@@ -39,6 +42,7 @@ var (
// AnalyzerOptions is used to initialize analyzers
type AnalyzerOptions struct {
Group Group
Slow bool
FilePatterns []string
DisabledAnalyzers []Type
SecretScannerOption SecretScannerOption
@@ -70,6 +74,13 @@ type analyzer interface {
Required(filePath string, info os.FileInfo) bool
}
type PostAnalyzer interface {
Type() Type
Version() int
PostAnalyze(ctx context.Context, input PostAnalysisInput) (*AnalysisResult, error)
Required(filePath string, info os.FileInfo) bool
}
////////////////////
// Analyzer group //
////////////////////
@@ -79,9 +90,21 @@ type Group string
const GroupBuiltin Group = "builtin"
func RegisterAnalyzer(analyzer analyzer) {
if _, ok := analyzers[analyzer.Type()]; ok {
log.Logger.Fatalf("analyzer %s is registered twice", analyzer.Type())
}
analyzers[analyzer.Type()] = analyzer
}
type postAnalyzerInitialize func(options AnalyzerOptions) (PostAnalyzer, error)
func RegisterPostAnalyzer(t Type, initializer postAnalyzerInitialize) {
if _, ok := postAnalyzers[t]; ok {
log.Logger.Fatalf("analyzer %s is registered twice", t)
}
postAnalyzers[t] = initializer
}
// DeregisterAnalyzer is mainly for testing
func DeregisterAnalyzer(t Type) {
delete(analyzers, t)
@@ -96,8 +119,9 @@ type CustomGroup interface {
type Opener func() (dio.ReadSeekCloserAt, error)
type AnalyzerGroup struct {
analyzers []analyzer
filePatterns map[Type][]*regexp.Regexp
analyzers []analyzer
postAnalyzers []PostAnalyzer
filePatterns map[Type][]*regexp.Regexp
}
///////////////////////////
@@ -113,6 +137,11 @@ type AnalysisInput struct {
Options AnalysisOptions
}
type PostAnalysisInput struct {
FS fs.FS
Options AnalysisOptions
}
type AnalysisOptions struct {
Offline bool
}
@@ -335,16 +364,39 @@ func NewAnalyzerGroup(opt AnalyzerOptions) (AnalyzerGroup, error) {
group.analyzers = append(group.analyzers, a)
}
for analyzerType, init := range postAnalyzers {
a, err := init(opt)
if err != nil {
return AnalyzerGroup{}, xerrors.Errorf("post-analyzer init error: %w", err)
}
if !belongToGroup(groupName, analyzerType, opt.DisabledAnalyzers, a) {
continue
}
group.postAnalyzers = append(group.postAnalyzers, a)
}
return group, nil
}
type Versions struct {
Analyzers map[string]int
PostAnalyzers map[string]int
}
// AnalyzerVersions returns analyzer version identifier used for cache keys.
func (ag AnalyzerGroup) AnalyzerVersions() map[string]int {
versions := map[string]int{}
func (ag AnalyzerGroup) AnalyzerVersions() Versions {
analyzerVersions := map[string]int{}
for _, a := range ag.analyzers {
versions[string(a.Type())] = a.Version()
analyzerVersions[string(a.Type())] = a.Version()
}
postAnalyzerVersions := map[string]int{}
for _, a := range ag.postAnalyzers {
postAnalyzerVersions[string(a.Type())] = a.Version()
}
return Versions{
Analyzers: analyzerVersions,
PostAnalyzers: postAnalyzerVersions,
}
return versions
}
func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, limit *semaphore.Weighted, result *AnalysisResult,
@@ -394,15 +446,47 @@ func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, lim
log.Logger.Debugf("Analysis error: %s", err)
return
}
if ret != nil {
result.Merge(ret)
}
result.Merge(ret)
}(a, rc)
}
return nil
}
func (ag AnalyzerGroup) RequiredPostAnalyzers(filePath string, info os.FileInfo) []Type {
var postAnalyzerTypes []Type
for _, a := range ag.postAnalyzers {
if a.Required(filePath, info) {
postAnalyzerTypes = append(postAnalyzerTypes, a.Type())
}
}
return postAnalyzerTypes
}
func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, files *syncx.Map[Type, *mapfs.FS], result *AnalysisResult, opts AnalysisOptions) error {
for _, a := range ag.postAnalyzers {
fsys, ok := files.Load(a.Type())
if !ok {
continue
}
filteredFS, err := fsys.Filter(result.SystemInstalledFiles)
if err != nil {
return xerrors.Errorf("unable to filter filesystem: %w", err)
}
res, err := a.PostAnalyze(ctx, PostAnalysisInput{
FS: filteredFS,
Options: opts,
})
if err != nil {
return xerrors.Errorf("post analysis error: %w", err)
}
result.Merge(res)
}
return nil
}
func (ag AnalyzerGroup) filePatternMatch(analyzerType Type, filePath string) bool {
for _, pattern := range ag.filePatterns[analyzerType] {
if pattern.MatchString(filePath) {

View File

@@ -18,6 +18,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/apk"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/ubuntu"
@@ -369,8 +370,31 @@ func TestAnalyzerGroup_AnalyzeFile(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []types.Package{
{
Name: "actioncable",
Version: "5.2.3",
ID: "actioncable@5.2.3",
Name: "actioncable",
Version: "5.2.3",
Indirect: false,
DependsOn: []string{
"actionpack@5.2.3",
},
Locations: []types.Location{
{
StartLine: 4,
EndLine: 4,
},
},
},
{
ID: "actionpack@5.2.3",
Name: "actionpack",
Version: "5.2.3",
Indirect: true,
Locations: []types.Location{
{
StartLine: 6,
EndLine: 6,
},
},
},
},
},
@@ -407,8 +431,31 @@ func TestAnalyzerGroup_AnalyzeFile(t *testing.T) {
FilePath: "/app/Gemfile-dev.lock",
Libraries: []types.Package{
{
Name: "actioncable",
Version: "5.2.3",
ID: "actioncable@5.2.3",
Name: "actioncable",
Version: "5.2.3",
Indirect: false,
DependsOn: []string{
"actionpack@5.2.3",
},
Locations: []types.Location{
{
StartLine: 4,
EndLine: 4,
},
},
},
{
ID: "actionpack@5.2.3",
Name: "actionpack",
Version: "5.2.3",
Indirect: true,
Locations: []types.Location{
{
StartLine: 6,
EndLine: 6,
},
},
},
},
},
@@ -503,17 +550,23 @@ func TestAnalyzerGroup_AnalyzerVersions(t *testing.T) {
tests := []struct {
name string
disabled []analyzer.Type
want map[string]int
want analyzer.Versions
}{
{
name: "happy path",
disabled: []analyzer.Type{},
want: map[string]int{
"alpine": 1,
"apk-repo": 1,
"apk": 2,
"bundler": 1,
"ubuntu": 1,
want: analyzer.Versions{
Analyzers: map[string]int{
"alpine": 1,
"apk-repo": 1,
"apk": 2,
"bundler": 1,
"ubuntu": 1,
"ubuntu-esm": 1,
},
PostAnalyzers: map[string]int{
"jar": 1,
},
},
},
{
@@ -522,10 +575,15 @@ func TestAnalyzerGroup_AnalyzerVersions(t *testing.T) {
analyzer.TypeAlpine,
analyzer.TypeApkRepo,
analyzer.TypeUbuntu,
analyzer.TypeUbuntuESM,
analyzer.TypeJar,
},
want: map[string]int{
"apk": 2,
"bundler": 1,
want: analyzer.Versions{
Analyzers: map[string]int{
"apk": 2,
"bundler": 1,
},
PostAnalyzers: map[string]int{},
},
},
}

View File

@@ -17,6 +17,7 @@ type ScannerOption struct {
HelmFileValues []string
HelmStringValues []string
TerraformTFVars []string
K8sVersion string
}
func (o *ScannerOption) Sort() {

View File

@@ -91,12 +91,14 @@ func NewConfigAnalyzerGroup(opts ConfigAnalyzerOptions) (ConfigAnalyzerGroup, er
}
// AnalyzerVersions returns analyzer version identifier used for cache keys.
func (ag *ConfigAnalyzerGroup) AnalyzerVersions() map[string]int {
func (ag *ConfigAnalyzerGroup) AnalyzerVersions() Versions {
versions := map[string]int{}
for _, ca := range ag.configAnalyzers {
versions[string(ca.Type())] = ca.Version()
}
return versions
return Versions{
Analyzers: versions,
}
}
func (ag *ConfigAnalyzerGroup) AnalyzeImageConfig(ctx context.Context, targetOS types.OS, config *v1.ConfigFile) *ConfigAnalysisResult {

View File

@@ -126,14 +126,16 @@ func TestConfigAnalyzerGroup_AnalyzerVersions(t *testing.T) {
tests := []struct {
name string
disabled []analyzer.Type
want map[string]int
want analyzer.Versions
}{
{
name: "happy path",
disabled: []analyzer.Type{},
want: map[string]int{
"apk-command": 1,
"test": 1,
want: analyzer.Versions{
Analyzers: map[string]int{
"apk-command": 1,
"test": 1,
},
},
},
{
@@ -142,8 +144,10 @@ func TestConfigAnalyzerGroup_AnalyzerVersions(t *testing.T) {
analyzer.TypeAlpine,
analyzer.TypeApkCommand,
},
want: map[string]int{
"test": 1,
want: analyzer.Versions{
Analyzers: map[string]int{
"test": 1,
},
},
},
}

View File

@@ -20,6 +20,7 @@ const (
TypeRedHatBase Type = "redhat"
TypeSUSE Type = "suse"
TypeUbuntu Type = "ubuntu"
TypeUbuntuESM Type = "ubuntu-esm"
// OS Package
TypeApk Type = "apk"

View File

@@ -23,7 +23,7 @@ func Analyze(fileType, filePath string, r dio.ReadSeekerAt, parser godeptypes.Pa
return ToAnalysisResult(fileType, filePath, "", parsedLibs, parsedDependencies), nil
}
func ToAnalysisResult(fileType, filePath, libFilePath string, libs []godeptypes.Library, depGraph []godeptypes.Dependency) *analyzer.AnalysisResult {
func ToApplication(fileType, filePath, libFilePath string, libs []godeptypes.Library, depGraph []godeptypes.Dependency) *types.Application {
if len(libs) == 0 {
return nil
}
@@ -61,11 +61,19 @@ func ToAnalysisResult(fileType, filePath, libFilePath string, libs []godeptypes.
Locations: locs,
})
}
apps := []types.Application{{
return &types.Application{
Type: fileType,
FilePath: filePath,
Libraries: pkgs,
}}
return &analyzer.AnalysisResult{Applications: apps}
}
}
func ToAnalysisResult(fileType, filePath, libFilePath string, libs []godeptypes.Library, depGraph []godeptypes.Dependency) *analyzer.AnalysisResult {
app := ToApplication(fileType, filePath, libFilePath, libs, depGraph)
if app == nil {
return nil
}
return &analyzer.AnalysisResult{Applications: []types.Application{*app}}
}

View File

@@ -2,57 +2,326 @@ package mod
import (
"context"
"errors"
"fmt"
"go/build"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"
"unicode"
"github.com/samber/lo"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/golang/mod"
"github.com/aquasecurity/go-dep-parser/pkg/golang/sum"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/licensing"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
func init() {
analyzer.RegisterAnalyzer(&gomodAnalyzer{})
analyzer.RegisterPostAnalyzer(types.GoMod, newGoModAnalyzer)
}
const version = 2
var requiredFiles = []string{types.GoMod, types.GoSum}
type gomodAnalyzer struct{}
func (a gomodAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
var parser godeptypes.Parser
switch filepath.Base(input.FilePath) {
case types.GoMod:
parser = mod.NewParser()
case types.GoSum:
parser = sum.NewParser()
default:
return nil, nil
var (
requiredFiles = []string{
types.GoMod,
types.GoSum,
}
licenseRegexp = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING|README|NOTICE).*$`)
)
res, err := language.Analyze(types.GoModule, input.FilePath, input.Content, parser)
if err != nil {
return nil, xerrors.Errorf("failed to analyze %s: %w", input.FilePath, err)
}
return res, nil
type gomodAnalyzer struct {
// root go.mod/go.sum
modParser godeptypes.Parser
sumParser godeptypes.Parser
// go.mod/go.sum in dependencies
leafModParser godeptypes.Parser
}
func (a gomodAnalyzer) Required(filePath string, _ os.FileInfo) bool {
func newGoModAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
return &gomodAnalyzer{
modParser: mod.NewParser(true), // Only the root module should replace
sumParser: sum.NewParser(),
leafModParser: mod.NewParser(false),
}, nil
}
func (a *gomodAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) {
var apps []types.Application
err := fs.WalkDir(input.FS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
} else if !d.Type().IsRegular() {
return nil
}
dir, file := filepath.Split(path)
if file != types.GoMod {
return nil
}
// Parse go.mod
gomod, err := parse(input.FS, path, a.modParser)
if err != nil {
return xerrors.Errorf("parse error: %w", err)
} else if gomod == nil {
return nil
}
if lessThanGo117(gomod) {
// e.g. /app/go.mod => /app/go.sum
sumPath := filepath.Join(dir, types.GoSum)
gosum, err := parse(input.FS, sumPath, a.sumParser)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return xerrors.Errorf("parse error: %w", err)
}
mergeGoSum(gomod, gosum)
}
apps = append(apps, *gomod)
return nil
})
if err != nil {
return nil, xerrors.Errorf("walk error: %w", err)
}
if err = a.fillAdditionalData(apps); err != nil {
return nil, xerrors.Errorf("unable to collect additional info: %w", err)
}
return &analyzer.AnalysisResult{
Applications: apps,
}, nil
}
func (a *gomodAnalyzer) Required(filePath string, _ os.FileInfo) bool {
fileName := filepath.Base(filePath)
return slices.Contains(requiredFiles, fileName)
}
func (a gomodAnalyzer) Type() analyzer.Type {
func (a *gomodAnalyzer) Type() analyzer.Type {
return analyzer.TypeGoMod
}
func (a gomodAnalyzer) Version() int {
func (a *gomodAnalyzer) Version() int {
return version
}
// fillAdditionalData collects licenses and dependency relationships, then update applications.
func (a *gomodAnalyzer) fillAdditionalData(apps []types.Application) error {
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = build.Default.GOPATH
}
// $GOPATH/pkg/mod
modPath := filepath.Join(gopath, "pkg", "mod")
if !fsutils.DirExists(modPath) {
log.Logger.Debugf("GOPATH (%s) not found. Need 'go mod download' to fill licenses and dependency relationships", modPath)
return nil
}
licenses := map[string][]string{}
for i, app := range apps {
// Actually used dependencies
usedLibs := lo.SliceToMap(app.Libraries, func(pkg types.Package) (string, types.Package) {
return pkg.Name, pkg
})
for j, lib := range app.Libraries {
if l, ok := licenses[lib.ID]; ok {
// Fill licenses
apps[i].Libraries[j].Licenses = l
continue
}
// e.g. $GOPATH/pkg/mod/github.com/aquasecurity/go-dep-parser@v1.0.0
modDir := filepath.Join(modPath, fmt.Sprintf("%s@v%s", normalizeModName(lib.Name), lib.Version))
// Collect licenses
if licenseNames, err := findLicense(modDir); err != nil {
return xerrors.Errorf("license error: %w", err)
} else {
// Cache the detected licenses
licenses[lib.ID] = licenseNames
// Fill licenses
apps[i].Libraries[j].Licenses = licenseNames
}
// Collect dependencies of the direct dependency
if dep, err := a.collectDeps(modDir, lib.ID); err != nil {
return xerrors.Errorf("dependency graph error: %w", err)
} else if dep.ID == "" {
// go.mod not found
continue
} else {
// Filter out unused dependencies and convert module names to module IDs
apps[i].Libraries[j].DependsOn = lo.FilterMap(dep.DependsOn, func(modName string, _ int) (string, bool) {
if m, ok := usedLibs[modName]; !ok {
return "", false
} else {
return m.ID, true
}
})
}
}
}
return nil
}
func (a *gomodAnalyzer) collectDeps(modDir string, pkgID string) (godeptypes.Dependency, error) {
// e.g. $GOPATH/pkg/mod/github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237/go.mod
modPath := filepath.Join(modDir, "go.mod")
f, err := os.Open(modPath)
if errors.Is(err, fs.ErrNotExist) {
log.Logger.Debugf("Unable to identify dependencies of %s as it doesn't support Go modules", pkgID)
return godeptypes.Dependency{}, nil
} else if err != nil {
return godeptypes.Dependency{}, xerrors.Errorf("file open error: %w", err)
}
defer f.Close()
// Parse go.mod under $GOPATH/pkg/mod
libs, _, err := a.leafModParser.Parse(f)
if err != nil {
return godeptypes.Dependency{}, xerrors.Errorf("%s parse error: %w", modPath, err)
}
// Filter out indirect dependencies
dependsOn := lo.FilterMap(libs, func(lib godeptypes.Library, index int) (string, bool) {
return lib.Name, !lib.Indirect
})
return godeptypes.Dependency{
ID: pkgID,
DependsOn: dependsOn,
}, nil
}
func parse(fsys fs.FS, path string, parser godeptypes.Parser) (*types.Application, error) {
f, err := fsys.Open(path)
if err != nil {
return nil, xerrors.Errorf("file open error: %w", err)
}
defer f.Close()
file, ok := f.(dio.ReadSeekCloserAt)
if !ok {
return nil, xerrors.Errorf("type assertion error: %w", err)
}
// Parse go.mod or go.sum
libs, deps, err := parser.Parse(file)
if err != nil {
return nil, xerrors.Errorf("%s parse error: %w", path, err)
}
return language.ToApplication(types.GoModule, path, "", libs, deps), nil
}
func lessThanGo117(gomod *types.Application) bool {
for _, lib := range gomod.Libraries {
// The indirect field is populated only in Go 1.17+
if lib.Indirect {
return false
}
}
return true
}
func mergeGoSum(gomod, gosum *types.Application) {
if gomod == nil || gosum == nil {
return
}
uniq := map[string]types.Package{}
for _, lib := range gomod.Libraries {
// It will be used for merging go.sum.
uniq[lib.Name] = lib
}
// For Go 1.16 or less, we need to merge go.sum into go.mod.
for _, lib := range gosum.Libraries {
// Skip dependencies in go.mod so that go.mod should be preferred.
if _, ok := uniq[lib.Name]; ok {
continue
}
// This dependency doesn't exist in go.mod, so it must be an indirect dependency.
lib.Indirect = true
uniq[lib.Name] = lib
}
gomod.Libraries = maps.Values(uniq)
}
func findLicense(dir string) ([]string, error) {
var license *types.LicenseFile
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
} else if !d.Type().IsRegular() {
return nil
}
if !licenseRegexp.MatchString(filepath.Base(path)) {
return nil
}
// e.g. $GOPATH/pkg/mod/github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237/LICENSE
f, err := os.Open(path)
if err != nil {
return xerrors.Errorf("file (%s) open error: %w", path, err)
}
defer f.Close()
l, err := licensing.Classify(path, f)
if err != nil {
return xerrors.Errorf("license classify error: %w", err)
}
// License found
if l != nil && len(l.Findings) > 0 {
license = l
return io.EOF
}
return nil
})
// The module path may not exist
if errors.Is(err, os.ErrNotExist) {
return nil, nil
} else if err != nil && !errors.Is(err, io.EOF) {
return nil, fmt.Errorf("finding a known open source license: %w", err)
} else if license == nil || len(license.Findings) == 0 {
return nil, nil
}
return lo.Map(license.Findings, func(finding types.LicenseFinding, _ int) string {
return finding.Name
}), nil
}
// normalizeModName escapes upper characters
// e.g. 'github.com/BurntSushi/toml' => 'github.com/!burnt!sushi'
func normalizeModName(name string) string {
var newName []rune
for _, c := range name {
if unicode.IsUpper(c) {
// 'A' => '!a'
newName = append(newName, '!', unicode.ToLower(c))
} else {
newName = append(newName, c)
}
}
return string(newName)
}

View File

@@ -15,28 +15,35 @@ import (
func Test_gomodAnalyzer_Analyze(t *testing.T) {
tests := []struct {
name string
filePath string
inputFile string
want *analyzer.AnalysisResult
wantErr string
name string
dir string
want *analyzer.AnalysisResult
wantErr string
}{
{
name: "go.mod",
filePath: "testdata/go.mod",
inputFile: "testdata/normal_go.mod",
name: "happy",
dir: "testdata/happy",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.GoModule,
FilePath: "testdata/go.mod",
FilePath: "go.mod",
Libraries: []types.Package{
{
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237",
Name: "github.com/aquasecurity/go-dep-parser",
Version: "0.0.0-20220406074731-71021a481237",
Licenses: []string{
"MIT",
},
DependsOn: []string{
"golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
},
},
{
Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1",
ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
Name: "golang.org/x/xerrors",
Version: "0.0.0-20200804184101-5ec99f83aff1",
Indirect: true,
},
},
@@ -45,62 +52,74 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) {
},
},
{
name: "go.sum",
filePath: "testdata/go.sum",
inputFile: "testdata/normal_go.sum",
name: "less than 1.17",
dir: "testdata/merge",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.GoModule,
FilePath: "testdata/go.sum",
FilePath: "go.mod",
Libraries: []types.Package{
{Name: "github.com/BurntSushi/toml", Version: "0.3.1"},
{Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0-20190314233015-f79a8a8ca69d"},
{Name: "github.com/davecgh/go-spew", Version: "1.1.0"},
{Name: "github.com/pmezard/go-difflib", Version: "1.0.0"},
{Name: "github.com/russross/blackfriday/v2", Version: "2.0.1"},
{Name: "github.com/shurcooL/sanitized_anchor_name", Version: "1.0.0"},
{Name: "github.com/stretchr/objx", Version: "0.1.0"},
{Name: "github.com/stretchr/testify", Version: "1.7.0"},
{Name: "github.com/urfave/cli", Version: "1.22.5"},
{Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"},
{Name: "gopkg.in/check.v1", Version: "0.0.0-20161208181325-20d25e280405"},
{Name: "gopkg.in/yaml.v2", Version: "2.2.2"},
{Name: "gopkg.in/yaml.v3", Version: "3.0.0-20200313102051-9f266ea9e77c"},
{
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230219131432-590b1dfb6edd",
Name: "github.com/aquasecurity/go-dep-parser",
Version: "0.0.0-20230219131432-590b1dfb6edd",
DependsOn: []string{
"github.com/BurntSushi/toml@v0.3.1",
},
},
{
ID: "github.com/BurntSushi/toml@v0.3.1",
Name: "github.com/BurntSushi/toml",
Version: "0.3.1",
Indirect: true,
Licenses: []string{
"MIT",
},
},
},
},
},
},
},
{
name: "sad go.mod",
filePath: "testdata/go.mod",
inputFile: "testdata/sad_go.mod",
wantErr: "unknown directive",
name: "no go.sum",
dir: "testdata/no_gosum",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.GoModule,
FilePath: "go.mod",
Libraries: []types.Package{
{
ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211110174639-8257534ffed3",
Name: "github.com/aquasecurity/go-dep-parser",
Version: "0.0.0-20211110174639-8257534ffed3",
},
},
},
},
},
},
{
name: "sad go.sum",
filePath: "testdata/go.sum",
inputFile: "testdata/sad_go.sum",
want: nil,
name: "sad go.mod",
dir: "testdata/sad",
wantErr: "unknown directive",
},
}
for _, tt := range tests {
t.Setenv("GOPATH", "testdata")
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.inputFile)
a, err := newGoModAnalyzer(analyzer.AnalyzerOptions{})
require.NoError(t, err)
defer f.Close()
a := gomodAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.filePath,
Content: f,
got, err := a.PostAnalyze(ctx, analyzer.PostAnalysisInput{
FS: os.DirFS(tt.dir),
})
if tt.wantErr != "" {
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
require.ErrorContains(t, err, tt.wantErr)
return
}
if got != nil {

View File

@@ -0,0 +1,7 @@
module github.com/org/repo
go 1.15
require github.com/aquasecurity/go-dep-parser v0.0.0-20211110174639-8257534ffed3
replace github.com/aquasecurity/go-dep-parser => github.com/aquasecurity/go-dep-parser v0.0.0-20230219131432-590b1dfb6edd

View File

@@ -0,0 +1,4 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aquasecurity/go-dep-parser v0.0.0-20230219131432-590b1dfb6edd h1:H9IR14rR3+Z13ZH7ay9bs2hHBL7WAqdEJLLr8nhx/Rs=
github.com/aquasecurity/go-dep-parser v0.0.0-20230219131432-590b1dfb6edd/go.mod h1:4dZHU2Ntsh9EopNVdTKf8UjSGDNTMVoyB5B34RjD75g=

View File

@@ -0,0 +1,5 @@
module github.com/org/repo
go 1.15
require github.com/aquasecurity/go-dep-parser v0.0.0-20211110174639-8257534ffed3

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 TOML authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Teppei Fukuda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,31 @@
module github.com/aquasecurity/go-dep-parser
go 1.18
require (
github.com/BurntSushi/toml v1.2.1
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/liamg/jfather v0.0.7
github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032
github.com/samber/lo v1.37.0
github.com/stretchr/testify v1.8.1
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4
golang.org/x/mod v0.8.0
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/text v0.3.8 // indirect
)

View File

@@ -0,0 +1,31 @@
module github.com/aquasecurity/go-dep-parser
go 1.18
require (
github.com/BurntSushi/toml v1.2.1
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/liamg/jfather v0.0.7
github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032
github.com/samber/lo v1.37.0
github.com/stretchr/testify v1.8.1
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4
golang.org/x/mod v0.8.0
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/text v0.3.8 // indirect
)

View File

@@ -1 +0,0 @@
invalid

View File

@@ -2,6 +2,7 @@ package jar
import (
"context"
"io/fs"
"os"
"path/filepath"
"strings"
@@ -9,16 +10,18 @@ import (
"golang.org/x/xerrors"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
"github.com/aquasecurity/go-dep-parser/pkg/java/jar"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/javadb"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/parallel"
)
func init() {
analyzer.RegisterAnalyzer(&javaLibraryAnalyzer{})
analyzer.RegisterPostAnalyzer(analyzer.TypeJar, newJavaLibraryAnalyzer)
}
const version = 1
@@ -34,9 +37,16 @@ var requiredExtensions = []string{
type javaLibraryAnalyzer struct {
once sync.Once
client *javadb.DB
slow bool
}
func (a *javaLibraryAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
func newJavaLibraryAnalyzer(options analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
return &javaLibraryAnalyzer{
slow: options.Slow,
}, nil
}
func (a *javaLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) {
// TODO: think about the sonatype API and "--offline"
var err error
a.once.Do(func() {
@@ -57,13 +67,33 @@ func (a *javaLibraryAnalyzer) Analyze(_ context.Context, input analyzer.Analysis
return nil, nil
}
p := jar.NewParser(a.client, jar.WithSize(input.Info.Size()), jar.WithFilePath(input.FilePath))
libs, deps, err := p.Parse(input.Content)
if err != nil {
return nil, xerrors.Errorf("jar/war/ear/par parse error: %w", err)
// It will be called on each JAR file
onFile := func(path string, info fs.FileInfo, r dio.ReadSeekerAt) (*types.Application, error) {
p := jar.NewParser(a.client, jar.WithSize(info.Size()), jar.WithFilePath(path))
libs, deps, err := p.Parse(r)
if err != nil {
return nil, xerrors.Errorf("jar/war/ear/par parse error: %w", err)
}
return language.ToApplication(types.Jar, path, path, libs, deps), nil
}
return language.ToAnalysisResult(types.Jar, input.FilePath, input.FilePath, libs, deps), nil
var apps []types.Application
onResult := func(app *types.Application) error {
if app == nil {
return nil
}
apps = append(apps, *app)
return nil
}
if err = parallel.WalkDir(ctx, input.FS, ".", a.slow, onFile, onResult); err != nil {
return nil, xerrors.Errorf("walk dir error: %w", err)
}
return &analyzer.AnalysisResult{
Applications: apps,
}, nil
}
func (a *javaLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool {

View File

@@ -2,8 +2,9 @@ package jar
import (
"context"
"github.com/aquasecurity/trivy/pkg/javadb"
"github.com/aquasecurity/trivy/pkg/mapfs"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@@ -11,6 +12,13 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/javadb"
_ "modernc.org/sqlite"
)
const (
defaultJavaDBRepository = "ghcr.io/aquasecurity/trivy-java-db"
)
func Test_javaLibraryAnalyzer_Analyze(t *testing.T) {
@@ -120,22 +128,20 @@ func Test_javaLibraryAnalyzer_Analyze(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.inputFile)
require.NoError(t, err)
defer f.Close()
stat, err := f.Stat()
require.NoError(t, err)
// init java-trivy-db with skip update
javadb.Init("testdata/testdb", true, false, false)
javadb.Init("testdata", defaultJavaDBRepository, true, false, false)
a := javaLibraryAnalyzer{}
a := javaLibraryAnalyzer{slow: true}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.inputFile,
Info: stat,
Content: f,
mfs := mapfs.New()
err := mfs.MkdirAll(filepath.Dir(tt.inputFile), os.ModePerm)
assert.NoError(t, err)
err = mfs.WriteFile(tt.inputFile, tt.inputFile)
assert.NoError(t, err)
got, err := a.PostAnalyze(ctx, analyzer.PostAnalysisInput{
FS: mfs,
})
if tt.wantErr != "" {

Binary file not shown.

View File

@@ -2,42 +2,129 @@ package poetry
import (
"context"
"errors"
"io/fs"
"os"
"path/filepath"
"golang.org/x/xerrors"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
"github.com/aquasecurity/go-dep-parser/pkg/python/poetry"
"github.com/aquasecurity/go-dep-parser/pkg/python/pyproject"
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
func init() {
analyzer.RegisterAnalyzer(&poetryLibraryAnalyzer{})
analyzer.RegisterPostAnalyzer(analyzer.TypePoetry, newPoetryAnalyzer)
}
const version = 1
type poetryLibraryAnalyzer struct{}
type poetryAnalyzer struct {
pyprojectParser *pyproject.Parser
lockParser godeptypes.Parser
}
func (a poetryLibraryAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
res, err := language.Analyze(types.Poetry, input.FilePath, input.Content, poetry.NewParser())
if err != nil {
return nil, xerrors.Errorf("unable to parse poetry.lock: %w", err)
func newPoetryAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
return &poetryAnalyzer{
pyprojectParser: pyproject.NewParser(),
lockParser: poetry.NewParser(),
}, nil
}
func (a poetryAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) {
var apps []types.Application
required := func(path string, d fs.DirEntry) bool {
return filepath.Base(path) == types.PoetryLock
}
return res, nil
err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r dio.ReadSeekerAt) error {
// Parse poetry.lock
app, err := a.parsePoetryLock(path, r)
if err != nil {
return xerrors.Errorf("parse error: %w", err)
} else if app == nil {
return nil
}
// Parse pyproject.toml alongside poetry.lock to identify the direct dependencies
if err = a.mergePyProject(input.FS, filepath.Dir(path), app); err != nil {
return err
}
apps = append(apps, *app)
return nil
})
if err != nil {
return nil, xerrors.Errorf("poetry walk error: %w", err)
}
return &analyzer.AnalysisResult{
Applications: apps,
}, nil
}
func (a poetryLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool {
func (a poetryAnalyzer) Required(filePath string, _ os.FileInfo) bool {
fileName := filepath.Base(filePath)
return fileName == types.PoetryLock
return fileName == types.PoetryLock || fileName == types.PyProject
}
func (a poetryLibraryAnalyzer) Type() analyzer.Type {
func (a poetryAnalyzer) Type() analyzer.Type {
return analyzer.TypePoetry
}
func (a poetryLibraryAnalyzer) Version() int {
func (a poetryAnalyzer) Version() int {
return version
}
func (a poetryAnalyzer) parsePoetryLock(path string, r dio.ReadSeekerAt) (*types.Application, error) {
libs, deps, err := a.lockParser.Parse(r)
if err != nil {
return nil, xerrors.Errorf("unable to parse poetry.lock: %w", err)
}
return language.ToApplication(types.Poetry, path, "", libs, deps), nil
}
func (a poetryAnalyzer) mergePyProject(fsys fs.FS, dir string, app *types.Application) error {
// Parse pyproject.toml to identify the direct dependencies
path := filepath.Join(dir, types.PyProject)
p, err := a.parsePyProject(fsys, path)
if errors.Is(err, fs.ErrNotExist) {
// Assume all the packages are direct dependencies as it cannot identify them from poetry.lock
log.Logger.Debugf("Poetry: %s not found", path)
return nil
} else if err != nil {
return xerrors.Errorf("unable to parse %s: %w", path, err)
}
for i, lib := range app.Libraries {
// Identify the direct/transitive dependencies
if _, ok := p[lib.Name]; !ok {
app.Libraries[i].Indirect = true
}
}
return nil
}
func (a poetryAnalyzer) parsePyProject(fsys fs.FS, path string) (map[string]string, error) {
// Parse pyproject.toml
f, err := fsys.Open(path)
if err != nil {
return nil, xerrors.Errorf("file open error: %w", err)
}
defer f.Close()
parsed, err := a.pyprojectParser.Parse(f)
if err != nil {
return nil, err
}
return parsed, nil
}

View File

@@ -0,0 +1,144 @@
package poetry
import (
"context"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
func Test_poetryLibraryAnalyzer_Analyze(t *testing.T) {
tests := []struct {
name string
dir string
want *analyzer.AnalysisResult
wantErr string
}{
{
name: "happy path",
dir: "testdata/happy",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.Poetry,
FilePath: "poetry.lock",
Libraries: []types.Package{
{
ID: "click@8.1.3",
Name: "click",
Version: "8.1.3",
Indirect: true,
DependsOn: []string{
"colorama@0.4.6",
},
},
{
ID: "colorama@0.4.6",
Name: "colorama",
Version: "0.4.6",
Indirect: true,
},
{
ID: "flask@1.0.3",
Name: "flask",
Version: "1.0.3",
DependsOn: []string{
"click@8.1.3",
"itsdangerous@2.1.2",
"jinja2@3.1.2",
"werkzeug@2.2.3",
},
},
{
ID: "itsdangerous@2.1.2",
Name: "itsdangerous",
Version: "2.1.2",
Indirect: true,
},
{
ID: "jinja2@3.1.2",
Name: "jinja2",
Version: "3.1.2",
Indirect: true,
DependsOn: []string{
"markupsafe@2.1.2",
},
},
{
ID: "markupsafe@2.1.2",
Name: "markupsafe",
Version: "2.1.2",
Indirect: true,
},
{
ID: "werkzeug@2.2.3",
Name: "werkzeug",
Version: "2.2.3",
Indirect: true,
DependsOn: []string{
"markupsafe@2.1.2",
},
},
},
},
},
},
},
{
name: "no pyproject.toml",
dir: "testdata/no-pyproject",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.Poetry,
FilePath: "poetry.lock",
Libraries: []types.Package{
{
ID: "click@8.1.3",
Name: "click",
Version: "8.1.3",
DependsOn: []string{
"colorama@0.4.6",
},
},
{
ID: "colorama@0.4.6",
Name: "colorama",
Version: "0.4.6",
},
},
},
},
},
},
{
name: "broken poetry.lock",
dir: "testdata/sad",
wantErr: "unable to parse poetry.lock",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a, err := newPoetryAnalyzer(analyzer.AnalyzerOptions{})
require.NoError(t, err)
got, err := a.PostAnalyze(context.Background(), analyzer.PostAnalysisInput{
FS: os.DirFS(tt.dir),
})
if tt.wantErr != "" {
assert.ErrorContains(t, err, tt.wantErr)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -0,0 +1,164 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "flask"
version = "1.0.3"
description = "A simple framework for building complex web applications."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "Flask-1.0.3-py2.py3-none-any.whl", hash = "sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61"},
{file = "Flask-1.0.3.tar.gz", hash = "sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3"},
]
[package.dependencies]
click = ">=5.1"
itsdangerous = ">=0.24"
Jinja2 = ">=2.10"
Werkzeug = ">=0.14"
[package.extras]
dev = ["coverage", "pallets-sphinx-themes", "pytest (>=3)", "sphinx", "sphinxcontrib-log-cabinet", "tox"]
docs = ["pallets-sphinx-themes", "sphinx", "sphinxcontrib-log-cabinet"]
dotenv = ["python-dotenv"]
[[package]]
name = "itsdangerous"
version = "2.1.2"
description = "Safely pass data to untrusted environments and back."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
]
[[package]]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "markupsafe"
version = "2.1.2"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"},
{file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"},
{file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"},
{file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"},
{file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"},
{file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"},
{file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"},
{file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"},
{file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"},
{file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"},
{file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"},
{file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"},
{file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"},
{file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"},
{file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"},
{file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"},
{file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"},
{file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"},
{file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"},
{file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"},
{file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"},
{file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"},
{file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"},
{file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"},
{file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"},
{file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"},
]
[[package]]
name = "werkzeug"
version = "2.2.3"
description = "The comprehensive WSGI web application library."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "Werkzeug-2.2.3-py3-none-any.whl", hash = "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612"},
{file = "Werkzeug-2.2.3.tar.gz", hash = "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe"},
]
[package.dependencies]
MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "c84861cc8679600635c65a32b5079dbfdf0c615c25a7db3d94c23156df8c56e9"

View File

@@ -0,0 +1,14 @@
[tool.poetry]
name = "example"
version = "0.1.0"
description = "My Hello World Example"
[tool.poetry.dependencies]
python = "^3.9"
flask = "^1.0"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

View File

@@ -0,0 +1,33 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "c84861cc8679600635c65a32b5079dbfdf0c615c25a7db3d94c23156df8c56e9"

View File

@@ -0,0 +1 @@
[

View File

@@ -54,7 +54,7 @@ func (a ubuntuESMAnalyzer) Required(filePath string, _ os.FileInfo) bool {
}
func (a ubuntuESMAnalyzer) Type() analyzer.Type {
return analyzer.TypeUbuntu
return analyzer.TypeUbuntuESM
}
func (a ubuntuESMAnalyzer) Version() int {

View File

@@ -24,7 +24,7 @@ func init() {
}
const (
analyzerVersion = 3
analyzerVersion = 4
statusFile = "var/lib/dpkg/status"
statusDir = "var/lib/dpkg/status.d/"
@@ -168,14 +168,19 @@ func (a dpkgAnalyzer) parseDpkgPkg(scanner *bufio.Scanner) (pkg *types.Package)
if name == "" || version == "" || !isInstalled {
return nil
} else if !debVersion.Valid(version) {
log.Logger.Warnf("Invalid Version Found : OS %s, Package %s, Version %s", "debian", name, version)
}
v, err := debVersion.NewVersion(version)
if err != nil {
log.Logger.Warnf("Invalid Version: OS %s, Package %s, Version %s", "debian", name, version)
return nil
}
pkg = &types.Package{
ID: a.pkgID(name, version),
Name: name,
Version: version,
Epoch: v.Epoch(),
Version: v.Version(),
Release: v.Revision(),
DependsOn: dependencies, // Will be consolidated later
Maintainer: maintainer,
}
@@ -194,12 +199,15 @@ func (a dpkgAnalyzer) parseDpkgPkg(scanner *bufio.Scanner) (pkg *types.Package)
sourceVersion = version
}
if !debVersion.Valid(sourceVersion) {
log.Logger.Warnf("Invalid Version Found : OS %s, Package %s, Version %s", "debian", sourceName, sourceVersion)
return pkg
sv, err := debVersion.NewVersion(sourceVersion)
if err != nil {
log.Logger.Warnf("Invalid SourceVersion Found : OS %s, Package %s, Version %s", "debian", sourceName, sourceVersion)
return nil
}
pkg.SrcName = sourceName
pkg.SrcVersion = sourceVersion
pkg.SrcVersion = sv.Version()
pkg.SrcEpoch = sv.Epoch()
pkg.SrcRelease = sv.Revision()
return pkg
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,14 +3,13 @@ GEM
specs:
actioncable (5.2.3)
actionpack (= 5.2.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionpack (5.2.3)
PLATFORMS
ruby
DEPENDENCIES
dotenv (~> 2.7)
actioncable (~> 5.0)
BUNDLED WITH
1.17.2

View File

@@ -4,7 +4,9 @@ import (
"context"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
@@ -21,7 +23,9 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/log"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/mapfs"
"github.com/aquasecurity/trivy/pkg/semaphore"
"github.com/aquasecurity/trivy/pkg/syncx"
)
type Artifact struct {
@@ -49,6 +53,7 @@ func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (a
a, err := analyzer.NewAnalyzerGroup(analyzer.AnalyzerOptions{
Group: opt.AnalyzerGroup,
Slow: opt.Slow,
FilePatterns: opt.FilePatterns,
DisabledAnalyzers: opt.DisabledAnalyzers,
SecretScannerOption: opt.SecretScannerOption,
@@ -234,7 +239,7 @@ func (a Artifact) inspect(ctx context.Context, missingImage string, layerKeys, b
layerInfo, err := a.inspectLayer(ctx, layer, disabledAnalyers)
if err != nil {
errCh <- xerrors.Errorf("failed to analyze layer: %s : %w", layerInfo.DiffID, err)
errCh <- xerrors.Errorf("failed to analyze layer (%s): %w", layer.DiffID, err)
return
}
if err = a.cache.PutBlob(layerKey, layerInfo); err != nil {
@@ -282,11 +287,25 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable
result := analyzer.NewAnalysisResult()
limit := semaphore.New(a.artifactOption.Slow)
// Prepare filesystem for post analysis
files := new(syncx.Map[analyzer.Type, *mapfs.FS])
tmpDir, err := os.MkdirTemp("", "layers-*")
if err != nil {
return types.BlobInfo{}, xerrors.Errorf("mkdir temp error: %w", err)
}
defer os.RemoveAll(tmpDir)
// Walk a tar layer
opqDirs, whFiles, err := a.walker.Walk(rc, func(filePath string, info os.FileInfo, opener analyzer.Opener) error {
if err = a.analyzer.AnalyzeFile(ctx, &wg, limit, result, "", filePath, info, opener, disabled, opts); err != nil {
return xerrors.Errorf("failed to analyze %s: %w", filePath, err)
}
// Build filesystem for post analysis
if err = a.buildFS(tmpDir, filePath, info, opener, files); err != nil {
return xerrors.Errorf("failed to build filesystem: %w", err)
}
return nil
})
if err != nil {
@@ -296,6 +315,11 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable
// Wait for all the goroutine to finish.
wg.Wait()
// Post-analysis
if err = a.analyzer.PostAnalyze(ctx, files, result, opts); err != nil {
return types.BlobInfo{}, xerrors.Errorf("post analysis error: %w", err)
}
// Sort the analysis result for consistent results
result.Sort()
@@ -326,6 +350,55 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable
return blobInfo, nil
}
// buildFS creates filesystem for post analysis
func (a Artifact) buildFS(tmpDir, filePath string, info os.FileInfo, opener analyzer.Opener,
files *syncx.Map[analyzer.Type, *mapfs.FS]) error {
// Get all post-analyzers that want to analyze the file
atypes := a.analyzer.RequiredPostAnalyzers(filePath, info)
if len(atypes) == 0 {
return nil
}
// Create a temporary file to which the file in the layer will be copied
// so that all the files will not be loaded into memory
f, err := os.CreateTemp(tmpDir, "layer-file-*")
if err != nil {
return xerrors.Errorf("create temp error: %w", err)
}
defer f.Close()
// Open a file in the layer
r, err := opener()
if err != nil {
return xerrors.Errorf("file open error: %w", err)
}
defer r.Close()
// Copy file content into the temporary file
if _, err = io.Copy(f, r); err != nil {
return xerrors.Errorf("copy error: %w", err)
}
if err = os.Chmod(f.Name(), info.Mode()); err != nil {
return xerrors.Errorf("chmod error: %w", err)
}
// Create fs.FS for each post-analyzer that wants to analyze the current file
for _, at := range atypes {
fsys, _ := files.LoadOrStore(at, mapfs.New())
if dir := filepath.Dir(filePath); dir != "." {
if err := fsys.MkdirAll(dir, os.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) {
return xerrors.Errorf("mapfs mkdir error: %w", err)
}
}
err = fsys.WriteFile(filePath, f.Name())
if err != nil {
return xerrors.Errorf("mapfs write error: %w", err)
}
}
return nil
}
func (a Artifact) diffIDs(configFile *v1.ConfigFile) []string {
if configFile == nil {
return nil

File diff suppressed because it is too large Load Diff

View File

@@ -59,7 +59,7 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
{
Args: cache.ArtifactCachePutBlobArgs{
BlobID: "sha256:8c90c68f385a8067778a200fd3e56e257d4d6dd563e519a7be65902ee0b6e861",
BlobID: "sha256:9c23872047046e145f49fb5533b63ace0cbf819f5b68e33f69f4e9bbab4c517e",
BlobInfo: types.BlobInfo{
SchemaVersion: types.BlobJSONSchemaVersion,
OS: types.OS{
@@ -94,9 +94,9 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
want: types.ArtifactReference{
Name: "test/image:10",
Type: types.ArtifactCycloneDX,
ID: "sha256:8c90c68f385a8067778a200fd3e56e257d4d6dd563e519a7be65902ee0b6e861",
ID: "sha256:9c23872047046e145f49fb5533b63ace0cbf819f5b68e33f69f4e9bbab4c517e",
BlobIDs: []string{
"sha256:8c90c68f385a8067778a200fd3e56e257d4d6dd563e519a7be65902ee0b6e861",
"sha256:9c23872047046e145f49fb5533b63ace0cbf819f5b68e33f69f4e9bbab4c517e",
},
},
},

View File

@@ -4,6 +4,8 @@ import (
"context"
"crypto/sha256"
"encoding/json"
"errors"
"io/fs"
"os"
"path/filepath"
"strings"
@@ -19,7 +21,9 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/mapfs"
"github.com/aquasecurity/trivy/pkg/semaphore"
"github.com/aquasecurity/trivy/pkg/syncx"
)
type Artifact struct {
@@ -40,6 +44,7 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, opt artifact.Option) (a
a, err := analyzer.NewAnalyzerGroup(analyzer.AnalyzerOptions{
Group: opt.AnalyzerGroup,
Slow: opt.Slow,
FilePatterns: opt.FilePatterns,
DisabledAnalyzers: opt.DisabledAnalyzers,
SecretScannerOption: opt.SecretScannerOption,
@@ -119,20 +124,29 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error)
var wg sync.WaitGroup
result := analyzer.NewAnalysisResult()
limit := semaphore.New(a.artifactOption.Slow)
opts := analyzer.AnalysisOptions{Offline: a.artifactOption.Offline}
// Prepare filesystem for post analysis
files := new(syncx.Map[analyzer.Type, *mapfs.FS])
err := a.walker.Walk(a.rootPath, func(filePath string, info os.FileInfo, opener analyzer.Opener) error {
directory := a.rootPath
dir := a.rootPath
// When the directory is the same as the filePath, a file was given
// instead of a directory, rewrite the file path and directory in this case.
if filePath == "." {
directory, filePath = filepath.Split(a.rootPath)
dir, filePath = filepath.Split(a.rootPath)
}
opts := analyzer.AnalysisOptions{Offline: a.artifactOption.Offline}
if err := a.analyzer.AnalyzeFile(ctx, &wg, limit, result, directory, filePath, info, opener, nil, opts); err != nil {
if err := a.analyzer.AnalyzeFile(ctx, &wg, limit, result, dir, filePath, info, opener, nil, opts); err != nil {
return xerrors.Errorf("analyze file (%s): %w", filePath, err)
}
// Build filesystem for post analysis
if err := a.buildFS(dir, filePath, info, files); err != nil {
return xerrors.Errorf("failed to build filesystem: %w", err)
}
return nil
})
if err != nil {
@@ -142,6 +156,11 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error)
// Wait for all the goroutine to finish.
wg.Wait()
// Post-analysis
if err = a.analyzer.PostAnalyze(ctx, files, result, opts); err != nil {
return types.ArtifactReference{}, xerrors.Errorf("post analysis error: %w", err)
}
// Sort the analysis result for consistent results
result.Sort()
@@ -206,3 +225,26 @@ func (a Artifact) calcCacheKey(blobInfo types.BlobInfo) (string, error) {
return cacheKey, nil
}
// buildFS creates filesystem for post analysis
func (a Artifact) buildFS(dir, filePath string, info os.FileInfo, files *syncx.Map[analyzer.Type, *mapfs.FS]) error {
// Get all post-analyzers that want to analyze the file
atypes := a.analyzer.RequiredPostAnalyzers(filePath, info)
if len(atypes) == 0 {
return nil
}
// Create fs.FS for each post-analyzer that wants to analyze the current file
for _, at := range atypes {
mfs, _ := files.LoadOrStore(at, mapfs.New())
if d := filepath.Dir(filePath); d != "." {
if err := mfs.MkdirAll(d, os.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) {
return xerrors.Errorf("mapfs mkdir error: %w", err)
}
}
if err := mfs.WriteFile(filePath, filepath.Join(dir, filePath)); err != nil {
return xerrors.Errorf("mapfs write error: %w", err)
}
}
return nil
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More