mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-10 14:50:50 -08:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
800473a8bc | ||
|
|
e6ab389f9e | ||
|
|
6614398ab4 | ||
|
|
1dc6fee781 | ||
|
|
3357ed096b | ||
|
|
1064636b3d | ||
|
|
60b7ef5a55 | ||
|
|
497c955a4b | ||
|
|
5d54310d76 | ||
|
|
44cf1e2f57 | ||
|
|
743b4b0d97 | ||
|
|
6de43855f8 | ||
|
|
9a0ceef166 | ||
|
|
0501b46d48 | ||
|
|
ee3004d292 | ||
|
|
5c8e604f56 | ||
|
|
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 | ||
|
|
7f8868b7d8 | ||
|
|
364379b7b2 | ||
|
|
0205475fa9 |
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.6
|
||||
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.6
|
||||
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.6
|
||||
with:
|
||||
path: dist/
|
||||
# use 'github.sha' to create a unique cache folder for each run.
|
||||
|
||||
8
.github/workflows/roadmap.yaml
vendored
8
.github/workflows/roadmap.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# 'kind/feature' AND 'priority/backlog' labels -> 'Backlog' column
|
||||
- uses: actions/add-to-project@v0.4.0 # add new issue to project
|
||||
- uses: actions/add-to-project@v0.4.1 # add new issue to project
|
||||
with:
|
||||
project-url: https://github.com/orgs/aquasecurity/projects/25
|
||||
github-token: ${{ secrets.ORG_PROJECT_TOKEN }}
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
field-values: Backlog
|
||||
|
||||
# 'kind/feature' AND 'priority/important-longterm' labels -> 'Important (long-term)' column
|
||||
- uses: actions/add-to-project@v0.4.0 # add new issue to project
|
||||
- uses: actions/add-to-project@v0.4.1 # add new issue to project
|
||||
with:
|
||||
project-url: https://github.com/orgs/aquasecurity/projects/25
|
||||
github-token: ${{ secrets.ORG_PROJECT_TOKEN }}
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
field-values: Important (long-term)
|
||||
|
||||
# 'kind/feature' AND 'priority/important-soon' labels -> 'Important (soon)' column
|
||||
- uses: actions/add-to-project@v0.4.0 # add new issue to project
|
||||
- uses: actions/add-to-project@v0.4.1 # add new issue to project
|
||||
with:
|
||||
project-url: https://github.com/orgs/aquasecurity/projects/25
|
||||
github-token: ${{ secrets.ORG_PROJECT_TOKEN }}
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
field-values: Important (soon)
|
||||
|
||||
# 'kind/feature' AND 'priority/critical-urgent' labels -> 'Urgent' column
|
||||
- uses: actions/add-to-project@v0.4.0 # add new issue to project
|
||||
- uses: actions/add-to-project@v0.4.1 # add new issue to project
|
||||
with:
|
||||
project-url: https://github.com/orgs/aquasecurity/projects/25
|
||||
github-token: ${{ secrets.ORG_PROJECT_TOKEN }}
|
||||
|
||||
4
.github/workflows/scan.yaml
vendored
4
.github/workflows/scan.yaml
vendored
@@ -13,11 +13,11 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run Trivy vulnerability scanner and create GitHub issues
|
||||
uses: knqyf263/trivy-issue-action@v0.0.4
|
||||
uses: knqyf263/trivy-issue-action@v0.0.5
|
||||
with:
|
||||
assignee: knqyf263
|
||||
severity: CRITICAL
|
||||
skip-dirs: integration,examples
|
||||
skip-dirs: integration,examples,pkg
|
||||
label: kind/security
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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.2
|
||||
RUN apk --no-cache add ca-certificates git
|
||||
|
||||
# binaries were created with GoReleaser
|
||||
|
||||
4
Makefile
4
Makefile
@@ -16,6 +16,8 @@ EXAMPLE_MODULES := $(patsubst %.go,%.wasm,$(EXAMPLE_MODULE_SRCS))
|
||||
MKDOCS_IMAGE := aquasec/mkdocs-material:dev
|
||||
MKDOCS_PORT := 8000
|
||||
|
||||
export CGO_ENABLED := 0
|
||||
|
||||
u := $(if $(update),-u)
|
||||
|
||||
# Tools
|
||||
@@ -130,4 +132,4 @@ mkdocs-serve:
|
||||
# Generate JSON marshaler/unmarshaler for TinyGo/WebAssembly as TinyGo doesn't support encoding/json.
|
||||
.PHONY: easyjson
|
||||
easyjson: $(GOBIN)/easyjson
|
||||
easyjson pkg/module/serialize/types.go
|
||||
easyjson pkg/module/serialize/types.go
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
TRIVY_VERSION=$(find ../dist/ -type f -name "*64bit.rpm" -printf "%f\n" | head -n1 | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p')
|
||||
TRIVY_VERSION=$(find dist/ -type f -name "*64bit.rpm" -printf "%f\n" | head -n1 | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p')
|
||||
|
||||
function create_rpm_repo () {
|
||||
version=$1
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -42,6 +42,11 @@ For example, to download the Kubernetes Trivy plugin you can execute the followi
|
||||
```bash
|
||||
$ trivy plugin install github.com/aquasecurity/trivy-plugin-kubectl
|
||||
```
|
||||
Also, Trivy plugin can be installed from a local archive:
|
||||
```bash
|
||||
$ trivy plugin install myplugin.tar.gz
|
||||
```
|
||||
|
||||
## Using Plugins
|
||||
Once the plugin is installed, Trivy will load all available plugins in the cache on the start of the next Trivy execution.
|
||||
A plugin will be made in the Trivy CLI based on the plugin name.
|
||||
@@ -162,6 +167,21 @@ When the plugin is called via Trivy CLI, `bin` command will be executed.
|
||||
|
||||
The plugin is responsible for handling flags and arguments. Any arguments are passed to the plugin from the `trivy` command.
|
||||
|
||||
A plugin should be archived `*.tar.gz`.
|
||||
|
||||
```bash
|
||||
$ tar -czvf myplugin.tar.gz plugin.yaml script.py
|
||||
plugin.yaml
|
||||
script.py
|
||||
|
||||
$ trivy plugin install myplugin.tar.gz
|
||||
2023-03-03T19:04:42.026+0600 INFO Installing the plugin from myplugin.tar.gz...
|
||||
2023-03-03T19:04:42.026+0600 INFO Loading the plugin metadata...
|
||||
|
||||
$ trivy myplugin
|
||||
Hello from Trivy demo plugin!
|
||||
```
|
||||
|
||||
## Example
|
||||
https://github.com/aquasecurity/trivy-plugin-kubectl
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@ SPDXID: SPDXRef-DOCUMENT
|
||||
DocumentName: alpine:3.15
|
||||
DocumentNamespace: http://aquasecurity.github.io/trivy/container_image/alpine:3.15-bebf6b19-a94c-4e2c-af44-065f63923f48
|
||||
Creator: Organization: aquasecurity
|
||||
Creator: Tool: trivy
|
||||
Creator: Tool: trivy-0.38.1
|
||||
Created: 2022-04-28T07:32:57.142806Z
|
||||
|
||||
##### Package: zlib
|
||||
@@ -167,7 +167,7 @@ $ cat result.spdx.json | jq .
|
||||
"creationInfo": {
|
||||
"created": "2022-04-28T08:16:55.328255Z",
|
||||
"creators": [
|
||||
"Tool: trivy",
|
||||
"Tool: trivy-0.38.1",
|
||||
"Organization: aquasecurity"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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
|
||||
```
|
||||
167
go.mod
167
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-20230302111817-e4068021315b
|
||||
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,34 +20,34 @@ 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-20230130194604-b1b12e703cf9
|
||||
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.212
|
||||
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.19
|
||||
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
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/go-containerregistry v0.12.0
|
||||
github.com/google/go-containerregistry v0.13.0
|
||||
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,45 +61,43 @@ 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
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/exp v0.0.0-20220823124025-807a23277127
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
||||
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.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/driver/sqlite v1.4.4 // 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
|
||||
@@ -118,9 +116,9 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.6 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.7 // 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
|
||||
@@ -131,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
|
||||
@@ -161,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
|
||||
@@ -175,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
|
||||
@@ -203,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
|
||||
@@ -235,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,36 +256,39 @@ require (
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.15.12 // indirect
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20221030142135-919c8a52f04f
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20230201142403-697bc51b3948
|
||||
github.com/knqyf263/nested v0.0.1
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
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.16 // 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/buildkit v0.11.4
|
||||
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
|
||||
@@ -312,24 +312,25 @@ require (
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // 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.3.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,15 +364,15 @@ 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
|
||||
@@ -380,12 +380,11 @@ require (
|
||||
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.4.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.20.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
oras.land/oras-go v1.2.0 // 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)
|
||||
|
||||
2
integration/testdata/conda-spdx.json.golden
vendored
2
integration/testdata/conda-spdx.json.golden
vendored
@@ -3,7 +3,7 @@
|
||||
"creationInfo": {
|
||||
"created": "2023-01-08T23:58:16.700785648Z",
|
||||
"creators": [
|
||||
"Tool: trivy",
|
||||
"Tool: trivy-dev",
|
||||
"Organization: aquasecurity"
|
||||
]
|
||||
},
|
||||
|
||||
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"
|
||||
@@ -3,7 +3,7 @@
|
||||
"creationInfo": {
|
||||
"created": "2022-09-13T13:27:55.874784Z",
|
||||
"creators": [
|
||||
"Tool: trivy",
|
||||
"Tool: trivy-dev",
|
||||
"Organization: aquasecurity"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDXID: SPDXRef-DOCUMENT
|
||||
DocumentName: integration/testdata/fixtures/images/centos-7.tar.gz
|
||||
DocumentNamespace: http://aquasecurity.github.io/trivy/container_image/integration/testdata/fixtures/images/centos-7.tar.gz-6a2c050f-bc12-46dc-b2df-1f4e3e0b5e1d
|
||||
Creator: Organization: aquasecurity
|
||||
Creator: Tool: trivy
|
||||
Creator: Tool: trivy-dev
|
||||
Created: 2022-09-13T13:24:58.796907Z
|
||||
|
||||
##### Package: integration/testdata/fixtures/images/centos-7.tar.gz
|
||||
|
||||
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 {
|
||||
|
||||
@@ -9,12 +9,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
awsScanner "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
awsScanner "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws"
|
||||
"github.com/aquasecurity/trivy-db/pkg/metadata"
|
||||
awscommands "github.com/aquasecurity/trivy/pkg/cloud/aws/commands"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/artifact"
|
||||
@@ -25,6 +24,7 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/module"
|
||||
"github.com/aquasecurity/trivy/pkg/plugin"
|
||||
"github.com/aquasecurity/trivy/pkg/policy"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
type VersionInfo struct {
|
||||
Version string `json:",omitempty"`
|
||||
VulnerabilityDB *metadata.Metadata `json:",omitempty"`
|
||||
PolicyBundle *policy.Metadata `json:",omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -151,9 +152,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 +162,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 +223,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 +292,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 +354,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 +404,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 +512,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 +554,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 +566,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 +715,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 +734,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 +758,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 +797,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 +862,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 +932,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(),
|
||||
@@ -1047,11 +1085,18 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
|
||||
}
|
||||
}
|
||||
|
||||
var pbMeta *policy.Metadata
|
||||
pc, err := policy.NewClient(cacheDir, false)
|
||||
if pc != nil && err == nil {
|
||||
pbMeta, _ = pc.GetMetadata()
|
||||
}
|
||||
|
||||
switch outputFormat {
|
||||
case "json":
|
||||
b, _ := json.Marshal(VersionInfo{
|
||||
Version: version,
|
||||
VulnerabilityDB: dbMeta,
|
||||
PolicyBundle: pbMeta,
|
||||
})
|
||||
fmt.Fprintln(outputWriter, string(b))
|
||||
default:
|
||||
@@ -1064,6 +1109,13 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
|
||||
DownloadedAt: %s
|
||||
`, dbMeta.Version, dbMeta.UpdatedAt.UTC(), dbMeta.NextUpdate.UTC(), dbMeta.DownloadedAt.UTC())
|
||||
}
|
||||
|
||||
if pbMeta != nil {
|
||||
output += fmt.Sprintf(`Policy Bundle:
|
||||
Digest: %s
|
||||
DownloadedAt: %s
|
||||
`, pbMeta.Digest, pbMeta.DownloadedAt.UTC())
|
||||
}
|
||||
fmt.Fprintf(outputWriter, output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ Vulnerability DB:
|
||||
UpdatedAt: 2022-03-02 06:07:07.99504083 +0000 UTC
|
||||
NextUpdate: 2022-03-02 12:07:07.99504023 +0000 UTC
|
||||
DownloadedAt: 2022-03-02 10:03:38.383312 +0000 UTC
|
||||
Policy Bundle:
|
||||
Digest: sha256:19a017cdc798631ad42f6f4dce823d77b2989128f0e1a7f9bc83ae3c59024edd
|
||||
DownloadedAt: 2023-03-02 01:06:08.191725 +0000 UTC
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -62,8 +65,11 @@ Vulnerability DB:
|
||||
UpdatedAt: 2022-03-02 06:07:07.99504083 +0000 UTC
|
||||
NextUpdate: 2022-03-02 12:07:07.99504023 +0000 UTC
|
||||
DownloadedAt: 2022-03-02 10:03:38.383312 +0000 UTC
|
||||
Policy Bundle:
|
||||
Digest: sha256:19a017cdc798631ad42f6f4dce823d77b2989128f0e1a7f9bc83ae3c59024edd
|
||||
DownloadedAt: 2023-03-02 01:06:08.191725 +0000 UTC
|
||||
`
|
||||
jsonOutput := `{"Version":"test","VulnerabilityDB":{"Version":2,"NextUpdate":"2022-03-02T12:07:07.99504023Z","UpdatedAt":"2022-03-02T06:07:07.99504083Z","DownloadedAt":"2022-03-02T10:03:38.383312Z"}}
|
||||
jsonOutput := `{"Version":"test","VulnerabilityDB":{"Version":2,"NextUpdate":"2022-03-02T12:07:07.99504023Z","UpdatedAt":"2022-03-02T06:07:07.99504083Z","DownloadedAt":"2022-03-02T10:03:38.383312Z"},"PolicyBundle":{"Digest":"sha256:19a017cdc798631ad42f6f4dce823d77b2989128f0e1a7f9bc83ae3c59024edd","DownloadedAt":"2023-03-01T17:06:08.191725-08:00"}}
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
@@ -24,12 +23,13 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/javadb"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/module"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
pkgReport "github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/result"
|
||||
"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)
|
||||
}
|
||||
@@ -300,22 +303,17 @@ func (r *runner) Report(opts flag.Options, report types.Report) error {
|
||||
}
|
||||
|
||||
func (r *runner) initDB(opts flag.Options) error {
|
||||
if err := r.initJavaDB(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When scanning config files or running as client mode, it doesn't need to download the vulnerability database.
|
||||
if opts.ServerAddr != "" || !opts.Scanners.Enabled(types.VulnerabilityScanner) {
|
||||
return nil
|
||||
}
|
||||
noProgress := opts.Quiet || opts.NoProgress
|
||||
|
||||
// Java DB
|
||||
javadb.Init(opts.CacheDir, opts.SkipJavaDBUpdate, noProgress, opts.Insecure)
|
||||
if opts.DownloadJavaDBOnly {
|
||||
if err := javadb.Update(); err != nil {
|
||||
return xerrors.Errorf("Java DB error: %w", err)
|
||||
}
|
||||
return SkipScan
|
||||
}
|
||||
|
||||
// download the database file
|
||||
noProgress := opts.Quiet || opts.NoProgress
|
||||
if err := operation.DownloadDB(opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.Insecure, opts.SkipDBUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -332,6 +330,31 @@ func (r *runner) initDB(opts flag.Options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runner) initJavaDB(opts flag.Options) error {
|
||||
// When running as server mode, it doesn't need to download the Java database.
|
||||
if opts.Listen != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If vulnerability scanning and SBOM generation are disabled, it doesn't need to download the Java database.
|
||||
if !opts.Scanners.Enabled(types.VulnerabilityScanner) &&
|
||||
!slices.Contains(report.SupportedSBOMFormats, opts.Format) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the Java DB
|
||||
noProgress := opts.Quiet || opts.NoProgress
|
||||
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)
|
||||
}
|
||||
return SkipScan
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runner) initCache(opts flag.Options) error {
|
||||
// Skip initializing cache when custom cache is passed
|
||||
if r.cache != nil {
|
||||
@@ -346,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()
|
||||
@@ -434,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
|
||||
@@ -470,6 +494,15 @@ func disabledAnalyzers(opts flag.Options) []analyzer.Type {
|
||||
analyzers = append(analyzers, analyzer.TypeLicenseFile)
|
||||
}
|
||||
|
||||
// Parsing jar files requires Java-db client
|
||||
// But we don't create client if vulnerability analysis is disabled and SBOM format is not used
|
||||
// We need to disable jar analyzer to avoid errors
|
||||
// TODO disable all languages that don't contain license information for this case
|
||||
if opts.Scanners.Enabled(types.LicenseScanner) && !opts.Scanners.Enabled(types.VulnerabilityScanner) &&
|
||||
!slices.Contains(report.SupportedSBOMFormats, opts.Format) {
|
||||
analyzers = append(analyzers, analyzer.TypeJar)
|
||||
}
|
||||
|
||||
// Do not perform misconfiguration scanning on container image config
|
||||
// when it is not specified.
|
||||
if !opts.ImageConfigScanners.Enabled(types.MisconfigScanner) {
|
||||
@@ -500,8 +533,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +577,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...),
|
||||
@@ -552,6 +587,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
|
||||
HelmFileValues: opts.HelmFileValues,
|
||||
HelmStringValues: opts.HelmStringValues,
|
||||
TerraformTFVars: opts.TerraformTFVars,
|
||||
K8sVersion: opts.K8sVersion,
|
||||
DisableEmbeddedPolicies: disableEmbedded,
|
||||
}
|
||||
}
|
||||
@@ -618,12 +654,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)
|
||||
@@ -643,6 +677,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)
|
||||
}
|
||||
|
||||
1
pkg/commands/testdata/policy/metadata.json
vendored
Normal file
1
pkg/commands/testdata/policy/metadata.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"Digest":"sha256:19a017cdc798631ad42f6f4dce823d77b2989128f0e1a7f9bc83ae3c59024edd","DownloadedAt":"2023-03-01T17:06:08.191725-08:00"}
|
||||
@@ -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,50 @@ 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 {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
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("%q parse error: %w", path, 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.
@@ -29,6 +29,7 @@ func Test_nodePkgLibraryAnalyzer_Analyze(t *testing.T) {
|
||||
FilePath: "testdata/package.json",
|
||||
Libraries: []types.Package{
|
||||
{
|
||||
ID: "lodash@5.0.0",
|
||||
Name: "lodash",
|
||||
Version: "5.0.0",
|
||||
Licenses: []string{"MIT"},
|
||||
|
||||
@@ -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]interface{}, 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
|
||||
}
|
||||
|
||||
167
pkg/fanal/analyzer/language/python/poetry/poetry_test.go
Normal file
167
pkg/fanal/analyzer/language/python/poetry/poetry_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
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: "certifi@2022.12.7",
|
||||
Name: "certifi",
|
||||
Version: "2022.12.7",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "charset-normalizer@2.1.1",
|
||||
Name: "charset-normalizer",
|
||||
Version: "2.1.1",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "click@7.1.2",
|
||||
Name: "click",
|
||||
Version: "7.1.2",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "flask@1.1.4",
|
||||
Name: "flask",
|
||||
Version: "1.1.4",
|
||||
DependsOn: []string{
|
||||
"click@7.1.2",
|
||||
"itsdangerous@1.1.0",
|
||||
"jinja2@2.11.3",
|
||||
"werkzeug@1.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "idna@3.4",
|
||||
Name: "idna",
|
||||
Version: "3.4",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "itsdangerous@1.1.0",
|
||||
Name: "itsdangerous",
|
||||
Version: "1.1.0",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "jinja2@2.11.3",
|
||||
Name: "jinja2",
|
||||
Version: "2.11.3",
|
||||
Indirect: true,
|
||||
DependsOn: []string{
|
||||
"markupsafe@2.1.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "markupsafe@2.1.2",
|
||||
Name: "markupsafe",
|
||||
Version: "2.1.2",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "requests@2.28.1",
|
||||
Name: "requests",
|
||||
Version: "2.28.1",
|
||||
DependsOn: []string{
|
||||
"certifi@2022.12.7",
|
||||
"charset-normalizer@2.1.1",
|
||||
"idna@3.4",
|
||||
"urllib3@1.26.14",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "urllib3@1.26.14",
|
||||
Name: "urllib3",
|
||||
Version: "1.26.14",
|
||||
Indirect: true,
|
||||
},
|
||||
{
|
||||
ID: "werkzeug@1.0.1",
|
||||
Name: "werkzeug",
|
||||
Version: "1.0.1",
|
||||
Indirect: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
225
pkg/fanal/analyzer/language/python/poetry/testdata/happy/poetry.lock
generated
vendored
Normal file
225
pkg/fanal/analyzer/language/python/poetry/testdata/happy/poetry.lock
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
# This file is automatically @generated by Poetry and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.12.7"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
|
||||
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.1.1"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.6.0"
|
||||
files = [
|
||||
{file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
|
||||
{file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
unicode-backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "7.1.2"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "1.1.4"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "Flask-1.1.4-py2.py3-none-any.whl", hash = "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22"},
|
||||
{file = "Flask-1.1.4.tar.gz", hash = "sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=5.1,<8.0"
|
||||
itsdangerous = ">=0.24,<2.0"
|
||||
Jinja2 = ">=2.10.1,<3.0"
|
||||
Werkzeug = ">=0.15,<2.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage", "pallets-sphinx-themes", "pytest", "sphinx", "sphinx-issues", "sphinxcontrib-log-cabinet", "tox"]
|
||||
docs = ["pallets-sphinx-themes", "sphinx", "sphinx-issues", "sphinxcontrib-log-cabinet"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.4"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "1.1.0"
|
||||
description = "Various helpers to pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
|
||||
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "2.11.3"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
|
||||
{file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=0.23"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=0.8)"]
|
||||
|
||||
[[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 = "requests"
|
||||
version = "2.28.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.7, <4"
|
||||
files = [
|
||||
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
|
||||
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = ">=2,<3"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.14"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
files = [
|
||||
{file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"},
|
||||
{file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "1.0.1"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},
|
||||
{file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage", "pallets-sphinx-themes", "pytest", "pytest-timeout", "sphinx", "sphinx-issues", "tox"]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "0ee6cb4bc2d84091d5dcb0a0110a65f244987ed427933b2f49949195e3ef69c7"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user