mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-09 06:10:47 -08:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc0836623c | ||
|
|
23cdac02ee | ||
|
|
302c8ae24c | ||
|
|
34120f4201 | ||
|
|
e399ed8439 | ||
|
|
ef7b762e48 | ||
|
|
00daebc161 | ||
|
|
98d1031552 | ||
|
|
b791362871 | ||
|
|
719fdb1b11 | ||
|
|
3ff5699b4b | ||
|
|
33909d9df3 | ||
|
|
d85a3e087b | ||
|
|
551899c24e | ||
|
|
3aaa2cfb75 | ||
|
|
9d1300c3e7 | ||
|
|
793cc43d4c | ||
|
|
6a3294e476 | ||
|
|
e9dc21d88a | ||
|
|
12976d42df | ||
|
|
1dc2b349c6 | ||
|
|
92eaf636ca | ||
|
|
9af436b999 | ||
|
|
88ee68d0c6 | ||
|
|
75c96bd968 | ||
|
|
baea3997d2 | ||
|
|
7ca0db17ea | ||
|
|
866999e454 | ||
|
|
b7bfb9a207 | ||
|
|
9badef27ac | ||
|
|
d856595b8e | ||
|
|
fe7c26a741 | ||
|
|
f251dfc5ce | ||
|
|
9be8062c10 | ||
|
|
370098dbf4 | ||
|
|
32acd293fd | ||
|
|
aa8e185e03 | ||
|
|
86603bb9c5 | ||
|
|
7b1e173f51 | ||
|
|
85d5d61bc7 | ||
|
|
2c17260ba8 | ||
|
|
c54f1aa8f0 | ||
|
|
625ea58122 | ||
|
|
623c7f9432 | ||
|
|
d291c34f51 | ||
|
|
6cac6c917f | ||
|
|
12b563b974 | ||
|
|
72a14c67af | ||
|
|
4c01d73fb7 | ||
|
|
10dd5d1a95 | ||
|
|
439c541fd3 | ||
|
|
200e04a767 | ||
|
|
a533ca87e6 | ||
|
|
4bccbe6e1c | ||
|
|
d0562085df | ||
|
|
f5e65749b4 | ||
|
|
d3da459d45 |
2
.github/workflows/canary.yaml
vendored
2
.github/workflows/canary.yaml
vendored
@@ -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}}
|
||||
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -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}}
|
||||
|
||||
4
.github/workflows/reusable-release.yaml
vendored
4
.github/workflows/reusable-release.yaml
vendored
@@ -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.
|
||||
|
||||
2
.github/workflows/semantic-pr.yaml
vendored
2
.github/workflows/semantic-pr.yaml
vendored
@@ -69,7 +69,7 @@ jobs:
|
||||
java
|
||||
go
|
||||
c
|
||||
c++
|
||||
c\+\+
|
||||
elixir
|
||||
dart
|
||||
|
||||
|
||||
9
.github/workflows/test.yaml
vendored
9
.github/workflows/test.yaml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
93
docs/docs/misconfiguration/custom/schema.md
Normal file
93
docs/docs/misconfiguration/custom/schema.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
28
docs/docs/references/cli/completion.md
Normal file
28
docs/docs/references/cli/completion.md
Normal 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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 | - |
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
55
docs/docs/vulnerability/languages/python.md
Normal file
55
docs/docs/vulnerability/languages/python.md
Normal 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.
|
||||
66
docs/tutorials/shell/shell-completion.md
Normal file
66
docs/tutorials/shell/shell-completion.md
Normal 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
159
go.mod
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 | `` |
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
29
integration/testdata/fixtures/fs/pipenv/Pipfile.lock
generated
vendored
Normal 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
50
integration/testdata/fixtures/fs/poetry/poetry.lock
generated
vendored
Normal 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"
|
||||
15
integration/testdata/fixtures/fs/poetry/pyproject.toml
vendored
Normal file
15
integration/testdata/fixtures/fs/poetry/pyproject.toml
vendored
Normal 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"
|
||||
4
integration/testdata/gomod-skip.json.golden
vendored
4
integration/testdata/gomod-skip.json.golden
vendored
@@ -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",
|
||||
|
||||
5
integration/testdata/gomod.json.golden
vendored
5
integration/testdata/gomod.json.golden
vendored
@@ -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
129
integration/testdata/pipenv.json.golden
vendored
Normal 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
97
integration/testdata/poetry.json.golden
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
11
mkdocs.yml
11
mkdocs.yml
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
75
pkg/compliance/spec/custom.go
Normal file
75
pkg/compliance/spec/custom.go
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type ScannerOption struct {
|
||||
HelmFileValues []string
|
||||
HelmStringValues []string
|
||||
TerraformTFVars []string
|
||||
K8sVersion string
|
||||
}
|
||||
|
||||
func (o *ScannerOption) Sort() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
TypeRedHatBase Type = "redhat"
|
||||
TypeSUSE Type = "suse"
|
||||
TypeUbuntu Type = "ubuntu"
|
||||
TypeUbuntuESM Type = "ubuntu-esm"
|
||||
|
||||
// OS Package
|
||||
TypeApk Type = "apk"
|
||||
|
||||
@@ -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}}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
7
pkg/fanal/analyzer/language/golang/mod/testdata/merge/go.mod
vendored
Normal file
7
pkg/fanal/analyzer/language/golang/mod/testdata/merge/go.mod
vendored
Normal 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
|
||||
4
pkg/fanal/analyzer/language/golang/mod/testdata/merge/go.sum
vendored
Normal file
4
pkg/fanal/analyzer/language/golang/mod/testdata/merge/go.sum
vendored
Normal 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=
|
||||
5
pkg/fanal/analyzer/language/golang/mod/testdata/no_gosum/go.mod
vendored
Normal file
5
pkg/fanal/analyzer/language/golang/mod/testdata/no_gosum/go.mod
vendored
Normal 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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
invalid
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 != "" {
|
||||
|
||||
BIN
pkg/fanal/analyzer/language/java/jar/testdata/java-db/trivy-java.db
vendored
Normal file
BIN
pkg/fanal/analyzer/language/java/jar/testdata/java-db/trivy-java.db
vendored
Normal file
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
}
|
||||
|
||||
144
pkg/fanal/analyzer/language/python/poetry/poetry_test.go
Normal file
144
pkg/fanal/analyzer/language/python/poetry/poetry_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
164
pkg/fanal/analyzer/language/python/poetry/testdata/happy/poetry.lock
generated
vendored
Normal file
164
pkg/fanal/analyzer/language/python/poetry/testdata/happy/poetry.lock
generated
vendored
Normal 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"
|
||||
14
pkg/fanal/analyzer/language/python/poetry/testdata/happy/pyproject.toml
vendored
Normal file
14
pkg/fanal/analyzer/language/python/poetry/testdata/happy/pyproject.toml
vendored
Normal 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"
|
||||
33
pkg/fanal/analyzer/language/python/poetry/testdata/no-pyproject/poetry.lock
generated
vendored
Normal file
33
pkg/fanal/analyzer/language/python/poetry/testdata/no-pyproject/poetry.lock
generated
vendored
Normal 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"
|
||||
1
pkg/fanal/analyzer/language/python/poetry/testdata/sad/poetry.lock
generated
vendored
Normal file
1
pkg/fanal/analyzer/language/python/poetry/testdata/sad/poetry.lock
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
5
pkg/fanal/analyzer/testdata/app/Gemfile.lock
vendored
5
pkg/fanal/analyzer/testdata/app/Gemfile.lock
vendored
@@ -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
|
||||
@@ -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
@@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user