mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-09 06:10:47 -08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20f2bae49b | ||
|
|
8eb9df8447 | ||
|
|
88aaffa957 | ||
|
|
469c0b41df | ||
|
|
4a34f72f22 | ||
|
|
4d721e1410 | ||
|
|
9c91da8a2b | ||
|
|
5b9d942313 | ||
|
|
d6b37cb87e | ||
|
|
9c6f077818 | ||
|
|
0b5d936dbe | ||
|
|
6eebed33b2 | ||
|
|
43085a80bc | ||
|
|
4f90b114ea | ||
|
|
d9fa353a06 | ||
|
|
9a1d7460f6 | ||
|
|
d18d17b861 | ||
|
|
4b57c0d4e6 | ||
|
|
ccd9b2d2c5 | ||
|
|
ec770cd819 | ||
|
|
b7ec633fb2 | ||
|
|
7aabff1236 | ||
|
|
9dc1bdffb1 | ||
|
|
2ac672a663 | ||
|
|
11ae6b29d5 | ||
|
|
f201f59e27 | ||
|
|
25d45e1ac5 | ||
|
|
298ba99b8f |
8
.circleci/codecov.yml
Normal file
8
.circleci/codecov.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
@@ -1,4 +1,6 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
codecov: codecov/codecov@1.1.0
|
||||
|
||||
defaults: &defaults
|
||||
docker :
|
||||
@@ -24,6 +26,9 @@ jobs:
|
||||
- run:
|
||||
name: Test
|
||||
command: make test
|
||||
- codecov/upload:
|
||||
file: ./coverage.txt
|
||||
|
||||
release:
|
||||
<<: *defaults
|
||||
steps:
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
.git
|
||||
.github
|
||||
.cache
|
||||
.circleci
|
||||
integration
|
||||
imgs
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,4 +18,5 @@
|
||||
thumbs.db
|
||||
|
||||
# test fixtures
|
||||
coverage.txt
|
||||
integration/testdata/fixtures/
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
FROM alpine:3.11
|
||||
FROM alpine:3.12
|
||||
RUN addgroup -g 1000 -S appgroup && adduser -u 1000 -S appuser -G appgroup
|
||||
RUN apk --no-cache add ca-certificates git rpm
|
||||
COPY trivy /usr/local/bin/trivy
|
||||
COPY contrib/gitlab.tpl contrib/gitlab.tpl
|
||||
COPY contrib/junit.tpl contrib/junit.tpl
|
||||
COPY contrib/sarif.tpl contrib/sarif.tpl
|
||||
USER appuser
|
||||
ENTRYPOINT ["trivy"]
|
||||
|
||||
2
Makefile
2
Makefile
@@ -28,7 +28,7 @@ $(GOBIN)/golangci-lint:
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v -short ./...
|
||||
go test -v -short -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
integration/testdata/fixtures/*.tar.gz:
|
||||
git clone https://github.com/aquasecurity/trivy-test-images.git integration/testdata/fixtures
|
||||
|
||||
106
README.md
106
README.md
@@ -6,6 +6,7 @@
|
||||
[](https://goreportcard.com/report/github.com/aquasecurity/trivy)
|
||||
[](https://github.com/aquasecurity/trivy/blob/master/LICENSE)
|
||||
[](https://microbadger.com/images/aquasec/trivy "Get your own version badge on microbadger.com")
|
||||
[](https://codecov.io/gh/aquasecurity/trivy)
|
||||
|
||||
A Simple and Comprehensive Vulnerability Scanner for Containers and other Artifacts, Suitable for CI.
|
||||
|
||||
@@ -44,6 +45,7 @@ A Simple and Comprehensive Vulnerability Scanner for Containers and other Artifa
|
||||
+ [Save the results using a template](#save-the-results-using-a-template)
|
||||
+ [Filter the vulnerabilities by severities](#filter-the-vulnerabilities-by-severities)
|
||||
+ [Filter the vulnerabilities by type](#filter-the-vulnerabilities-by-type)
|
||||
+ [Filter the vulnerabilities by Open Policy Agent](#filter-the-vulnerabilities-by-open-policy-agent-policy)
|
||||
+ [Skip update of vulnerability DB](#skip-update-of-vulnerability-db)
|
||||
+ [Only download vulnerability database](#only-download-vulnerability-database)
|
||||
+ [Ignore unfixed vulnerabilities](#ignore-unfixed-vulnerabilities)
|
||||
@@ -59,15 +61,18 @@ A Simple and Comprehensive Vulnerability Scanner for Containers and other Artifa
|
||||
+ [Authentication](#authentication)
|
||||
+ [Deprecated options](#deprecated-options)
|
||||
- [Continuous Integration (CI)](#continuous-integration-ci)
|
||||
* [GitHub Actions](#github-actions)
|
||||
* [Travis CI](#travis-ci)
|
||||
* [CircleCI](#circleci)
|
||||
* [GitLab CI](#gitlab-ci)
|
||||
* [AWS CodePipeline](#aws-codepipeline)
|
||||
* [Authorization for Private Docker Registry](#authorization-for-private-docker-registry)
|
||||
- [Vulnerability Detection](#vulnerability-detection)
|
||||
* [OS Packages](#os-packages)
|
||||
* [Application Dependencies](#application-dependencies)
|
||||
* [Image Tar format](#image-tar-format)
|
||||
* [Data source](#data-source)
|
||||
* [Data sources](#data-sources)
|
||||
- [Air-gapped environment](#air-gapped-environment)
|
||||
- [Comparison with other scanners](#comparison-with-other-scanners)
|
||||
- [Usage](#usage)
|
||||
* [Image](#image-1)
|
||||
@@ -178,7 +183,7 @@ yay -Sy trivy-bin
|
||||
|
||||
## Homebrew
|
||||
|
||||
You can use homebrew on macOS.
|
||||
You can use homebrew on macOS and Linux.
|
||||
|
||||
```
|
||||
$ brew install aquasecurity/trivy/trivy
|
||||
@@ -312,7 +317,7 @@ $ docker run --rm -it alpine:3.11
|
||||
```
|
||||
|
||||
## Embed in Dockerfile
|
||||
Scan your image as part of the build process by embedding Trivy in the Dockerfile. This approach can be used to update Dockerfiles currently using Aqua’s [Microscanner][https://github.com/aquasecurity/microscanner].
|
||||
Scan your image as part of the build process by embedding Trivy in the Dockerfile. This approach can be used to update Dockerfiles currently using Aqua’s [Microscanner](https://github.com/aquasecurity/microscanner).
|
||||
|
||||
```
|
||||
$ cat Dockerfile
|
||||
@@ -885,6 +890,16 @@ You can load templates from a file prefixing the template path with an @.
|
||||
$ trivy image --format template --template "@/path/to/template" golang:1.12-alpine
|
||||
```
|
||||
|
||||
In the following example using the template `junit.tpl` XML can be generated.
|
||||
```
|
||||
$ trivy image --format template --template "@contrib/junit.tpl" -o junit-report.xml golang:1.12-alpine
|
||||
```
|
||||
|
||||
In the following example using the template `sarif.tpl` [Sarif](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/managing-results-from-code-scanning) can be generated.
|
||||
```
|
||||
$ trivy image --format template --template "@contrib/sarif.tpl" -o report.sarif golang:1.12-alpine
|
||||
```
|
||||
|
||||
### Filter the vulnerabilities by severities
|
||||
|
||||
```
|
||||
@@ -1085,6 +1100,41 @@ Total: 4751 (UNKNOWN: 1, LOW: 150, MEDIUM: 3504, HIGH: 1013, CRITICAL: 83)
|
||||
|
||||
</details>
|
||||
|
||||
### Filter the vulnerabilities by Open Policy Agent policy
|
||||
[EXPERIMENTAL] This feature might change without preserving backwards compatibility.
|
||||
|
||||
Trivy supports Open Policy Agent (OPA) to filter vulnerabilities. You can specify a Rego file with `--ignore-policy` option.
|
||||
|
||||
The Rego package name must be `trivy` and it must include a rule called `ignore` which determines if each individual vulnerability should be excluded (ignore=true) or not (ignore=false). In the policy, each vulnerability will be available for inspection as the `input` variable. The structure of each vulnerability input is the same as for the Trivy JSON output.
|
||||
There is a built-in Rego library with helper functions that you can import into your policy using: `import data.lib.trivy`. For more info about the helper functions, look at the library [here](pkg/vulnerability/module.go)
|
||||
|
||||
To get started, see the [example policy](./contrib/example_policy).
|
||||
|
||||
```
|
||||
$ trivy image --policy contrib/example_filter/basic.rego centos:7
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
```
|
||||
centos:7 (centos 7.8.2003)
|
||||
==========================
|
||||
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
|
||||
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
| glib2 | CVE-2016-3191 | HIGH | 2.56.1-5.el7 | | pcre: workspace overflow |
|
||||
| | | | | | for (*ACCEPT) with deeply |
|
||||
| | | | | | nested parentheses (8.39/13, |
|
||||
| | | | | | 10.22/12) |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Skip update of vulnerability DB
|
||||
|
||||
`Trivy` always updates its vulnerability database when it starts operating. This is usually fast, as it is a difference update. But if you want to skip even that, use the `--skip-update` option.
|
||||
@@ -1379,9 +1429,15 @@ $ trivy client --remote http://localhost:8080 --token dummy alpine:3.10
|
||||
|
||||
# Continuous Integration (CI)
|
||||
|
||||
Scan your image built in Travis CI/CircleCI. The test will fail if a vulnerability is found. When you don't want to fail the test, specify `--exit-code 0` .
|
||||
Scan your image automatically as part of your CI workflow, failing the workflow if a vulnerability is found. When you don't want to fail the test, specify `--exit-code 0`.
|
||||
|
||||
Since in automated scenarios such as CI/CD you only interested in the end result, and not the full report, use the `--light` flag to optimize for this scenario and get fast results.
|
||||
Since in automated scenarios such as CI/CD you are only interested in the end result, and not the full report, use the `--light` flag to optimize for this scenario and get fast results.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
- Here is the [Trivy Github Action](https://github.com/aquasecurity/trivy-action) (currently Experimental)
|
||||
- The Microsoft Azure team have written a [container-scan action](https://github.com/Azure/container-scan) that uses Trivy and Dockle
|
||||
- For full control over the options specified to Trivy, this [blog post](https://blog.aquasec.com/devsecops-with-trivy-github-actions) describes adding Trivy into your own GitHub action workflows
|
||||
|
||||
## Travis CI
|
||||
|
||||
@@ -1495,6 +1551,10 @@ trivy:
|
||||
container_scanning: gl-container-scanning-report.json
|
||||
```
|
||||
|
||||
## AWS CodePipeline
|
||||
|
||||
See [this blog post](https://aws.amazon.com/blogs/containers/scanning-images-with-trivy-in-an-aws-codepipeline/) for an example of using Trivy within AWS CodePipeline.
|
||||
|
||||
## Authorization for Private Docker Registry
|
||||
|
||||
Trivy can download images from a private registry, without installing `Docker` or any other 3rd party tools.
|
||||
@@ -1570,13 +1630,18 @@ Distroless: https://github.com/GoogleContainerTools/distroless
|
||||
|
||||
`Trivy` automatically detects the following files in the container and scans vulnerabilities in the application dependencies.
|
||||
|
||||
- Gemfile.lock
|
||||
- Pipfile.lock
|
||||
- poetry.lock
|
||||
- composer.lock
|
||||
- package-lock.json
|
||||
- yarn.lock
|
||||
- Cargo.lock
|
||||
- Ruby
|
||||
- Gemfile.lock
|
||||
- Python
|
||||
- Pipfile.lock
|
||||
- poetry.lock
|
||||
- PHP
|
||||
- composer.lock
|
||||
- Node.js
|
||||
- package-lock.json
|
||||
- yarn.lock
|
||||
- Rust
|
||||
- Cargo.lock
|
||||
|
||||
The path of these files does not matter.
|
||||
|
||||
@@ -1667,6 +1732,7 @@ OPTIONS:
|
||||
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
|
||||
--timeout value docker timeout (default: 2m0s) [$TRIVY_TIMEOUT]
|
||||
--light light mode: it's faster, but vulnerability descriptions and references are not displayed (default: false) [$TRIVY_LIGHT]
|
||||
--list-all-pkgs enabling the option will output all packages regardless of vulnerability [$TRIVY_LIST_ALL_PKGS]
|
||||
--help, -h show help (default: false)
|
||||
```
|
||||
|
||||
@@ -1718,6 +1784,9 @@ OPTIONS:
|
||||
--listen value listen address (default: "localhost:4954") [$TRIVY_LISTEN]
|
||||
```
|
||||
|
||||
# Air-gapped environment
|
||||
See [here](docs/air-gap.md)
|
||||
|
||||
# Comparison with other scanners
|
||||
|
||||
## Overview
|
||||
@@ -1886,6 +1955,19 @@ Try again with `--reset` option:
|
||||
```
|
||||
$ trivy image --reset
|
||||
```
|
||||
|
||||
### Error: FATAL unable to initialize the cache: failed to create cache dir: mkdir /root/.cache: permission denied
|
||||
|
||||
```
|
||||
$ docker run --rm -v $PWD:/root/.cache/ aquasec/trivy:latest --cache-dir /root/.cache image centos:7
|
||||
2020-07-29T15:02:54.435Z FATAL unable to initialize the cache: failed to create cache dir: mkdir /root/.cache: permission denied
|
||||
```
|
||||
|
||||
Try:
|
||||
|
||||
```
|
||||
$ docker run --rm -v $PWD:/tmp/.cache/ aquasec/trivy:latest --cache-dir /tmp/.cache image centos:7
|
||||
```
|
||||
---
|
||||
|
||||
# Credits
|
||||
|
||||
94
contrib/example_policy/advanced.rego
Normal file
94
contrib/example_policy/advanced.rego
Normal file
@@ -0,0 +1,94 @@
|
||||
package trivy
|
||||
|
||||
import data.lib.trivy
|
||||
|
||||
default ignore = false
|
||||
|
||||
nvd_v3_vector = v {
|
||||
v := input.CVSS.nvd.v3
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires high privilege
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.PrivilegesRequired == "High"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires user interaction
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.UserInteraction == "Required"
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "openssl"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "openssl"
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM", "HIGH"}[_]
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {
|
||||
"CWE-119", # Improper Restriction of Operations within the Bounds of a Memory Buffer
|
||||
"CWE-200", # Exposure of Sensitive Information to an Unauthorized Actor
|
||||
}
|
||||
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "bash"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local", "Adjacent"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM", "HIGH"}[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "django"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM"}[_]
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {
|
||||
"CWE-89", # SQL Injection
|
||||
"CWE-78", # OS Command Injection
|
||||
}
|
||||
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "jquery"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {"CWE-79"} # XSS
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
45
contrib/example_policy/basic.rego
Normal file
45
contrib/example_policy/basic.rego
Normal file
@@ -0,0 +1,45 @@
|
||||
package trivy
|
||||
|
||||
import data.lib.trivy
|
||||
|
||||
default ignore = false
|
||||
|
||||
ignore_pkgs := {"bash", "bind-license", "rpm", "vim", "vim-minimal"}
|
||||
|
||||
ignore_severities := {"LOW", "MEDIUM"}
|
||||
|
||||
nvd_v3_vector = v {
|
||||
v := input.CVSS.nvd.v3
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == ignore_pkgs[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.Severity == ignore_severities[_]
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which is not remotely exploitable
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.AttackVector != "Network"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires high privilege
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.PrivilegesRequired == "High"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires user interaction
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.UserInteraction == "Required"
|
||||
}
|
||||
|
||||
# Ignore CSRF
|
||||
ignore {
|
||||
# https://cwe.mitre.org/data/definitions/352.html
|
||||
input.CweIDs[_] == "CWE-352"
|
||||
}
|
||||
18
contrib/junit.tpl
Normal file
18
contrib/junit.tpl
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" ?>
|
||||
<testsuites>
|
||||
{{- range . -}}
|
||||
{{- $failures := len .Vulnerabilities }}
|
||||
<testsuite tests="1" failures="{{ $failures }}" time="" name="{{ .Target }}">
|
||||
{{- if not (eq .Type "") }}
|
||||
<properties>
|
||||
<property name="type" value="{{ .Type }}"></property>
|
||||
</properties>
|
||||
{{- end -}}
|
||||
{{ range .Vulnerabilities }}
|
||||
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
|
||||
<failure message={{escapeXML .Title | printf "%q" }} type="description">{{escapeXML .Description | printf "%q" }}</failure>
|
||||
</testcase>
|
||||
{{- end }}
|
||||
</testsuite>
|
||||
{{- end }}
|
||||
</testsuites>
|
||||
81
contrib/sarif.tpl
Normal file
81
contrib/sarif.tpl
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Trivy",
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"rules": [
|
||||
{{- $t_first := true }}
|
||||
{{- range . }}
|
||||
{{- range .Vulnerabilities -}}
|
||||
{{- if $t_first -}}
|
||||
{{- $t_first = false -}}
|
||||
{{ else -}}
|
||||
,
|
||||
{{- end }}
|
||||
{
|
||||
"id": "[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}",
|
||||
"name": "dockerfile_scan",
|
||||
"shortDescription": {
|
||||
"text": "{{ .VulnerabilityID }} Package: {{ .PkgName }}"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "{{ endWithPeriod .Title }}"
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability {{ .VulnerabilityID }}\nSeverity: {{ .Vulnerability.Severity }}\nPackage: {{ .PkgName }}\nInstalled Version: {{ .InstalledVersion }}\nFixed Version: {{ .FixedVersion }}\nLink: [{{ .VulnerabilityID }}](https://nvd.nist.gov/vuln/detail/{{ .VulnerabilityID | toLower }})",
|
||||
"markdown": "**Vulnerability {{ .VulnerabilityID }}**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|{{ .Vulnerability.Severity }}|{{ .PkgName }}|{{ .InstalledVersion }}|{{ .FixedVersion }}|[{{ .VulnerabilityID }}](https://nvd.nist.gov/vuln/detail/{{ .VulnerabilityID | toLower }})|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"{{ .Vulnerability.Severity }}",
|
||||
"{{ .PkgName }}"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{{- $t_first := true }}
|
||||
{{- range . }}
|
||||
{{- range $index, $vulnerability := .Vulnerabilities -}}
|
||||
{{- if $t_first -}}
|
||||
{{- $t_first = false -}}
|
||||
{{ else -}}
|
||||
,
|
||||
{{- end }}
|
||||
{
|
||||
"ruleId": "[{{ $vulnerability.Vulnerability.Severity }}] {{ $vulnerability.VulnerabilityID }}",
|
||||
"ruleIndex": {{ $index }},
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": {{ endWithPeriod $vulnerability.Description | printf "%q" }}
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "Dockerfile"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
],
|
||||
"columnKind": "utf16CodeUnits"
|
||||
}
|
||||
]
|
||||
}
|
||||
55
docs/air-gap.md
Normal file
55
docs/air-gap.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Air-gapped environment
|
||||
Trivy can be used in air-gapped environments.
|
||||
|
||||
## Download the vulnerability database
|
||||
At first, you need to download the vulnerability database for use in air-gapped environments.
|
||||
Go to [trivy-db](https://github.com/aquasecurity/trivy-db/releases) and download `trivy-offline.db.tgz` in the latest release.
|
||||
If you download `trivy-light-offline.db.tgz`, you have to run Trivy with `--light` option.
|
||||
|
||||
```
|
||||
$ wget https://github.com/aquasecurity/trivy-db/releases/latest/download/trivy-offline.db.tgz
|
||||
```
|
||||
|
||||
|
||||
## Transfer the DB file into the air-gapped environment
|
||||
The way of transfer depends on the environment.
|
||||
|
||||
```
|
||||
$ rsync -av -e ssh /path/to/trivy-offline.db.tgz [user]@[host]:dst
|
||||
```
|
||||
|
||||
## Put the DB file in Trivy's cache directory
|
||||
You have to know where to put the DB file. The following command shows the default cache directory.
|
||||
|
||||
```
|
||||
$ ssh user@host
|
||||
$ trivy -h | grep cache
|
||||
--cache-dir value cache directory (default: "/home/myuser/.cache/trivy") [$TRIVY_CACHE_DIR]
|
||||
```
|
||||
|
||||
Put the DB file in the cache directory + `/db`.
|
||||
|
||||
```
|
||||
$ mkdir -p /home/myuser/.cache/trivy/db
|
||||
$ cd /home/myuser/.cache/trivy/db
|
||||
$ mv /path/to/trivy-offline.db.tgz .
|
||||
```
|
||||
|
||||
Then, decompress it.
|
||||
`trivy-offline.db.tgz` file includes two files, `trivy.db` and `metadata.json`.
|
||||
|
||||
```
|
||||
$ tar xvf trivy-offline.db.tgz
|
||||
x trivy.db
|
||||
x metadata.json
|
||||
$ rm trivy-offline.db.tgz
|
||||
```
|
||||
|
||||
In an air-gapped environment it is your responsibility to update the Trivy database on a regular basis, so that the scanner can detect recently-identified vulnerabilities.
|
||||
|
||||
## Run Trivy with --skip-update option
|
||||
In an air-gapped environment, specify `--skip-update` so that Trivy doesn't attempt to download the latest database file.
|
||||
|
||||
```
|
||||
$ trivy image --skip-update alpine:3.12
|
||||
```
|
||||
8
go.mod
8
go.mod
@@ -3,9 +3,10 @@ module github.com/aquasecurity/trivy
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.1.0
|
||||
github.com/aquasecurity/fanal v0.0.0-20200528202907-79693bf4a058
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200715174849-fa5a3ca24b16
|
||||
github.com/caarlos0/env/v6 v6.0.0
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cheggaaa/pb/v3 v3.0.3
|
||||
@@ -15,14 +16,15 @@ require (
|
||||
github.com/google/go-containerregistry v0.0.0-20200331213917-3d03ed9b1ca2
|
||||
github.com/google/go-github/v28 v28.1.1
|
||||
github.com/google/wire v0.3.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-rpm-version v0.0.0-20170716094938-74609b86c936
|
||||
github.com/knqyf263/go-version v1.1.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
|
||||
github.com/open-policy-agent/opa v0.21.1
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/testcontainers/testcontainers-go v0.3.1
|
||||
github.com/twitchtv/twirp v5.10.1+incompatible
|
||||
github.com/urfave/cli/v2 v2.2.0
|
||||
|
||||
42
go.sum
42
go.sum
@@ -27,6 +27,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64=
|
||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
|
||||
@@ -34,6 +36,8 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tT
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.7 h1:fzrmmkskv067ZQbd9wERNGuxckWw67dyzoMG62p7LMo=
|
||||
github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
@@ -52,8 +56,8 @@ github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ul
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ=
|
||||
github.com/aquasecurity/testdocker v0.0.0-20200426142840-5f05bce6f12a h1:hsw7PpiymXP64evn/K7gsj3hWzMqLrdoeE6JkqDocVg=
|
||||
github.com/aquasecurity/testdocker v0.0.0-20200426142840-5f05bce6f12a/go.mod h1:psfu0MVaiTDLpNxCoNsTeILSKY2EICBwv345f3M+Ffs=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470 h1:6VE+g4AK2uivPqZtVk/QtcCBb2rUjAvKqDNexSgqMC0=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470/go.mod h1:F77bF2nRbcH4EIhhcNEP585MoAKdLpEP3dihF9V1Hbw=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200715174849-fa5a3ca24b16 h1:Hh9MOUaJGI+PS9ZULxYqYQmsFfvtktt8jD7gMt43BA8=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200715174849-fa5a3ca24b16/go.mod h1:EiFA908RL0ACrbYo/9HfT7f9QcdC2bZoIO5XAAcvz9A=
|
||||
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 h1:xbdUfr2KE4THsFx9CFWtWpU91lF+YhgP46moV94nYTA=
|
||||
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
|
||||
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
||||
@@ -147,6 +151,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
@@ -181,10 +187,13 @@ github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -193,6 +202,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v0.0.0-20181025225059-d3de96c4c28e/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -227,6 +237,7 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v0.0.0-20181024020800-521ea7b17d02/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
@@ -272,13 +283,13 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
|
||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GXhHq+7LeOzx/haG7HSIZokl3/0GkoUFzsRJjg=
|
||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8=
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8=
|
||||
github.com/knqyf263/go-version v1.1.1 h1:+MpcBC9b7rk5ihag8Y/FLG8get1H2GjniwKQ+9DxI2o=
|
||||
github.com/knqyf263/go-version v1.1.1/go.mod h1:0tBvHvOBSf5TqGNcY+/ih9o8qo3R16iZCpB9rP0D3VM=
|
||||
github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc=
|
||||
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -312,6 +323,7 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed/go.mod h1:SDJ4hurDYyQ9/7nc+eCYtXqdufgK4Cq9TJlwPklqEYA=
|
||||
github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||
@@ -348,6 +360,8 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/open-policy-agent/opa v0.21.1 h1:c4lUnB0mO2KssiUnyh6Y9IGhggvXI3EgObkmhVTvEqQ=
|
||||
github.com/open-policy-agent/opa v0.21.1/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
@@ -359,6 +373,8 @@ github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmU
|
||||
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -367,14 +383,18 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20181025174421-f30f42803563/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
@@ -410,10 +430,12 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v0.0.0-20181024212040-082b515c9490/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -427,6 +449,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/testcontainers/testcontainers-go v0.3.1 h1:KZkEKNfnlsipJblzGCz6fmzd+0DzJ3djulYrislG3Zw=
|
||||
github.com/testcontainers/testcontainers-go v0.3.1/go.mod h1:br7bkzIukhPSIjy07Ma3OuXjjFvl2jm7CDU0LQNsqLw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -445,6 +470,8 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
@@ -468,7 +495,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -480,6 +506,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20181023182221-1baf3a9d7d67/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -508,6 +535,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -533,7 +561,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -599,6 +626,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -644,6 +672,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
|
||||
@@ -94,3 +94,5 @@ dockers:
|
||||
- "--label=org.label-schema.vcs-ref={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- contrib/gitlab.tpl
|
||||
- contrib/junit.tpl
|
||||
- contrib/sarif.tpl
|
||||
|
||||
@@ -85,6 +85,16 @@ func TestClientServer(t *testing.T) {
|
||||
},
|
||||
golden: "testdata/alpine-310.gitlab.golden",
|
||||
},
|
||||
{
|
||||
name: "alpine 3.10 integration with sarif template",
|
||||
testArgs: args{
|
||||
Format: "template",
|
||||
TemplatePath: "@../contrib/sarif.tpl",
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/alpine-310.tar.gz",
|
||||
},
|
||||
golden: "testdata/alpine-310.sarif.golden",
|
||||
},
|
||||
{
|
||||
name: "alpine 3.9 integration",
|
||||
testArgs: args{
|
||||
|
||||
@@ -5,6 +5,7 @@ package integration
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -13,8 +14,6 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
dbFile "github.com/aquasecurity/trivy/pkg/db"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
@@ -53,7 +52,9 @@ func gunzipDB() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = dbFile.NewMetadata(afero.NewOsFs(), tmpDir).Store(db.Metadata{
|
||||
fs := afero.NewOsFs()
|
||||
metadataFile := filepath.Join(dbDir, "metadata.json")
|
||||
b, err := json.Marshal(db.Metadata{
|
||||
Version: 1,
|
||||
Type: 1,
|
||||
NextUpdate: time.Time{},
|
||||
@@ -62,6 +63,10 @@ func gunzipDB() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = afero.WriteFile(fs, metadataFile, b, 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tmpDir, nil
|
||||
}
|
||||
|
||||
185
integration/testdata/alpine-310.sarif.golden
vendored
Normal file
185
integration/testdata/alpine-310.sarif.golden
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Trivy",
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"rules": [
|
||||
{
|
||||
"id": "[MEDIUM] CVE-2019-1549",
|
||||
"name": "dockerfile_scan",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1549 Package: openssl"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "openssl: information disclosure in fork()."
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-2019-1549\nSeverity: MEDIUM\nPackage: openssl\nInstalled Version: 1.1.1c-r0\nFixed Version: 1.1.1d-r0\nLink: [CVE-2019-1549](https://nvd.nist.gov/vuln/detail/cve-2019-1549)",
|
||||
"markdown": "**Vulnerability CVE-2019-1549**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|MEDIUM|openssl|1.1.1c-r0|1.1.1d-r0|[CVE-2019-1549](https://nvd.nist.gov/vuln/detail/cve-2019-1549)|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"MEDIUM",
|
||||
"openssl"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "[MEDIUM] CVE-2019-1551",
|
||||
"name": "dockerfile_scan",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1551 Package: openssl"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "openssl: Integer overflow in RSAZ modular exponentiation on x86_64."
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-2019-1551\nSeverity: MEDIUM\nPackage: openssl\nInstalled Version: 1.1.1c-r0\nFixed Version: 1.1.1d-r2\nLink: [CVE-2019-1551](https://nvd.nist.gov/vuln/detail/cve-2019-1551)",
|
||||
"markdown": "**Vulnerability CVE-2019-1551**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|MEDIUM|openssl|1.1.1c-r0|1.1.1d-r2|[CVE-2019-1551](https://nvd.nist.gov/vuln/detail/cve-2019-1551)|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"MEDIUM",
|
||||
"openssl"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "[MEDIUM] CVE-2019-1563",
|
||||
"name": "dockerfile_scan",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1563 Package: openssl"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "openssl: information disclosure in PKCS7_dataDecode and CMS_decrypt_set1_pkey."
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-2019-1563\nSeverity: MEDIUM\nPackage: openssl\nInstalled Version: 1.1.1c-r0\nFixed Version: 1.1.1d-r0\nLink: [CVE-2019-1563](https://nvd.nist.gov/vuln/detail/cve-2019-1563)",
|
||||
"markdown": "**Vulnerability CVE-2019-1563**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|MEDIUM|openssl|1.1.1c-r0|1.1.1d-r0|[CVE-2019-1563](https://nvd.nist.gov/vuln/detail/cve-2019-1563)|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"MEDIUM",
|
||||
"openssl"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "[LOW] CVE-2019-1547",
|
||||
"name": "dockerfile_scan",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1547 Package: openssl"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "openssl: side-channel weak encryption vulnerability."
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-2019-1547\nSeverity: LOW\nPackage: openssl\nInstalled Version: 1.1.1c-r0\nFixed Version: 1.1.1d-r0\nLink: [CVE-2019-1547](https://nvd.nist.gov/vuln/detail/cve-2019-1547)",
|
||||
"markdown": "**Vulnerability CVE-2019-1547**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|LOW|openssl|1.1.1c-r0|1.1.1d-r0|[CVE-2019-1547](https://nvd.nist.gov/vuln/detail/cve-2019-1547)|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"LOW",
|
||||
"openssl"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "[MEDIUM] CVE-2019-1549",
|
||||
"ruleIndex": 0,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "OpenSSL 1.1.1 introduced a rewritten random number generator (RNG). This was intended to include protection in the event of a fork() system call in order to ensure that the parent and child processes did not share the same RNG state. However this protection was not being used in the default case. A partial mitigation for this issue is that the output from a high precision timer is mixed into the RNG state so the likelihood of a parent and child process sharing state is significantly reduced. If an application already calls OPENSSL_init_crypto() explicitly using OPENSSL_INIT_ATFORK then this problem does not occur at all. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c)."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "Dockerfile"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "[MEDIUM] CVE-2019-1551",
|
||||
"ruleIndex": 1,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "There is an overflow bug in the x64_64 Montgomery squaring procedure used in exponentiation with 512-bit moduli. No EC algorithms are affected. Analysis suggests that attacks against 2-prime RSA1024, 3-prime RSA1536, and DSA1024 as a result of this defect would be very difficult to perform and are not believed likely. Attacks against DH512 are considered just feasible. However, for an attack the target would have to re-use the DH512 private key, which is not recommended anyway. Also applications directly using the low level API BN_mod_exp may be affected if they use BN_FLG_CONSTTIME. Fixed in OpenSSL 1.1.1e (Affected 1.1.1-1.1.1d). Fixed in OpenSSL 1.0.2u (Affected 1.0.2-1.0.2t)."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "Dockerfile"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "[MEDIUM] CVE-2019-1563",
|
||||
"ruleIndex": 2,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "In situations where an attacker receives automated notification of the success or failure of a decryption attempt an attacker, after sending a very large number of messages to be decrypted, can recover a CMS/PKCS7 transported encryption key or decrypt any RSA encrypted message that was encrypted with the public RSA key, using a Bleichenbacher padding oracle attack. Applications are not affected if they use a certificate together with the private RSA key to the CMS_decrypt or PKCS7_decrypt functions to select the correct recipient info to decrypt. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s)."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "Dockerfile"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "[LOW] CVE-2019-1547",
|
||||
"ruleIndex": 3,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "Normally in OpenSSL EC groups always have a co-factor present and this is used in side channel resistant code paths. However, in some cases, it is possible to construct a group using explicit parameters (instead of using a named curve). In those cases it is possible that such a group does not have the cofactor present. This can occur even where all the parameters match a known named curve. If such a curve is used then OpenSSL falls back to non-side channel resistant code paths which may result in full key recovery during an ECDSA signature operation. In order to be vulnerable an attacker would have to have the ability to time the creation of a large number of signatures where explicit parameters with no co-factor present are in use by an application using libcrypto. For the avoidance of doubt libssl is not vulnerable because explicit parameters are never used. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s)."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "Dockerfile"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 1,
|
||||
"startColumn": 1,
|
||||
"endColumn": 1
|
||||
}
|
||||
}
|
||||
}]
|
||||
}],
|
||||
"columnKind": "utf16CodeUnits"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -176,6 +176,18 @@ var (
|
||||
EnvVars: []string{"TRIVY_TOKEN_HEADER"},
|
||||
}
|
||||
|
||||
ignorePolicy = cli.StringFlag{
|
||||
Name: "ignore-policy",
|
||||
Usage: "specify the Rego file to evaluate each vulnerability",
|
||||
EnvVars: []string{"TRIVY_IGNORE_POLICY"},
|
||||
}
|
||||
|
||||
listAllPackages = cli.BoolFlag{
|
||||
Name: "list-all-pkgs",
|
||||
Usage: "enabling the option will output all packages regardless of vulnerability",
|
||||
EnvVars: []string{"TRIVY_LIST_ALL_PKGS"},
|
||||
}
|
||||
|
||||
globalFlags = []cli.Flag{
|
||||
&quietFlag,
|
||||
&debugFlag,
|
||||
@@ -200,6 +212,8 @@ var (
|
||||
&ignoreFileFlag,
|
||||
&timeoutFlag,
|
||||
&lightFlag,
|
||||
&ignorePolicy,
|
||||
&listAllPackages,
|
||||
}
|
||||
|
||||
// deprecated options
|
||||
@@ -352,6 +366,8 @@ func NewFilesystemCommand() *cli.Command {
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&noProgressFlag,
|
||||
&ignorePolicy,
|
||||
&listAllPackages,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -380,6 +396,8 @@ func NewRepositoryCommand() *cli.Command {
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&noProgressFlag,
|
||||
&ignorePolicy,
|
||||
&listAllPackages,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -407,6 +425,7 @@ func NewClientCommand() *cli.Command {
|
||||
&ignoreFileFlag,
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&ignorePolicy,
|
||||
|
||||
// original flags
|
||||
&token,
|
||||
|
||||
@@ -2,6 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbFile "github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
@@ -87,17 +87,20 @@ Vulnerability DB:
|
||||
}
|
||||
|
||||
if tt.createDB {
|
||||
m := dbFile.NewMetadata(afero.NewOsFs(), cacheDir)
|
||||
fs := afero.NewOsFs()
|
||||
err := os.MkdirAll(filepath.Join(cacheDir, "db"), os.ModePerm)
|
||||
require.NoError(t, err)
|
||||
metadataFile := filepath.Join(cacheDir, "db", "metadata.json")
|
||||
|
||||
err = m.Store(db.Metadata{
|
||||
b, err := json.Marshal(db.Metadata{
|
||||
Version: 42,
|
||||
Type: 1,
|
||||
NextUpdate: time.Unix(1584403020, 0),
|
||||
UpdatedAt: time.Unix(1584402020, 0),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = afero.WriteFile(fs, metadataFile, b, 0600)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
fw := new(bytes.Buffer)
|
||||
|
||||
@@ -65,7 +65,8 @@ func run(c config.Config, initializeScanner InitializeScanner) error {
|
||||
target = c.Input
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
|
||||
defer cancel()
|
||||
scanner, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, c.Timeout)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to initialize a scanner: %w", err)
|
||||
@@ -75,6 +76,7 @@ func run(c config.Config, initializeScanner InitializeScanner) error {
|
||||
scanOptions := types.ScanOptions{
|
||||
VulnType: c.VulnType,
|
||||
ScanRemovedPackages: c.ScanRemovedPkgs, // this is valid only for image subcommand
|
||||
ListAllPackages: c.ListAllPkgs,
|
||||
}
|
||||
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
|
||||
|
||||
@@ -86,11 +88,15 @@ func run(c config.Config, initializeScanner InitializeScanner) error {
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
for i := range results {
|
||||
vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile, c.IgnorePolicy)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to filter vulnerabilities: %w", err)
|
||||
}
|
||||
results[i].Vulnerabilities = vulns
|
||||
}
|
||||
|
||||
if err = report.WriteResults(c.Format, c.Output, results, c.Template, c.Light); err != nil {
|
||||
if err = report.WriteResults(c.Format, c.Output, c.Severities, results, c.Template, c.Light); err != nil {
|
||||
return xerrors.Errorf("unable to write results: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ func run(c config.Config) (err error) {
|
||||
}
|
||||
|
||||
var scanner scanner.Scanner
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
|
||||
defer cancel()
|
||||
remoteCache := cache.NewRemoteCache(cache.RemoteURL(c.RemoteAddr), c.CustomHeaders)
|
||||
|
||||
cleanup := func() {}
|
||||
@@ -79,11 +80,15 @@ func run(c config.Config) (err error) {
|
||||
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
for i := range results {
|
||||
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile, c.IgnorePolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results[i].Vulnerabilities = vulns
|
||||
}
|
||||
|
||||
if err = report.WriteResults(c.Format, c.Output, results, c.Template, false); err != nil {
|
||||
if err = report.WriteResults(c.Format, c.Output, c.Severities, results, c.Template, false); err != nil {
|
||||
return xerrors.Errorf("unable to write results: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,13 @@ import (
|
||||
|
||||
type ImageConfig struct {
|
||||
ScanRemovedPkgs bool
|
||||
ListAllPkgs bool
|
||||
}
|
||||
|
||||
func NewImageConfig(c *cli.Context) ImageConfig {
|
||||
return ImageConfig{
|
||||
ScanRemovedPkgs: c.Bool("removed-pkgs"),
|
||||
ListAllPkgs: c.Bool("list-all-pkgs"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ type ReportConfig struct {
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
IgnorePolicy string
|
||||
|
||||
// these variables are not exported
|
||||
vulnType string
|
||||
@@ -32,9 +33,10 @@ type ReportConfig struct {
|
||||
|
||||
func NewReportConfig(c *cli.Context) ReportConfig {
|
||||
return ReportConfig{
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
IgnorePolicy: c.String("ignore-policy"),
|
||||
|
||||
vulnType: c.String("vuln-type"),
|
||||
severities: c.String("severity"),
|
||||
|
||||
21
pkg/db/db.go
21
pkg/db/db.go
@@ -59,6 +59,7 @@ type Operation interface {
|
||||
|
||||
type dbOperation interface {
|
||||
GetMetadata() (metadata db.Metadata, err error)
|
||||
StoreMetadata(metadata db.Metadata, dir string) (err error)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
@@ -181,17 +182,17 @@ func (c Client) UpdateMetadata(cacheDir string) error {
|
||||
|
||||
metadata, err := c.dbc.GetMetadata()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to get a metadata: %w", err)
|
||||
return xerrors.Errorf("unable to get metadata: %w", err)
|
||||
}
|
||||
|
||||
if err = c.metadata.Store(metadata); err != nil {
|
||||
if err = c.dbc.StoreMetadata(metadata, filepath.Join(cacheDir, "db")); err != nil {
|
||||
return xerrors.Errorf("failed to store metadata: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
type Metadata struct { // TODO: Move all Metadata things to trivy-db repo
|
||||
fs afero.Fs
|
||||
filePath string
|
||||
}
|
||||
@@ -210,20 +211,6 @@ func MetadataPath(cacheDir string) string {
|
||||
return filepath.Join(dbDir, metadataFile)
|
||||
}
|
||||
|
||||
// StoreMetadata stores database metadata as a file
|
||||
func (m Metadata) Store(metadata db.Metadata) error {
|
||||
f, err := m.fs.Create(m.filePath)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to create a metadata file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err = json.NewEncoder(f).Encode(metadata); err != nil {
|
||||
return xerrors.Errorf("unable to encode metadata: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteMetadata deletes the file of database metadata
|
||||
func (m Metadata) Delete() error {
|
||||
if err := m.fs.Remove(m.filePath); err != nil {
|
||||
|
||||
@@ -2,6 +2,8 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -142,11 +144,13 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
metadata := NewMetadata(fs, "/cache")
|
||||
if tc.metadata != (db.Metadata{}) {
|
||||
metadata.Store(tc.metadata)
|
||||
b, err := json.Marshal(tc.metadata)
|
||||
require.NoError(t, err)
|
||||
err = afero.WriteFile(fs, metadata.filePath, b, 0600)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
client := Client{
|
||||
//dbc: mockConfig,
|
||||
clock: tc.clock,
|
||||
metadata: metadata,
|
||||
}
|
||||
@@ -161,22 +165,15 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expected, needsUpdate)
|
||||
//mockConfig.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Download(t *testing.T) {
|
||||
type getMetadataOutput struct {
|
||||
metadata db.Metadata
|
||||
err error
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
light bool
|
||||
downloadDB []github.DownloadDBExpectation
|
||||
getMetadata dbOperationGetMetadataExpectation
|
||||
expectedContent []byte
|
||||
expectedError error
|
||||
}{
|
||||
@@ -191,15 +188,6 @@ func TestClient_Download(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
getMetadata: dbOperationGetMetadataExpectation{
|
||||
Returns: dbOperationGetMetadataReturns{
|
||||
Metadata: db.Metadata{
|
||||
Version: 1,
|
||||
Type: db.TypeFull,
|
||||
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DownloadDB returns an error",
|
||||
@@ -235,7 +223,6 @@ func TestClient_Download(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockConfig := new(mockDbOperation)
|
||||
mockConfig.ApplyGetMetadataExpectation(tc.getMetadata)
|
||||
|
||||
mockGitHubClient, err := github.NewMockClient(tc.downloadDB)
|
||||
require.NoError(t, err, tc.name)
|
||||
@@ -263,3 +250,99 @@ func TestClient_Download(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateMetadata(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
getMetadataExpectation dbOperationGetMetadataExpectation
|
||||
storeMetadataExpectation dbOperationStoreMetadataExpectation
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
getMetadataExpectation: dbOperationGetMetadataExpectation{
|
||||
Returns: dbOperationGetMetadataReturns{
|
||||
Metadata: db.Metadata{
|
||||
Version: 1,
|
||||
Type: 1,
|
||||
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
},
|
||||
storeMetadataExpectation: dbOperationStoreMetadataExpectation{
|
||||
Metadata: db.Metadata{
|
||||
Version: 1,
|
||||
Type: 1,
|
||||
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sad path, get metadata fails",
|
||||
getMetadataExpectation: dbOperationGetMetadataExpectation{
|
||||
Returns: dbOperationGetMetadataReturns{
|
||||
Err: errors.New("get metadata failed"),
|
||||
},
|
||||
},
|
||||
expectedError: errors.New("unable to get metadata: get metadata failed"),
|
||||
},
|
||||
{
|
||||
name: "sad path, store metadata fails",
|
||||
getMetadataExpectation: dbOperationGetMetadataExpectation{
|
||||
Returns: dbOperationGetMetadataReturns{
|
||||
Metadata: db.Metadata{
|
||||
Version: 1,
|
||||
Type: 1,
|
||||
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Err: nil,
|
||||
},
|
||||
},
|
||||
storeMetadataExpectation: dbOperationStoreMetadataExpectation{
|
||||
Metadata: db.Metadata{
|
||||
Version: 1,
|
||||
Type: 1,
|
||||
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Returns: dbOperationStoreMetadataReturns{
|
||||
Err: errors.New("store metadata failed"),
|
||||
},
|
||||
},
|
||||
expectedError: errors.New("failed to store metadata: store metadata failed"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockConfig := new(mockDbOperation)
|
||||
err := log.InitLogger(false, true)
|
||||
require.NoError(t, err, "failed to init logger")
|
||||
|
||||
mockConfig.ApplyGetMetadataExpectation(tc.getMetadataExpectation)
|
||||
mockConfig.ApplyStoreMetadataExpectation(tc.storeMetadataExpectation)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
metadata := NewMetadata(fs, "/cache")
|
||||
|
||||
dir, err := ioutil.TempDir("", "db")
|
||||
require.NoError(t, err, tc.name)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pb := indicator.NewProgressBar(true)
|
||||
client := NewClient(mockConfig, nil, pb, nil, metadata)
|
||||
|
||||
err = client.UpdateMetadata(dir)
|
||||
switch {
|
||||
case tc.expectedError != nil:
|
||||
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
|
||||
default:
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
pkgdb "github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ type mockDbOperation struct {
|
||||
}
|
||||
|
||||
type dbOperationGetMetadataReturns struct {
|
||||
Metadata pkgdb.Metadata
|
||||
Metadata db.Metadata
|
||||
Err error
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ func (_m *mockDbOperation) ApplyGetMetadataExpectations(expectations []dbOperati
|
||||
}
|
||||
|
||||
// GetMetadata provides a mock function with given fields:
|
||||
func (_m *mockDbOperation) GetMetadata() (pkgdb.Metadata, error) {
|
||||
func (_m *mockDbOperation) GetMetadata() (db.Metadata, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 pkgdb.Metadata
|
||||
if rf, ok := ret.Get(0).(func() pkgdb.Metadata); ok {
|
||||
var r0 db.Metadata
|
||||
if rf, ok := ret.Get(0).(func() db.Metadata); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(pkgdb.Metadata)
|
||||
r0 = ret.Get(0).(db.Metadata)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
@@ -52,3 +52,37 @@ func (_m *mockDbOperation) GetMetadata() (pkgdb.Metadata, error) {
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type dbOperationStoreMetadataReturns struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type dbOperationStoreMetadataExpectation struct {
|
||||
Metadata db.Metadata
|
||||
Dir string
|
||||
Returns dbOperationStoreMetadataReturns
|
||||
}
|
||||
|
||||
func (_m *mockDbOperation) ApplyStoreMetadataExpectation(e dbOperationStoreMetadataExpectation) {
|
||||
_m.On("StoreMetadata", e.Metadata, mock.Anything).Return(e.Returns.Err)
|
||||
}
|
||||
|
||||
func (_m *mockDbOperation) ApplyStoreMetadataExpectations(expectations []dbOperationStoreMetadataExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyStoreMetadataExpectation(e)
|
||||
}
|
||||
}
|
||||
|
||||
// StoreMetadata provides a mock function with given fields: metadata, dir
|
||||
func (_m *mockDbOperation) StoreMetadata(metadata db.Metadata, dir string) error {
|
||||
ret := _m.Called(metadata, dir)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(db.Metadata, string) error); ok {
|
||||
r0 = rf(metadata, dir)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package bundler
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/knqyf263/go-version"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
bundlerSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/bundler"
|
||||
@@ -44,7 +44,7 @@ func NewAdvisory() *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (a *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
advisories, err := a.vs.Get(pkgName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get bundler advisories: %w", err)
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
bundlerSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/bundler"
|
||||
"github.com/knqyf263/go-version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -53,7 +54,7 @@ func TestScanner_Detect(t *testing.T) {
|
||||
versionStr := "1.9.25-x64-mingw32"
|
||||
versionStr = platformReplacer.Replace(versionStr)
|
||||
|
||||
v, _ := version.NewVersion(versionStr)
|
||||
v, _ := semver.NewVersion(versionStr)
|
||||
|
||||
vulns, err := s.DetectVulnerabilities("ffi", v)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
cargoSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/cargo"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/knqyf263/go-version"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewAdvisory() *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
advisories, err := s.vs.Get(pkgName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get cargo advisories: %w", err)
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
composerSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/composer"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/knqyf263/go-version"
|
||||
)
|
||||
|
||||
type Advisory struct {
|
||||
@@ -23,7 +24,7 @@ func NewAdvisory() *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
ref := fmt.Sprintf("composer://%s", pkgName)
|
||||
advisories, err := s.vs.Get(ref)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,11 +8,12 @@ import (
|
||||
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/knqyf263/go-version"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
@@ -54,7 +55,7 @@ func detect(driver Driver, libs []ftypes.LibraryInfo) ([]types.DetectedVulnerabi
|
||||
log.Logger.Infof("Detecting %s vulnerabilities...", driver.Type())
|
||||
var vulnerabilities []types.DetectedVulnerability
|
||||
for _, lib := range libs {
|
||||
v, err := version.NewVersion(lib.Library.Version)
|
||||
v, err := semver.NewVersion(utils.FormatPatchVersion(lib.Library.Version))
|
||||
if err != nil {
|
||||
log.Logger.Debugf("invalid version, library: %s, version: %s, error: %s\n",
|
||||
lib.Library.Name, lib.Library.Version, err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package library
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/aquasecurity/fanal/analyzer/library"
|
||||
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/node"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/python"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/knqyf263/go-version"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ type Factory interface {
|
||||
}
|
||||
|
||||
type advisory interface {
|
||||
DetectVulnerabilities(string, *version.Version) ([]types.DetectedVulnerability, error)
|
||||
DetectVulnerabilities(string, *semver.Version) ([]types.DetectedVulnerability, error)
|
||||
}
|
||||
|
||||
type DriverFactory struct{}
|
||||
@@ -59,7 +59,7 @@ func NewDriver(p string, advisories ...advisory) Driver {
|
||||
return Driver{pkgManager: p, advisories: advisories}
|
||||
}
|
||||
|
||||
func (driver *Driver) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (driver *Driver) Detect(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
var detectedVulnerabilities []types.DetectedVulnerability
|
||||
uniqVulnIdMap := make(map[string]struct{})
|
||||
for _, d := range driver.advisories {
|
||||
|
||||
@@ -3,7 +3,7 @@ package ghsa
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/knqyf263/go-version"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
|
||||
@@ -25,7 +25,7 @@ func NewAdvisory(ecosystem ghsa.Ecosystem) *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
advisories, err := s.vs.Get(pkgName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get ghsa advisories: %w", err)
|
||||
|
||||
@@ -3,9 +3,10 @@ package node
|
||||
import (
|
||||
"strings"
|
||||
|
||||
version "github.com/knqyf263/go-version"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/node"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
@@ -21,7 +22,7 @@ func NewAdvisory() *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
replacer := strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc", " <", ", <", " >", ", >")
|
||||
advisories, err := s.vs.Get(pkgName)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/knqyf263/go-version"
|
||||
)
|
||||
|
||||
type Advisory struct {
|
||||
@@ -22,7 +22,7 @@ func NewAdvisory() *Advisory {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
|
||||
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
|
||||
advisories, err := s.vs.Get(pkgName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get python advisories: %w", err)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
version "github.com/knqyf263/go-deb-version"
|
||||
version "github.com/knqyf263/go-apk-version"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
|
||||
@@ -153,6 +153,54 @@ func TestScanner_Detect(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "contain pre",
|
||||
args: args{
|
||||
osVer: "3.12",
|
||||
pkgs: []ftypes.Package{
|
||||
{
|
||||
Name: "test",
|
||||
Version: "0.1.0_alpha",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mocks: mocks{
|
||||
get: []get{
|
||||
{
|
||||
input: getInput{
|
||||
osVer: "3.12",
|
||||
pkgName: "test",
|
||||
},
|
||||
output: getOutput{
|
||||
advisories: []dbTypes.Advisory{
|
||||
{
|
||||
VulnerabilityID: "CVE-2030-0001",
|
||||
FixedVersion: "0.1.0_alpha_pre2",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2030-0002",
|
||||
FixedVersion: "0.1.0_alpha2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2030-0002",
|
||||
PkgName: "test",
|
||||
InstalledVersion: "0.1.0_alpha",
|
||||
FixedVersion: "0.1.0_alpha2",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Get returns an error",
|
||||
args: args{
|
||||
|
||||
@@ -24,43 +24,56 @@ func InitLogger(debug, disable bool) (err error) {
|
||||
}
|
||||
|
||||
func NewLogger(debug, disable bool) (*zap.SugaredLogger, error) {
|
||||
level := zap.NewAtomicLevel()
|
||||
if debug {
|
||||
level.SetLevel(zapcore.DebugLevel)
|
||||
} else {
|
||||
level.SetLevel(zapcore.InfoLevel)
|
||||
// First, define our level-handling logic.
|
||||
errorPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
||||
return lvl >= zapcore.ErrorLevel
|
||||
})
|
||||
logPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
||||
if debug {
|
||||
return lvl < zapcore.ErrorLevel
|
||||
}
|
||||
// Not enable debug level
|
||||
return zapcore.DebugLevel < lvl && lvl < zapcore.ErrorLevel
|
||||
})
|
||||
|
||||
encoderConfig := zapcore.EncoderConfig{
|
||||
TimeKey: "Time",
|
||||
LevelKey: "Level",
|
||||
NameKey: "Name",
|
||||
CallerKey: "Caller",
|
||||
MessageKey: "Msg",
|
||||
StacktraceKey: "St",
|
||||
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
||||
|
||||
myConfig := zap.Config{
|
||||
Level: level,
|
||||
Encoding: "console",
|
||||
Development: debug,
|
||||
DisableStacktrace: true,
|
||||
DisableCaller: true,
|
||||
EncoderConfig: zapcore.EncoderConfig{
|
||||
TimeKey: "Time",
|
||||
LevelKey: "Level",
|
||||
NameKey: "Name",
|
||||
CallerKey: "Caller",
|
||||
MessageKey: "Msg",
|
||||
StacktraceKey: "St",
|
||||
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
},
|
||||
OutputPaths: []string{"stdout"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
|
||||
// High-priority output should also go to standard error, and low-priority
|
||||
// output should also go to standard out.
|
||||
consoleLogs := zapcore.Lock(os.Stdout)
|
||||
consoleErrors := zapcore.Lock(os.Stderr)
|
||||
if disable {
|
||||
myConfig.OutputPaths = []string{os.DevNull}
|
||||
myConfig.ErrorOutputPaths = []string{os.DevNull}
|
||||
devNull, err := os.Create(os.DevNull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Discard low-priority output
|
||||
consoleLogs = zapcore.Lock(devNull)
|
||||
}
|
||||
|
||||
logger, err := myConfig.Build()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to build zap config: %w", err)
|
||||
core := zapcore.NewTee(
|
||||
zapcore.NewCore(consoleEncoder, consoleErrors, errorPriority),
|
||||
zapcore.NewCore(consoleEncoder, consoleLogs, logPriority),
|
||||
)
|
||||
|
||||
opts := []zap.Option{zap.ErrorOutput(zapcore.Lock(os.Stderr))}
|
||||
if debug {
|
||||
opts = append(opts, zap.Development())
|
||||
}
|
||||
logger := zap.New(core, opts...)
|
||||
|
||||
return logger.Sugar(), nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -11,9 +13,10 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
@@ -22,26 +25,44 @@ type Results []Result
|
||||
type Result struct {
|
||||
Target string `json:"Target"`
|
||||
Type string `json:"Type,omitempty"`
|
||||
Packages []ftypes.Package `json:"Packages,omitempty"`
|
||||
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities"`
|
||||
}
|
||||
|
||||
func WriteResults(format string, output io.Writer, results Results, outputTemplate string, light bool) error {
|
||||
if strings.HasPrefix(outputTemplate, "@") {
|
||||
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Error retrieving template from path: %w", err)
|
||||
}
|
||||
outputTemplate = string(buf)
|
||||
}
|
||||
|
||||
func WriteResults(format string, output io.Writer, severities []dbTypes.Severity, results Results, outputTemplate string, light bool) error {
|
||||
var writer Writer
|
||||
switch format {
|
||||
case "table":
|
||||
writer = &TableWriter{Output: output, Light: light}
|
||||
writer = &TableWriter{Output: output, Light: light, Severities: severities}
|
||||
case "json":
|
||||
writer = &JsonWriter{Output: output}
|
||||
case "template":
|
||||
tmpl, err := template.New("output template").Parse(outputTemplate)
|
||||
if strings.HasPrefix(outputTemplate, "@") {
|
||||
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Error retrieving template from path: %w", err)
|
||||
}
|
||||
outputTemplate = string(buf)
|
||||
}
|
||||
tmpl, err := template.New("output template").Funcs(template.FuncMap{
|
||||
"escapeXML": func(input string) string {
|
||||
escaped := &bytes.Buffer{}
|
||||
if err := xml.EscapeText(escaped, []byte(input)); err != nil {
|
||||
fmt.Printf("error while escapeString to XML: %v", err.Error())
|
||||
return input
|
||||
}
|
||||
return escaped.String()
|
||||
},
|
||||
"endWithPeriod": func(input string) string {
|
||||
if !strings.HasSuffix(input, ".") {
|
||||
input += "."
|
||||
}
|
||||
return input
|
||||
},
|
||||
"toLower": func(input string) string {
|
||||
return strings.ToLower(input)
|
||||
},
|
||||
}).Parse(outputTemplate)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error parsing template: %w", err)
|
||||
}
|
||||
@@ -61,8 +82,9 @@ type Writer interface {
|
||||
}
|
||||
|
||||
type TableWriter struct {
|
||||
Output io.Writer
|
||||
Light bool
|
||||
Severities []dbTypes.Severity
|
||||
Output io.Writer
|
||||
Light bool
|
||||
}
|
||||
|
||||
func (tw TableWriter) Write(results Results) error {
|
||||
@@ -106,7 +128,16 @@ func (tw TableWriter) write(result Result) {
|
||||
}
|
||||
|
||||
var results []string
|
||||
|
||||
var severities []string
|
||||
for _, sev := range tw.Severities {
|
||||
severities = append(severities, sev.String())
|
||||
}
|
||||
|
||||
for _, severity := range dbTypes.SeverityNames {
|
||||
if !utils.StringInSlice(severity, severities) {
|
||||
continue
|
||||
}
|
||||
r := fmt.Sprintf("%s: %d", severity, severityCount[severity])
|
||||
results = append(results, r)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestReportWriter_Table(t *testing.T) {
|
||||
},
|
||||
}
|
||||
tableWritten := bytes.Buffer{}
|
||||
assert.NoError(t, report.WriteResults("table", &tableWritten, inputResults, "", tc.light), tc.name)
|
||||
assert.NoError(t, report.WriteResults("table", &tableWritten, nil, inputResults, "", tc.light), tc.name)
|
||||
assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name)
|
||||
})
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func TestReportWriter_JSON(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, report.WriteResults("json", &jsonWritten, inputResults, "", false), tc.name)
|
||||
assert.NoError(t, report.WriteResults("json", &jsonWritten, nil, inputResults, "", false), tc.name)
|
||||
|
||||
writtenResults := report.Results{}
|
||||
errJson := json.Unmarshal([]byte(jsonWritten.String()), &writtenResults)
|
||||
@@ -230,18 +230,85 @@ func TestReportWriter_Template(t *testing.T) {
|
||||
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
|
||||
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
|
||||
Description: `curl version curl 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
template: `<testsuites>
|
||||
{{- range . -}}
|
||||
{{- $failures := len .Vulnerabilities }}
|
||||
<testsuite tests="1" failures="{{ $failures }}" time="" name="{{ .Target }}">
|
||||
{{- if not (eq .Type "") }}
|
||||
<properties>
|
||||
<property name="type" value="{{ .Type }}"></property>
|
||||
</properties>
|
||||
{{- end -}}
|
||||
{{ range .Vulnerabilities }}
|
||||
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
|
||||
<failure message={{escapeXML .Title | printf "%q" }} type="description">{{escapeXML .Description | printf "%q" }}</failure>
|
||||
</testcase>
|
||||
{{- end }}
|
||||
</testsuite>
|
||||
{{- end }}
|
||||
</testsuites>`,
|
||||
|
||||
expected: `<testsuites>
|
||||
<testsuite tests="1" failures="1" time="" name="foojunit">
|
||||
<properties>
|
||||
<property name="type" value="test"></property>
|
||||
</properties>
|
||||
<testcase classname="foo-1.2.3" name="[HIGH] 123" time="">
|
||||
<failure message="gcc: POWER9 "DARN" RNG intrinsic produces repeated output" type="description">"curl version curl 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0."</failure>
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>`,
|
||||
},
|
||||
{
|
||||
name: "happy path with/without period description should return with period",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "without period",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "with period.",
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod .Description | printf "%q" }}{{ end }}{{ end }}`,
|
||||
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tmplWritten := bytes.Buffer{}
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foojson",
|
||||
Target: "foojunit",
|
||||
Type: "test",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, report.WriteResults("template", &tmplWritten, inputResults, tc.template, false))
|
||||
assert.NoError(t, report.WriteResults("template", &tmplWritten, nil, inputResults, tc.template, false))
|
||||
assert.Equal(t, tc.expected, tmplWritten.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,6 +153,20 @@ func TestScanner_Scan(t *testing.T) {
|
||||
Severity: common.Severity_CRITICAL,
|
||||
References: []string{"http://exammple.com"},
|
||||
SeveritySource: "nvd",
|
||||
Cvss: map[string]*common.CVSS{
|
||||
"nvd": {
|
||||
V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 7.2,
|
||||
V3Score: 7.8,
|
||||
},
|
||||
"redhat": {
|
||||
V2Vector: "AV:H/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 4.2,
|
||||
V3Score: 2.8,
|
||||
},
|
||||
},
|
||||
Layer: &common.Layer{
|
||||
DiffId: "sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10",
|
||||
},
|
||||
@@ -177,6 +191,20 @@ func TestScanner_Scan(t *testing.T) {
|
||||
Description: "Denial os Service",
|
||||
Severity: "CRITICAL",
|
||||
References: []string{"http://exammple.com"},
|
||||
CVSS: dbTypes.VendorCVSS{
|
||||
"nvd": {
|
||||
V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 7.2,
|
||||
V3Score: 7.8,
|
||||
},
|
||||
"redhat": {
|
||||
V2Vector: "AV:H/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 4.2,
|
||||
V3Score: 2.8,
|
||||
},
|
||||
},
|
||||
},
|
||||
SeveritySource: "nvd",
|
||||
Layer: ftypes.Layer{
|
||||
|
||||
@@ -101,6 +101,15 @@ func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
|
||||
if err != nil {
|
||||
log.Logger.Warn(err)
|
||||
}
|
||||
cvssMap := make(map[string]*common.CVSS) // This is needed because protobuf generates a map[string]*CVSS type
|
||||
for vendor, vendorSeverity := range vuln.CVSS {
|
||||
cvssMap[vendor] = &common.CVSS{
|
||||
V2Vector: vendorSeverity.V2Vector,
|
||||
V3Vector: vendorSeverity.V3Vector,
|
||||
V2Score: vendorSeverity.V2Score,
|
||||
V3Score: vendorSeverity.V3Score,
|
||||
}
|
||||
}
|
||||
|
||||
rpcVulns = append(rpcVulns, &common.Vulnerability{
|
||||
VulnerabilityId: vuln.VulnerabilityID,
|
||||
@@ -115,6 +124,7 @@ func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
|
||||
Digest: vuln.Layer.Digest,
|
||||
DiffId: vuln.Layer.DiffID,
|
||||
},
|
||||
Cvss: cvssMap,
|
||||
SeveritySource: vuln.SeveritySource,
|
||||
})
|
||||
}
|
||||
@@ -128,6 +138,16 @@ func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
|
||||
var vulns []types.DetectedVulnerability
|
||||
for _, vuln := range result.Vulnerabilities {
|
||||
severity := dbTypes.Severity(vuln.Severity)
|
||||
cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
|
||||
for vendor, vendorSeverity := range vuln.Cvss {
|
||||
cvssMap[vendor] = dbTypes.CVSS{
|
||||
V2Vector: vendorSeverity.V2Vector,
|
||||
V3Vector: vendorSeverity.V3Vector,
|
||||
V2Score: vendorSeverity.V2Score,
|
||||
V3Score: vendorSeverity.V3Score,
|
||||
}
|
||||
}
|
||||
|
||||
vulns = append(vulns, types.DetectedVulnerability{
|
||||
VulnerabilityID: vuln.VulnerabilityId,
|
||||
PkgName: vuln.PkgName,
|
||||
@@ -137,6 +157,7 @@ func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
|
||||
Title: vuln.Title,
|
||||
Description: vuln.Description,
|
||||
Severity: severity.String(),
|
||||
CVSS: cvssMap,
|
||||
References: vuln.References,
|
||||
},
|
||||
Layer: ftypes.Layer{
|
||||
|
||||
@@ -252,7 +252,15 @@ func TestConvertToRpcVulns(t *testing.T) {
|
||||
Title: "DoS",
|
||||
Description: "Denial of Service",
|
||||
Severity: "MEDIUM",
|
||||
References: []string{"http://example.com"},
|
||||
CVSS: dbTypes.VendorCVSS{
|
||||
"redhat": {
|
||||
V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 7.2,
|
||||
V3Score: 7.8,
|
||||
},
|
||||
},
|
||||
References: []string{"http://example.com"},
|
||||
},
|
||||
Layer: ftypes.Layer{
|
||||
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
|
||||
@@ -270,7 +278,15 @@ func TestConvertToRpcVulns(t *testing.T) {
|
||||
Title: "DoS",
|
||||
Description: "Denial of Service",
|
||||
Severity: common.Severity_MEDIUM,
|
||||
References: []string{"http://example.com"},
|
||||
Cvss: map[string]*common.CVSS{
|
||||
"redhat": {
|
||||
V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
V2Score: 7.2,
|
||||
V3Score: 7.8,
|
||||
},
|
||||
},
|
||||
References: []string{"http://example.com"},
|
||||
Layer: &common.Layer{
|
||||
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
|
||||
DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
|
||||
@@ -309,6 +325,7 @@ func TestConvertToRpcVulns(t *testing.T) {
|
||||
Title: "DoS",
|
||||
Description: "Denial of Service",
|
||||
Severity: common.Severity_UNKNOWN,
|
||||
Cvss: make(map[string]*common.CVSS),
|
||||
References: []string{"http://example.com"},
|
||||
Layer: &common.Layer{
|
||||
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
detectExpectation library.OperationDetectExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
wantRes *proto.DetectResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -80,8 +80,8 @@ func TestServer_Detect(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
@@ -112,6 +112,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
Title: "title",
|
||||
Description: "description",
|
||||
Severity: common.Severity_MEDIUM,
|
||||
Cvss: make(map[string]*common.CVSS),
|
||||
References: []string{"http://example.com"},
|
||||
Layer: &common.Layer{
|
||||
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
detectExpectation ospkg.DetectExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
wantRes *proto.DetectResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -73,8 +73,8 @@ func TestServer_Detect(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
@@ -96,6 +96,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
VulnerabilityId: "CVE-2019-0001",
|
||||
PkgName: "musl",
|
||||
Severity: common.Severity_HIGH,
|
||||
Cvss: make(map[string]*common.CVSS),
|
||||
Layer: &common.Layer{
|
||||
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
|
||||
DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/rpc"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/local"
|
||||
@@ -78,17 +77,9 @@ func (s *CacheServer) PutBlob(_ context.Context, in *rpcCache.PutBlobRequest) (*
|
||||
}
|
||||
|
||||
func (s *CacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBlobsRequest) (*rpcCache.MissingBlobsResponse, error) {
|
||||
var layerIDs []string
|
||||
for _, blobID := range in.BlobIds {
|
||||
l, err := s.cache.GetBlob(blobID)
|
||||
if err != nil || l.SchemaVersion != ftypes.BlobJSONSchemaVersion {
|
||||
layerIDs = append(layerIDs, blobID)
|
||||
}
|
||||
missingArtifact, blobIDs, err := s.cache.MissingBlobs(in.ArtifactId, in.BlobIds)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get missing blobs: %w", err)
|
||||
}
|
||||
var missingImage bool
|
||||
img, err := s.cache.GetArtifact(in.ArtifactId)
|
||||
if err != nil || img.SchemaVersion != ftypes.ArtifactJSONSchemaVersion {
|
||||
missingImage = true
|
||||
}
|
||||
return &rpcCache.MissingBlobsResponse{MissingArtifact: missingImage, MissingBlobIds: layerIDs}, nil
|
||||
return &rpcCache.MissingBlobsResponse{MissingArtifact: missingArtifact, MissingBlobIds: blobIDs}, nil
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
scanExpectation scanner.DriverScanExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
want *rpcScanner.ScanResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -82,8 +82,8 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
@@ -114,6 +114,7 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
FixedVersion: "1.2.4",
|
||||
SeveritySource: "nvd",
|
||||
Layer: &common.Layer{},
|
||||
Cvss: make(map[string]*common.CVSS),
|
||||
},
|
||||
},
|
||||
Type: "alpine",
|
||||
@@ -473,12 +474,11 @@ func TestCacheServer_MissingBlobs(t *testing.T) {
|
||||
in *rpcCache.MissingBlobsRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
getLayerExpectations []cache.LocalArtifactCacheGetBlobExpectation
|
||||
getImageExpectations []cache.LocalArtifactCacheGetArtifactExpectation
|
||||
want *rpcCache.MissingBlobsResponse
|
||||
wantErr string
|
||||
name string
|
||||
args args
|
||||
getArtifactCacheMissingBlobsExpectations []cache.ArtifactCacheMissingBlobsExpectation
|
||||
want *rpcCache.MissingBlobsResponse
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
@@ -491,100 +491,24 @@ func TestCacheServer_MissingBlobs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
getLayerExpectations: []cache.LocalArtifactCacheGetBlobExpectation{
|
||||
getArtifactCacheMissingBlobsExpectations: []cache.ArtifactCacheMissingBlobsExpectation{
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetBlobArgs{
|
||||
BlobID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetBlobReturns{
|
||||
BlobInfo: ftypes.BlobInfo{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetBlobArgs{
|
||||
BlobID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetBlobReturns{
|
||||
BlobInfo: ftypes.BlobInfo{
|
||||
SchemaVersion: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
getImageExpectations: []cache.LocalArtifactCacheGetArtifactExpectation{
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetArtifactArgs{
|
||||
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetArtifactReturns{
|
||||
ArtifactInfo: ftypes.ArtifactInfo{
|
||||
SchemaVersion: 1,
|
||||
},
|
||||
},
|
||||
Args: cache.ArtifactCacheMissingBlobsArgs{ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
|
||||
BlobIDs: []string{"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5"}},
|
||||
Returns: cache.ArtifactCacheMissingBlobsReturns{
|
||||
MissingArtifact: false, MissingBlobIDs: []string{"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5"}, Err: nil},
|
||||
},
|
||||
},
|
||||
want: &rpcCache.MissingBlobsResponse{
|
||||
MissingArtifact: false,
|
||||
MissingBlobIds: []string{"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "schema version doesn't match",
|
||||
args: args{
|
||||
in: &rpcCache.MissingBlobsRequest{
|
||||
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
|
||||
BlobIds: []string{
|
||||
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
|
||||
},
|
||||
},
|
||||
},
|
||||
getLayerExpectations: []cache.LocalArtifactCacheGetBlobExpectation{
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetBlobArgs{
|
||||
BlobID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetBlobReturns{
|
||||
BlobInfo: ftypes.BlobInfo{
|
||||
SchemaVersion: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetBlobArgs{
|
||||
BlobID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetBlobReturns{
|
||||
BlobInfo: ftypes.BlobInfo{
|
||||
SchemaVersion: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
getImageExpectations: []cache.LocalArtifactCacheGetArtifactExpectation{
|
||||
{
|
||||
Args: cache.LocalArtifactCacheGetArtifactArgs{
|
||||
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
|
||||
},
|
||||
Returns: cache.LocalArtifactCacheGetArtifactReturns{
|
||||
ArtifactInfo: ftypes.ArtifactInfo{},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &rpcCache.MissingBlobsResponse{
|
||||
MissingArtifact: true,
|
||||
MissingBlobIds: []string{
|
||||
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
|
||||
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
|
||||
},
|
||||
MissingBlobIds: []string{"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockCache := new(mockCache)
|
||||
mockCache.ApplyGetBlobExpectations(tt.getLayerExpectations)
|
||||
mockCache.ApplyGetArtifactExpectations(tt.getImageExpectations)
|
||||
mockCache.ApplyMissingBlobsExpectations(tt.getArtifactCacheMissingBlobsExpectations)
|
||||
|
||||
s := NewCacheServer(mockCache)
|
||||
got, err := s.MissingBlobs(tt.args.ctx, tt.args.in)
|
||||
@@ -597,7 +521,7 @@ func TestCacheServer_MissingBlobs(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
mockCache.MockLocalArtifactCache.AssertExpectations(t)
|
||||
mockCache.MockArtifactCache.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package local
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/wire"
|
||||
@@ -98,12 +99,18 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
|
||||
return nil, nil, false, xerrors.Errorf("failed to scan OS packages: %w", err)
|
||||
}
|
||||
if result != nil {
|
||||
if options.ListAllPackages {
|
||||
sort.Slice(pkgs, func(i, j int) bool {
|
||||
return strings.Compare(pkgs[i].Name, pkgs[j].Name) <= 0
|
||||
})
|
||||
result.Packages = pkgs
|
||||
}
|
||||
results = append(results, *result)
|
||||
}
|
||||
}
|
||||
|
||||
if utils.StringInSlice("library", options.VulnType) {
|
||||
libResults, err := s.scanLibrary(imageDetail.Applications)
|
||||
libResults, err := s.scanLibrary(imageDetail.Applications, options.ListAllPackages)
|
||||
if err != nil {
|
||||
return nil, nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
|
||||
}
|
||||
@@ -133,19 +140,33 @@ func (s Scanner) scanOSPkg(target, osFamily, osName string, pkgs []ftypes.Packag
|
||||
return result, eosl, nil
|
||||
}
|
||||
|
||||
func (s Scanner) scanLibrary(apps []ftypes.Application) (report.Results, error) {
|
||||
func (s Scanner) scanLibrary(apps []ftypes.Application, listAllPackages bool) (report.Results, error) {
|
||||
var results report.Results
|
||||
for _, app := range apps {
|
||||
vulns, err := s.libDetector.Detect("", app.FilePath, time.Time{}, app.Libraries)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err)
|
||||
}
|
||||
|
||||
results = append(results, report.Result{
|
||||
libReport := report.Result{
|
||||
Target: app.FilePath,
|
||||
Vulnerabilities: vulns,
|
||||
Type: app.Type,
|
||||
})
|
||||
}
|
||||
if listAllPackages {
|
||||
var pkgs []ftypes.Package
|
||||
for _, lib := range app.Libraries {
|
||||
pkgs = append(pkgs, ftypes.Package{
|
||||
Name: lib.Library.Name,
|
||||
Version: lib.Library.Version,
|
||||
Layer: lib.Layer,
|
||||
})
|
||||
}
|
||||
sort.Slice(pkgs, func(i, j int) bool {
|
||||
return strings.Compare(pkgs[i].Name, pkgs[j].Name) <= 0
|
||||
})
|
||||
libReport.Packages = pkgs
|
||||
}
|
||||
results = append(results, libReport)
|
||||
}
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].Target < results[j].Target
|
||||
|
||||
@@ -173,6 +173,184 @@ func TestScanner_Scan(t *testing.T) {
|
||||
Name: "3.11",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with list all packages",
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}, ListAllPackages: true},
|
||||
},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
Args: ApplierApplyLayersArgs{
|
||||
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
},
|
||||
Returns: ApplierApplyLayersReturns{
|
||||
Detail: ftypes.ArtifactDetail{
|
||||
OS: &ftypes.OS{
|
||||
Family: "alpine",
|
||||
Name: "3.11",
|
||||
},
|
||||
Packages: []ftypes.Package{
|
||||
{
|
||||
Name: "musl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ausl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:bbf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
},
|
||||
Applications: []ftypes.Application{
|
||||
{
|
||||
Type: "bundler",
|
||||
FilePath: "/app/Gemfile.lock",
|
||||
Libraries: []ftypes.LibraryInfo{
|
||||
{
|
||||
Library: dtypes.Library{Name: "rails", Version: "6.0"},
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ospkgDetectExpectations: []OspkgDetectorDetectExpectation{
|
||||
{
|
||||
Args: OspkgDetectorDetectArgs{
|
||||
OsFamily: "alpine",
|
||||
OsName: "3.11",
|
||||
Pkgs: []ftypes.Package{
|
||||
{
|
||||
Name: "musl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ausl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:bbf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Returns: OspkgDetectorDetectReturns{
|
||||
DetectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-9999",
|
||||
PkgName: "musl",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
},
|
||||
Eosl: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
libDetectExpectations: []LibraryDetectorDetectExpectation{
|
||||
{
|
||||
Args: LibraryDetectorDetectArgs{
|
||||
FilePath: "/app/Gemfile.lock",
|
||||
Pkgs: []ftypes.LibraryInfo{
|
||||
{
|
||||
Library: dtypes.Library{Name: "rails", Version: "6.0"},
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Returns: LibraryDetectorDetectReturns{
|
||||
DetectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-10000",
|
||||
PkgName: "rails",
|
||||
InstalledVersion: "6.0",
|
||||
FixedVersion: "6.1",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantResults: report.Results{
|
||||
{
|
||||
Target: "alpine:latest (alpine 3.11)",
|
||||
Packages: []ftypes.Package{
|
||||
{
|
||||
Name: "ausl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:bbf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "musl",
|
||||
Version: "1.2.3",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
},
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-9999",
|
||||
PkgName: "musl",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:ebf12965380b39889c99a9c02e82ba465f887b45975b6e389d42e9e6a3857888",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: vulnerability.Alpine,
|
||||
},
|
||||
{
|
||||
Target: "/app/Gemfile.lock",
|
||||
Packages: []ftypes.Package{
|
||||
{
|
||||
Name: "rails",
|
||||
Version: "6.0",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
|
||||
},
|
||||
},
|
||||
},
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-10000",
|
||||
PkgName: "rails",
|
||||
InstalledVersion: "6.0",
|
||||
FixedVersion: "6.1",
|
||||
Layer: ftypes.Layer{
|
||||
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: "bundler",
|
||||
},
|
||||
},
|
||||
wantOS: &ftypes.OS{
|
||||
Family: "alpine",
|
||||
Name: "3.11",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with empty os",
|
||||
args: args{
|
||||
|
||||
@@ -2,27 +2,55 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"github.com/aquasecurity/fanal/types"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/knqyf263/go-version"
|
||||
)
|
||||
|
||||
var (
|
||||
replacer = strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc")
|
||||
replacer = strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc", "==", "=")
|
||||
preReleaseSplitter = regexp.MustCompile(`(?P<Number>^[0-9]+)(?P<PreRelease>[a-z]*.*)`)
|
||||
)
|
||||
|
||||
func MatchVersions(currentVersion *version.Version, rangeVersions []string) bool {
|
||||
for _, p := range rangeVersions {
|
||||
c, err := version.NewConstraint(replacer.Replace(p))
|
||||
func MatchVersions(currentVersion *semver.Version, rangeVersions []string) bool {
|
||||
for _, v := range rangeVersions {
|
||||
v = replacer.Replace(v)
|
||||
constraintParts := strings.Split(v, ",")
|
||||
for j := range constraintParts {
|
||||
constraintParts[j] = FormatPatchVersion(constraintParts[j])
|
||||
}
|
||||
v = strings.Join(constraintParts, ",")
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
c, err := semver.NewConstraint(v)
|
||||
if err != nil {
|
||||
log.Logger.Debug("NewConstraint", "error", err)
|
||||
return false
|
||||
continue
|
||||
}
|
||||
if c.Check(currentVersion) {
|
||||
// Validate a version against a constraint.
|
||||
valid, msgs := c.Validate(currentVersion)
|
||||
if valid {
|
||||
return true
|
||||
}
|
||||
for _, m := range msgs {
|
||||
// re-validate after removing the patch version
|
||||
if strings.HasSuffix(m.Error(), "is a prerelease version and the constraint is only looking for release versions") {
|
||||
v2, err := semver.NewVersion(fmt.Sprintf("%v.%v.%v", currentVersion.Major(), currentVersion.Minor(), currentVersion.Patch()))
|
||||
if err == nil {
|
||||
valid, _ = c.Validate(v2)
|
||||
if valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -35,6 +63,33 @@ func FormatSrcVersion(pkg types.Package) string {
|
||||
return formatVersion(pkg.SrcEpoch, pkg.SrcVersion, pkg.SrcRelease)
|
||||
}
|
||||
|
||||
func FormatPatchVersion(version string) string {
|
||||
part := strings.Split(version, ".")
|
||||
if len(part) > 3 {
|
||||
if _, err := strconv.Atoi(part[2]); err == nil {
|
||||
version = strings.Join(part[:3], ".") + "-" + strings.Join(part[3:], ".")
|
||||
}
|
||||
} else {
|
||||
for i := range part {
|
||||
res := preReleaseSplitter.FindStringSubmatch(part[i])
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
number := res[1]
|
||||
preRelease := res[2]
|
||||
if preRelease != "" {
|
||||
if !strings.HasPrefix(preRelease, "-") {
|
||||
preRelease = "-" + preRelease
|
||||
}
|
||||
part[i] = number + preRelease
|
||||
break
|
||||
}
|
||||
}
|
||||
version = strings.Join(part, ".")
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
func formatVersion(epoch int, version, release string) string {
|
||||
v := version
|
||||
if release != "" {
|
||||
|
||||
163
pkg/scanner/utils/utils_test.go
Normal file
163
pkg/scanner/utils/utils_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatchVersions(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
currentVersion string
|
||||
rangeVersion []string
|
||||
expectedCheck bool
|
||||
}{
|
||||
{
|
||||
name: "pass: expect true when os/machine is in version string",
|
||||
currentVersion: "1.9.25-x86-mingw32",
|
||||
rangeVersion: []string{`>= 1.9.24`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "pass: expect true when language is in version string",
|
||||
currentVersion: "1.8.6-java",
|
||||
rangeVersion: []string{`~> 1.5.5`, `~> 1.6.8`, `>= 1.7.7`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "expect false",
|
||||
currentVersion: "1.9.23-x86-mingw32",
|
||||
rangeVersion: []string{`>= 1.9.24`},
|
||||
expectedCheck: false,
|
||||
},
|
||||
{
|
||||
// passes if (>= 1.2.3, < 2.0.0)
|
||||
name: "expect false",
|
||||
currentVersion: "1.2.4",
|
||||
rangeVersion: []string{`^1.2.3`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
// passes if (>= 1.2.3, < 2.0.0)
|
||||
name: "expect false",
|
||||
currentVersion: "2.0.0",
|
||||
rangeVersion: []string{`^1.2.3`},
|
||||
expectedCheck: false,
|
||||
},
|
||||
{
|
||||
// passes if (>= 2.0.18, < 3.0.0) || (>= 3.1.16, < 4.0.0) || (>= 4.0.8, < 5.0.0) || ( >=5.0.0,<6.0.0)
|
||||
name: "expect false",
|
||||
currentVersion: "3.1.16",
|
||||
rangeVersion: []string{`^2.0.18 || ^3.1.6 || ^4.0.8 || ^5.0.0-beta.5`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
// passes if (>= 2.0.18, < 3.0.0) || (>= 3.1.16, < 4.0.0) || (>= 4.0.8, < 5.0.0) || ( >=5.0.0,<6.0.0)
|
||||
name: "expect false",
|
||||
currentVersion: "6.0.0",
|
||||
rangeVersion: []string{`^2.0.18 || ^3.1.6 || ^4.0.8 || ^5.0.0-beta.5`},
|
||||
expectedCheck: false,
|
||||
},
|
||||
{
|
||||
// passes if (>= 2.0.18, < 3.0.0) || (>= 3.1.16, < 4.0.0) || (>= 4.0.8, < 5.0.0) || ( >=5.0.0,<6.0.0)
|
||||
name: "expect false",
|
||||
currentVersion: "5.0.0-beta.5",
|
||||
rangeVersion: []string{`^2.0.18 || ^3.1.6 || ^4.0.8 || ^5.0.0-beta.5`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
// Ruby GEM with more dots
|
||||
name: "expect false",
|
||||
currentVersion: "1.10.9-java",
|
||||
rangeVersion: []string{`>= 1.6.7.1`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "expect prerelease suffixed in minor version to work",
|
||||
currentVersion: "4.1a",
|
||||
rangeVersion: []string{`< 4.2b1`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "expect prerelease suffixed in patch version to work",
|
||||
currentVersion: "4.1.2a",
|
||||
rangeVersion: []string{`< 4.2b1`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
{
|
||||
name: "expect prerelease suffixed in patch version to work in failing case",
|
||||
currentVersion: "4.1.2c",
|
||||
rangeVersion: []string{`<= 4.1.2b`},
|
||||
expectedCheck: false,
|
||||
},
|
||||
{
|
||||
name: "expect double equal to work",
|
||||
currentVersion: "1.7",
|
||||
rangeVersion: []string{`==1.7`},
|
||||
expectedCheck: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v, err := semver.NewVersion(FormatPatchVersion(tc.currentVersion))
|
||||
require.NoError(t, err)
|
||||
match := MatchVersions(v, tc.rangeVersion)
|
||||
assert.Equal(t, tc.expectedCheck, match)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatPatchVersio(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
currentVersion string
|
||||
expectedVersion string
|
||||
}{
|
||||
{
|
||||
name: "patch with no dots should return version should be unchanged",
|
||||
currentVersion: "1.2.3-beta",
|
||||
expectedVersion: "1.2.3-beta",
|
||||
},
|
||||
{
|
||||
name: "patch with dots after non-integer patch version should be unchanged",
|
||||
currentVersion: "1.2.3-beta.1",
|
||||
expectedVersion: "1.2.3-beta.1",
|
||||
},
|
||||
{
|
||||
name: "patch with dots after integer patch version should append dash and join rest versions parts",
|
||||
currentVersion: "1.2.3.4",
|
||||
expectedVersion: "1.2.3-4",
|
||||
},
|
||||
{
|
||||
name: "patch with dots after integer patch version should append dash and join extra versions parts",
|
||||
currentVersion: "1.2.3.4.5",
|
||||
expectedVersion: "1.2.3-4.5",
|
||||
},
|
||||
{
|
||||
name: "unchanged case",
|
||||
currentVersion: "1.2.3.4-5",
|
||||
expectedVersion: "1.2.3-4-5",
|
||||
},
|
||||
{
|
||||
name: "prerelease suffixed in minor",
|
||||
currentVersion: "1.11a",
|
||||
expectedVersion: "1.11-a",
|
||||
},
|
||||
{
|
||||
name: "prerelease suffixed in patch",
|
||||
currentVersion: "1.11.5rc",
|
||||
expectedVersion: "1.11.5-rc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := FormatPatchVersion(tc.currentVersion)
|
||||
assert.Equal(t, tc.expectedVersion, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@ package types
|
||||
type ScanOptions struct {
|
||||
VulnType []string
|
||||
ScanRemovedPackages bool
|
||||
ListAllPackages bool
|
||||
}
|
||||
|
||||
@@ -9,11 +9,10 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func touch(t *testing.T, name string) {
|
||||
|
||||
@@ -2,27 +2,32 @@
|
||||
|
||||
package vulnerability
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
import types "github.com/aquasecurity/trivy/pkg/types"
|
||||
import (
|
||||
context "context"
|
||||
|
||||
pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
// MockOperation is an autogenerated mock type for the Operation type
|
||||
type MockOperation struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type FillInfoArgs struct {
|
||||
type OperationFillInfoArgs struct {
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
ReportType string
|
||||
ReportTypeAnything bool
|
||||
}
|
||||
|
||||
type FillInfoExpectation struct {
|
||||
Args FillInfoArgs
|
||||
type OperationFillInfoExpectation struct {
|
||||
Args OperationFillInfoArgs
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectation(e FillInfoExpectation) {
|
||||
func (_m *MockOperation) ApplyFillInfoExpectation(e OperationFillInfoExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
@@ -37,7 +42,7 @@ func (_m *MockOperation) ApplyFillInfoExpectation(e FillInfoExpectation) {
|
||||
_m.On("FillInfo", args...)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []FillInfoExpectation) {
|
||||
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []OperationFillInfoExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFillInfoExpectation(e)
|
||||
}
|
||||
@@ -48,7 +53,9 @@ func (_m *MockOperation) FillInfo(vulns []types.DetectedVulnerability, reportTyp
|
||||
_m.Called(vulns, reportType)
|
||||
}
|
||||
|
||||
type FilterArgs struct {
|
||||
type OperationFilterArgs struct {
|
||||
Ctx context.Context
|
||||
CtxAnything bool
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
Severities []pkgtypes.Severity
|
||||
@@ -57,19 +64,27 @@ type FilterArgs struct {
|
||||
IgnoreUnfixedAnything bool
|
||||
IgnoreFile string
|
||||
IgnoreFileAnything bool
|
||||
Policy string
|
||||
PolicyAnything bool
|
||||
}
|
||||
|
||||
type FilterReturns struct {
|
||||
type OperationFilterReturns struct {
|
||||
_a0 []types.DetectedVulnerability
|
||||
_a1 error
|
||||
}
|
||||
|
||||
type FilterExpectation struct {
|
||||
Args FilterArgs
|
||||
Returns FilterReturns
|
||||
type OperationFilterExpectation struct {
|
||||
Args OperationFilterArgs
|
||||
Returns OperationFilterReturns
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectation(e FilterExpectation) {
|
||||
func (_m *MockOperation) ApplyFilterExpectation(e OperationFilterExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.CtxAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Ctx)
|
||||
}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
@@ -90,27 +105,39 @@ func (_m *MockOperation) ApplyFilterExpectation(e FilterExpectation) {
|
||||
} else {
|
||||
args = append(args, e.Args.IgnoreFile)
|
||||
}
|
||||
_m.On("Filter", args...).Return(e.Returns._a0)
|
||||
if e.Args.PolicyAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Policy)
|
||||
}
|
||||
_m.On("Filter", args...).Return(e.Returns._a0, e.Returns._a1)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectations(expectations []FilterExpectation) {
|
||||
func (_m *MockOperation) ApplyFilterExpectations(expectations []OperationFilterExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFilterExpectation(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter provides a mock function with given fields: vulns, severities, ignoreUnfixed, ignoreFile
|
||||
func (_m *MockOperation) Filter(vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
|
||||
ret := _m.Called(vulns, severities, ignoreUnfixed, ignoreFile)
|
||||
// Filter provides a mock function with given fields: ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy
|
||||
func (_m *MockOperation) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error) {
|
||||
ret := _m.Called(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
|
||||
var r0 []types.DetectedVulnerability
|
||||
if rf, ok := ret.Get(0).(func([]types.DetectedVulnerability, []pkgtypes.Severity, bool, string) []types.DetectedVulnerability); ok {
|
||||
r0 = rf(vulns, severities, ignoreUnfixed, ignoreFile)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) []types.DetectedVulnerability); ok {
|
||||
r0 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]types.DetectedVulnerability)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) error); ok {
|
||||
r1 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
67
pkg/vulnerability/module.go
Normal file
67
pkg/vulnerability/module.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package vulnerability
|
||||
|
||||
const (
|
||||
module = `
|
||||
package lib.trivy
|
||||
|
||||
parse_cvss_vector_v3(cvss) = vector {
|
||||
s := split(cvss, "/")
|
||||
vector := {
|
||||
"AttackVector": attack_vector[s[1]],
|
||||
"AttackComplexity": attack_complexity[s[2]],
|
||||
"PrivilegesRequired": privileges_required[s[3]],
|
||||
"UserInteraction": user_interaction[s[4]],
|
||||
"Scope": scope[s[5]],
|
||||
"Confidentiality": confidentiality[s[6]],
|
||||
"Integrity": integrity[s[7]],
|
||||
"Availability": availability[s[8]],
|
||||
}
|
||||
}
|
||||
|
||||
attack_vector := {
|
||||
"AV:N": "Network",
|
||||
"AV:A": "Adjacent",
|
||||
"AV:L": "Local",
|
||||
"AV:P": "Physical",
|
||||
}
|
||||
|
||||
attack_complexity := {
|
||||
"AC:L": "Low",
|
||||
"AC:H": "High",
|
||||
}
|
||||
|
||||
privileges_required := {
|
||||
"PR:N": "None",
|
||||
"PR:L": "Low",
|
||||
"PR:H": "High",
|
||||
}
|
||||
|
||||
user_interaction := {
|
||||
"UI:N": "None",
|
||||
"UI:R": "Required",
|
||||
}
|
||||
|
||||
scope := {
|
||||
"S:U": "Unchanged",
|
||||
"S:C": "Changed",
|
||||
}
|
||||
|
||||
confidentiality := {
|
||||
"C:N": "None",
|
||||
"C:L": "Low",
|
||||
"C:H": "High",
|
||||
}
|
||||
|
||||
integrity := {
|
||||
"I:N": "None",
|
||||
"I:L": "Low",
|
||||
"I:H": "High",
|
||||
}
|
||||
|
||||
availability := {
|
||||
"A:N": "None",
|
||||
"A:L": "Low",
|
||||
"A:H": "High",
|
||||
}
|
||||
`
|
||||
)
|
||||
5
pkg/vulnerability/testdata/test.rego
vendored
Normal file
5
pkg/vulnerability/testdata/test.rego
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package trivy
|
||||
|
||||
ignore {
|
||||
input.VulnerabilityID != "CVE-2019-0001"
|
||||
}
|
||||
@@ -2,17 +2,19 @@ package vulnerability
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
|
||||
"github.com/google/wire"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
@@ -30,8 +32,8 @@ var SuperSet = wire.NewSet(
|
||||
|
||||
type Operation interface {
|
||||
FillInfo(vulns []types.DetectedVulnerability, reportType string)
|
||||
Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability
|
||||
Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
@@ -89,8 +91,8 @@ func (c Client) getVendorSeverity(vuln *types.DetectedVulnerability, reportType
|
||||
}
|
||||
}
|
||||
|
||||
func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
|
||||
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
ignoredIDs := getIgnoredIDs(ignoreFile)
|
||||
var vulnerabilities []types.DetectedVulnerability
|
||||
for _, vuln := range vulns {
|
||||
@@ -108,6 +110,15 @@ func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyFile != "" {
|
||||
var err error
|
||||
vulnerabilities, err = applyPolicy(ctx, vulnerabilities, policyFile)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to apply the policy: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(vulnerabilities, func(i, j int) bool {
|
||||
if vulnerabilities[i].PkgName != vulnerabilities[j].PkgName {
|
||||
return vulnerabilities[i].PkgName < vulnerabilities[j].PkgName
|
||||
@@ -120,7 +131,45 @@ func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes
|
||||
}
|
||||
return vulnerabilities[i].VulnerabilityID < vulnerabilities[j].VulnerabilityID
|
||||
})
|
||||
return vulnerabilities
|
||||
return vulnerabilities, nil
|
||||
}
|
||||
|
||||
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
policy, err := ioutil.ReadFile(policyFile)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to read the policy file: %w", err)
|
||||
}
|
||||
|
||||
query, err := rego.New(
|
||||
rego.Query("data.trivy.ignore"),
|
||||
rego.Module("lib.rego", module),
|
||||
rego.Module("trivy.rego", string(policy)),
|
||||
).PrepareForEval(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to prepare for eval: %w", err)
|
||||
}
|
||||
|
||||
var filtered []types.DetectedVulnerability
|
||||
for _, vuln := range vulns {
|
||||
results, err := query.Eval(ctx, rego.EvalInput(vuln))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to evaluate the policy: %w", err)
|
||||
} else if len(results) == 0 {
|
||||
// Handle undefined result.
|
||||
filtered = append(filtered, vuln)
|
||||
continue
|
||||
}
|
||||
ignore, ok := results[0].Expressions[0].Value.(bool)
|
||||
if !ok {
|
||||
// Handle unexpected result type.
|
||||
return nil, xerrors.New("the policy must return boolean")
|
||||
}
|
||||
if ignore {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, vuln)
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func getIgnoredIDs(ignoreFile string) []string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package vulnerability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
)
|
||||
@@ -146,7 +148,76 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and vendor vectors",
|
||||
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
|
||||
getVulnerability: []db.GetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.GetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.GetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityMedium.String(),
|
||||
CweIDs: []string{"CWE-311"},
|
||||
VendorSeverity: dbTypes.VendorSeverity{
|
||||
vulnerability.RedHat: dbTypes.SeverityLow, // CentOS uses RedHat
|
||||
},
|
||||
CVSS: map[string]dbTypes.CVSS{
|
||||
vulnerability.Nvd: {
|
||||
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
V2Score: 4.5,
|
||||
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 5.6,
|
||||
},
|
||||
vulnerability.RedHat: {
|
||||
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
V2Score: 7.8,
|
||||
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 9.8,
|
||||
},
|
||||
},
|
||||
References: []string{"http://example.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
},
|
||||
reportType: vulnerability.CentOS,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
CweIDs: []string{"CWE-311"},
|
||||
References: []string{"http://example.com"},
|
||||
CVSS: map[string]dbTypes.CVSS{
|
||||
vulnerability.Nvd: {
|
||||
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
V2Score: 4.5,
|
||||
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 5.6,
|
||||
},
|
||||
vulnerability.RedHat: {
|
||||
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
V2Score: 7.8,
|
||||
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 9.8,
|
||||
},
|
||||
},
|
||||
},
|
||||
SeveritySource: vulnerability.RedHat,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and deprecated vendor vectors",
|
||||
getVulnerability: []db.GetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.GetVulnerabilityArgs{
|
||||
@@ -322,6 +393,7 @@ func TestClient_Filter(t *testing.T) {
|
||||
severities []dbTypes.Severity
|
||||
ignoreUnfixed bool
|
||||
ignoreFile string
|
||||
policyFile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -497,11 +569,63 @@ func TestClient_Filter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with a policy file",
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this vulnerability is ignored
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this vulnerability is ignored
|
||||
VulnerabilityID: "CVE-2019-0003",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
ignoreUnfixed: false,
|
||||
policyFile: "./testdata/test.rego",
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := Client{}
|
||||
got := c.Filter(tt.args.vulns, tt.args.severities, tt.args.ignoreUnfixed, tt.args.ignoreFile)
|
||||
got, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities,
|
||||
tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got, tt.name)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -358,19 +358,20 @@ func (m *Library) GetVersion() string {
|
||||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
|
||||
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
|
||||
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
|
||||
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
|
||||
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.common.Severity" json:"severity,omitempty"`
|
||||
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
|
||||
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
|
||||
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
|
||||
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
|
||||
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
|
||||
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
|
||||
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.common.Severity" json:"severity,omitempty"`
|
||||
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
|
||||
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
|
||||
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
|
||||
Cvss map[string]*CVSS `protobuf:"bytes,12,rep,name=cvss,proto3" json:"cvss,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
|
||||
@@ -468,6 +469,13 @@ func (m *Vulnerability) GetSeveritySource() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Vulnerability) GetCvss() map[string]*CVSS {
|
||||
if m != nil {
|
||||
return m.Cvss
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Layer struct {
|
||||
Digest string `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
|
||||
DiffId string `protobuf:"bytes,2,opt,name=diff_id,json=diffId,proto3" json:"diff_id,omitempty"`
|
||||
@@ -515,6 +523,69 @@ func (m *Layer) GetDiffId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type CVSS struct {
|
||||
V2Vector string `protobuf:"bytes,1,opt,name=v2_vector,json=v2Vector,proto3" json:"v2_vector,omitempty"`
|
||||
V3Vector string `protobuf:"bytes,2,opt,name=v3_vector,json=v3Vector,proto3" json:"v3_vector,omitempty"`
|
||||
V2Score float64 `protobuf:"fixed64,3,opt,name=v2_score,json=v2Score,proto3" json:"v2_score,omitempty"`
|
||||
V3Score float64 `protobuf:"fixed64,4,opt,name=v3_score,json=v3Score,proto3" json:"v3_score,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CVSS) Reset() { *m = CVSS{} }
|
||||
func (m *CVSS) String() string { return proto.CompactTextString(m) }
|
||||
func (*CVSS) ProtoMessage() {}
|
||||
func (*CVSS) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_6e749acacaaabfff, []int{7}
|
||||
}
|
||||
|
||||
func (m *CVSS) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CVSS.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CVSS) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CVSS.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CVSS) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CVSS.Merge(m, src)
|
||||
}
|
||||
func (m *CVSS) XXX_Size() int {
|
||||
return xxx_messageInfo_CVSS.Size(m)
|
||||
}
|
||||
func (m *CVSS) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CVSS.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CVSS proto.InternalMessageInfo
|
||||
|
||||
func (m *CVSS) GetV2Vector() string {
|
||||
if m != nil {
|
||||
return m.V2Vector
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *CVSS) GetV3Vector() string {
|
||||
if m != nil {
|
||||
return m.V3Vector
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *CVSS) GetV2Score() float64 {
|
||||
if m != nil {
|
||||
return m.V2Score
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *CVSS) GetV3Score() float64 {
|
||||
if m != nil {
|
||||
return m.V3Score
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("trivy.common.Severity", Severity_name, Severity_value)
|
||||
proto.RegisterType((*OS)(nil), "trivy.common.OS")
|
||||
@@ -523,50 +594,59 @@ func init() {
|
||||
proto.RegisterType((*Package)(nil), "trivy.common.Package")
|
||||
proto.RegisterType((*Library)(nil), "trivy.common.Library")
|
||||
proto.RegisterType((*Vulnerability)(nil), "trivy.common.Vulnerability")
|
||||
proto.RegisterMapType((map[string]*CVSS)(nil), "trivy.common.Vulnerability.CvssEntry")
|
||||
proto.RegisterType((*Layer)(nil), "trivy.common.Layer")
|
||||
proto.RegisterType((*CVSS)(nil), "trivy.common.CVSS")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("rpc/common/service.proto", fileDescriptor_6e749acacaaabfff) }
|
||||
|
||||
var fileDescriptor_6e749acacaaabfff = []byte{
|
||||
// 613 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x5d, 0x6f, 0xd3, 0x30,
|
||||
0x14, 0x25, 0x4d, 0xdb, 0xa4, 0x37, 0xfb, 0x08, 0x66, 0x8c, 0xa0, 0x49, 0x50, 0x85, 0x07, 0x3a,
|
||||
0x90, 0x3a, 0xe8, 0x1e, 0xe0, 0x75, 0x6c, 0x13, 0x8b, 0xd8, 0xba, 0x29, 0x63, 0x9b, 0x84, 0x84,
|
||||
0x2a, 0xcf, 0x71, 0x5a, 0x6b, 0x69, 0x12, 0xd9, 0x59, 0x45, 0x7e, 0x01, 0xbf, 0x98, 0x77, 0x64,
|
||||
0xc7, 0xc9, 0xda, 0x89, 0x17, 0xde, 0x7c, 0xcf, 0x39, 0xb9, 0xe7, 0x9e, 0x1b, 0x27, 0xe0, 0xf1,
|
||||
0x9c, 0xec, 0x91, 0x6c, 0x3e, 0xcf, 0xd2, 0x3d, 0x41, 0xf9, 0x82, 0x11, 0x3a, 0xcc, 0x79, 0x56,
|
||||
0x64, 0x68, 0xad, 0xe0, 0x6c, 0x51, 0x0e, 0x2b, 0xce, 0xff, 0x00, 0xad, 0xf3, 0x4b, 0xb4, 0x0d,
|
||||
0xdd, 0x18, 0xcf, 0x59, 0x52, 0x7a, 0x46, 0xdf, 0x18, 0xf4, 0x42, 0x5d, 0x21, 0x04, 0xed, 0x14,
|
||||
0xcf, 0xa9, 0xd7, 0x52, 0xa8, 0x3a, 0xfb, 0x3f, 0xc1, 0xb9, 0xc0, 0xe4, 0x0e, 0x4f, 0x69, 0x90,
|
||||
0xc6, 0x19, 0xda, 0x81, 0x5e, 0xcc, 0x12, 0x3a, 0xc9, 0x71, 0x31, 0xd3, 0x4f, 0xdb, 0x12, 0xb8,
|
||||
0xc0, 0xc5, 0x0c, 0x7d, 0x04, 0x3b, 0xaf, 0xb4, 0xc2, 0x6b, 0xf5, 0xcd, 0x81, 0x33, 0x7a, 0x3e,
|
||||
0x5c, 0xb6, 0x1f, 0xea, 0x4e, 0x61, 0x23, 0xf3, 0x05, 0x38, 0x07, 0x79, 0x9e, 0x30, 0x82, 0x0b,
|
||||
0x96, 0xa5, 0x72, 0x82, 0xa2, 0xcc, 0xa9, 0xee, 0xac, 0xce, 0xab, 0x96, 0xad, 0x47, 0x96, 0xfb,
|
||||
0xd0, 0x4b, 0xd8, 0x2d, 0xc7, 0x9c, 0x51, 0xe1, 0x99, 0xff, 0xf2, 0x3c, 0x55, 0x74, 0x19, 0x3e,
|
||||
0xe8, 0xfc, 0x3f, 0x06, 0x58, 0x7a, 0x94, 0x26, 0xb3, 0xf1, 0x90, 0x19, 0x79, 0x60, 0x2d, 0x28,
|
||||
0x17, 0x2c, 0x4b, 0xb5, 0x5f, 0x5d, 0x4a, 0x86, 0xd3, 0x84, 0x62, 0x41, 0x3d, 0xb3, 0x62, 0x74,
|
||||
0x89, 0xb6, 0xa0, 0x43, 0xf3, 0x8c, 0xcc, 0xbc, 0x76, 0xdf, 0x18, 0x74, 0xc2, 0xaa, 0x90, 0xdd,
|
||||
0x31, 0x27, 0x33, 0xaf, 0x53, 0x75, 0x97, 0x67, 0xf4, 0x12, 0x6c, 0xc1, 0xc9, 0x44, 0xb9, 0x76,
|
||||
0xab, 0x26, 0x82, 0x93, 0xb1, 0x34, 0x7e, 0x0d, 0x8e, 0xa4, 0x6a, 0x73, 0x4b, 0xb1, 0x20, 0x38,
|
||||
0xb9, 0xd6, 0xfe, 0x5a, 0x50, 0xcf, 0x60, 0x37, 0x82, 0x50, 0x8f, 0xb1, 0x03, 0x3d, 0x29, 0xa8,
|
||||
0x46, 0xe9, 0xa9, 0x51, 0xa4, 0xdb, 0xb1, 0xac, 0xfd, 0x4f, 0x60, 0xe9, 0x6d, 0xfc, 0x5f, 0x6c,
|
||||
0xff, 0xb7, 0x09, 0xeb, 0xd7, 0xf7, 0x49, 0x4a, 0x39, 0xbe, 0x65, 0x09, 0x2b, 0x4a, 0xb4, 0x0b,
|
||||
0xee, 0x62, 0x19, 0x98, 0xb0, 0x48, 0xf7, 0xda, 0x5c, 0xc1, 0x83, 0x48, 0xe6, 0xcd, 0xef, 0xa6,
|
||||
0x93, 0xa5, 0x9b, 0x65, 0xe5, 0x77, 0x53, 0x95, 0xf7, 0x3d, 0x3c, 0x65, 0xa9, 0x28, 0x70, 0x92,
|
||||
0xd0, 0xa8, 0x49, 0x5d, 0x2d, 0xd6, 0x6d, 0x88, 0x3a, 0xfb, 0x1b, 0x58, 0x8f, 0xd9, 0xaf, 0x25,
|
||||
0x61, 0x5b, 0x09, 0xd7, 0x14, 0x58, 0x8b, 0xb6, 0xa0, 0x53, 0xb0, 0x22, 0xa1, 0x7a, 0xe3, 0x55,
|
||||
0x81, 0xfa, 0xe0, 0x44, 0x54, 0x10, 0xce, 0x72, 0x79, 0xcb, 0xf4, 0xd6, 0x97, 0x21, 0x34, 0x02,
|
||||
0x5b, 0xd0, 0x05, 0xe5, 0xac, 0x28, 0xd5, 0xda, 0x37, 0x46, 0xdb, 0xab, 0xd7, 0xe8, 0x52, 0xb3,
|
||||
0x61, 0xa3, 0x43, 0xaf, 0x00, 0x38, 0x8d, 0x29, 0xa7, 0x29, 0xa1, 0xc2, 0xb3, 0xfb, 0xa6, 0x7c,
|
||||
0x17, 0x0f, 0x08, 0xda, 0x85, 0x4e, 0x82, 0x4b, 0xca, 0x3d, 0xe8, 0x1b, 0x03, 0x67, 0xf4, 0xec,
|
||||
0xd1, 0xbd, 0x94, 0x54, 0x58, 0x29, 0xd0, 0x5b, 0xd8, 0xac, 0xdb, 0x4e, 0x44, 0x76, 0xcf, 0x09,
|
||||
0xf5, 0x1c, 0x35, 0xe4, 0x46, 0x0d, 0x5f, 0x2a, 0xd4, 0xff, 0x0c, 0x1d, 0xf5, 0xa0, 0xfc, 0x86,
|
||||
0x23, 0x36, 0xa5, 0xa2, 0xa8, 0xbf, 0xe1, 0xaa, 0x42, 0x2f, 0xc0, 0x8a, 0x58, 0x1c, 0xcb, 0xf7,
|
||||
0xd1, 0xaa, 0x89, 0x38, 0x0e, 0xa2, 0x77, 0x47, 0x60, 0xd7, 0x19, 0x90, 0x03, 0xd6, 0xd5, 0xf8,
|
||||
0xdb, 0xf8, 0xfc, 0x66, 0xec, 0x3e, 0x41, 0x16, 0x98, 0xa7, 0xe7, 0x37, 0xae, 0x81, 0x00, 0xba,
|
||||
0x67, 0xc7, 0x47, 0xc1, 0xd5, 0x99, 0xdb, 0x42, 0x36, 0xb4, 0x4f, 0x82, 0xaf, 0x27, 0xae, 0x89,
|
||||
0xd6, 0xc0, 0x3e, 0x0c, 0x83, 0xef, 0xc1, 0xe1, 0xc1, 0xa9, 0xdb, 0xfe, 0x62, 0xff, 0xe8, 0x56,
|
||||
0xf3, 0xdf, 0x76, 0xd5, 0xff, 0x65, 0xff, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xe0, 0xac,
|
||||
0x75, 0x7b, 0x04, 0x00, 0x00,
|
||||
// 733 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x51, 0x6f, 0xdb, 0x36,
|
||||
0x10, 0x9e, 0x6c, 0xd9, 0x92, 0x4f, 0x4e, 0xa2, 0x71, 0x59, 0xa6, 0x2c, 0xc0, 0x66, 0x68, 0x18,
|
||||
0xe6, 0x6c, 0x80, 0xb3, 0xca, 0x0f, 0x4d, 0xfb, 0x96, 0x3a, 0x41, 0x63, 0x24, 0x71, 0x02, 0xb9,
|
||||
0x71, 0x80, 0x02, 0x85, 0xa1, 0xd0, 0x94, 0x4d, 0x58, 0x96, 0x04, 0x52, 0x11, 0xaa, 0x5f, 0xd1,
|
||||
0x5f, 0xdb, 0xf7, 0x82, 0x14, 0xe5, 0xd8, 0x41, 0x5f, 0xfa, 0xc6, 0xbb, 0xef, 0xd3, 0xf7, 0xdd,
|
||||
0x1d, 0x4f, 0x04, 0x87, 0xa5, 0xf8, 0x04, 0x27, 0xab, 0x55, 0x12, 0x9f, 0x70, 0xc2, 0x72, 0x8a,
|
||||
0x49, 0x2f, 0x65, 0x49, 0x96, 0xa0, 0x76, 0xc6, 0x68, 0x5e, 0xf4, 0x4a, 0xcc, 0xfd, 0x1f, 0x6a,
|
||||
0xb7, 0x63, 0x74, 0x00, 0xcd, 0x30, 0x58, 0xd1, 0xa8, 0x70, 0xb4, 0x8e, 0xd6, 0x6d, 0xf9, 0x2a,
|
||||
0x42, 0x08, 0xf4, 0x38, 0x58, 0x11, 0xa7, 0x26, 0xb3, 0xf2, 0xec, 0x7e, 0x02, 0xeb, 0x2e, 0xc0,
|
||||
0xcb, 0x60, 0x4e, 0x86, 0x71, 0x98, 0xa0, 0x23, 0x68, 0x85, 0x34, 0x22, 0xd3, 0x34, 0xc8, 0x16,
|
||||
0xea, 0x6b, 0x53, 0x24, 0xee, 0x82, 0x6c, 0x81, 0x5e, 0x81, 0x99, 0x96, 0x5c, 0xee, 0xd4, 0x3a,
|
||||
0xf5, 0xae, 0xe5, 0xfd, 0xda, 0xdb, 0xb4, 0xef, 0x29, 0x25, 0x7f, 0x4d, 0x73, 0x39, 0x58, 0x67,
|
||||
0x69, 0x1a, 0x51, 0x1c, 0x64, 0x34, 0x89, 0x45, 0x05, 0x59, 0x91, 0x12, 0xa5, 0x2c, 0xcf, 0xdb,
|
||||
0x96, 0xb5, 0x17, 0x96, 0x7d, 0x68, 0x45, 0xf4, 0x91, 0x05, 0x8c, 0x12, 0xee, 0xd4, 0xbf, 0xe7,
|
||||
0x79, 0x2d, 0xe1, 0xc2, 0x7f, 0xe6, 0xb9, 0x5f, 0x35, 0x30, 0x54, 0x29, 0xeb, 0x9e, 0xb5, 0xe7,
|
||||
0x9e, 0x91, 0x03, 0x46, 0x4e, 0x18, 0xa7, 0x49, 0xac, 0xfc, 0xaa, 0x50, 0x20, 0x8c, 0x44, 0x24,
|
||||
0xe0, 0xc4, 0xa9, 0x97, 0x88, 0x0a, 0xd1, 0x3e, 0x34, 0x48, 0x9a, 0xe0, 0x85, 0xa3, 0x77, 0xb4,
|
||||
0x6e, 0xc3, 0x2f, 0x03, 0xa1, 0x1e, 0x30, 0xbc, 0x70, 0x1a, 0xa5, 0xba, 0x38, 0xa3, 0x43, 0x30,
|
||||
0x39, 0xc3, 0x53, 0xe9, 0xda, 0x2c, 0x45, 0x38, 0xc3, 0x23, 0x61, 0xfc, 0x27, 0x58, 0x02, 0xaa,
|
||||
0xcc, 0x0d, 0x89, 0x02, 0x67, 0x78, 0xa2, 0xfc, 0x15, 0xa1, 0xaa, 0xc1, 0x5c, 0x13, 0x7c, 0x55,
|
||||
0xc6, 0x11, 0xb4, 0x04, 0xa1, 0x2c, 0xa5, 0x25, 0x4b, 0x11, 0x6e, 0x17, 0x22, 0x76, 0x5f, 0x83,
|
||||
0xa1, 0xa6, 0xf1, 0x63, 0x6d, 0xbb, 0x5f, 0x74, 0xd8, 0x99, 0x3c, 0x45, 0x31, 0x61, 0xc1, 0x23,
|
||||
0x8d, 0x68, 0x56, 0xa0, 0x63, 0xb0, 0xf3, 0xcd, 0xc4, 0x94, 0xce, 0x94, 0xd6, 0xde, 0x56, 0x7e,
|
||||
0x38, 0x13, 0xfd, 0xa6, 0xcb, 0xf9, 0x74, 0x63, 0xb3, 0x8c, 0x74, 0x39, 0x97, 0xfd, 0xfe, 0x07,
|
||||
0x3f, 0xd3, 0x98, 0x67, 0x41, 0x14, 0x91, 0xd9, 0xba, 0xeb, 0x72, 0xb0, 0xf6, 0x1a, 0xa8, 0x7a,
|
||||
0xff, 0x0b, 0x76, 0x42, 0xfa, 0x79, 0x83, 0xa8, 0x4b, 0x62, 0x5b, 0x26, 0x2b, 0xd2, 0x3e, 0x34,
|
||||
0x32, 0x9a, 0x45, 0x44, 0x4d, 0xbc, 0x0c, 0x50, 0x07, 0xac, 0x19, 0xe1, 0x98, 0xd1, 0x54, 0x6c,
|
||||
0x99, 0x9a, 0xfa, 0x66, 0x0a, 0x79, 0x60, 0x72, 0x92, 0x13, 0x46, 0xb3, 0x42, 0x8e, 0x7d, 0xd7,
|
||||
0x3b, 0xd8, 0x5e, 0xa3, 0xb1, 0x42, 0xfd, 0x35, 0x0f, 0xfd, 0x01, 0xc0, 0x48, 0x48, 0x18, 0x89,
|
||||
0x31, 0xe1, 0x8e, 0xd9, 0xa9, 0x8b, 0xbb, 0x78, 0xce, 0xa0, 0x63, 0x68, 0x44, 0x41, 0x41, 0x98,
|
||||
0x03, 0x1d, 0xad, 0x6b, 0x79, 0xbf, 0xbc, 0xd8, 0x4b, 0x01, 0xf9, 0x25, 0x03, 0xfd, 0x03, 0x7b,
|
||||
0x95, 0xec, 0x94, 0x27, 0x4f, 0x0c, 0x13, 0xc7, 0x92, 0x45, 0xee, 0x56, 0xe9, 0xb1, 0xcc, 0xa2,
|
||||
0x37, 0xa0, 0xe3, 0x9c, 0x73, 0xa7, 0x2d, 0x57, 0xfd, 0xef, 0x6d, 0xc9, 0xad, 0x2b, 0xea, 0x0d,
|
||||
0x72, 0xce, 0x2f, 0xe2, 0x8c, 0x15, 0xbe, 0xfc, 0xe4, 0xf7, 0x2b, 0x68, 0xad, 0x53, 0xc8, 0x86,
|
||||
0xfa, 0x92, 0x54, 0xff, 0xbf, 0x38, 0xa2, 0x2e, 0x34, 0xf2, 0x20, 0x7a, 0x2a, 0xef, 0xc8, 0xf2,
|
||||
0xd0, 0xb6, 0xf4, 0x60, 0x32, 0x1e, 0xfb, 0x25, 0xe1, 0x6d, 0xed, 0x54, 0x73, 0x4f, 0xa1, 0x21,
|
||||
0x1b, 0x10, 0x6f, 0xc9, 0x8c, 0xce, 0x09, 0xcf, 0xaa, 0xb7, 0xa4, 0x8c, 0xd0, 0x6f, 0x60, 0xcc,
|
||||
0x68, 0x18, 0x8a, 0xbd, 0xa8, 0x55, 0x40, 0x18, 0x0e, 0x67, 0x6e, 0x0e, 0xba, 0x10, 0x13, 0x9b,
|
||||
0x9a, 0x7b, 0xd3, 0x9c, 0xe0, 0x2c, 0x61, 0xd5, 0x4b, 0x92, 0x7b, 0x13, 0x19, 0x4b, 0xb0, 0x5f,
|
||||
0x81, 0xea, 0x9f, 0xcf, 0xfb, 0x0a, 0x3c, 0x04, 0x33, 0xf7, 0xa6, 0x1c, 0x27, 0xac, 0xfc, 0x0b,
|
||||
0x35, 0xdf, 0xc8, 0xbd, 0xb1, 0x08, 0x25, 0xd4, 0x57, 0x90, 0xae, 0xa0, 0xbe, 0x84, 0xfe, 0x3d,
|
||||
0x07, 0xb3, 0xba, 0x43, 0x64, 0x81, 0x71, 0x3f, 0xba, 0x1a, 0xdd, 0x3e, 0x8c, 0xec, 0x9f, 0x90,
|
||||
0x01, 0xf5, 0xeb, 0xdb, 0x07, 0x5b, 0x43, 0x00, 0xcd, 0x9b, 0x8b, 0xf3, 0xe1, 0xfd, 0x8d, 0x5d,
|
||||
0x43, 0x26, 0xe8, 0x97, 0xc3, 0xf7, 0x97, 0x76, 0x1d, 0xb5, 0xc1, 0x1c, 0xf8, 0xc3, 0x0f, 0xc3,
|
||||
0xc1, 0xd9, 0xb5, 0xad, 0xbf, 0x33, 0x3f, 0x36, 0xcb, 0x89, 0x3c, 0x36, 0xe5, 0xfb, 0xda, 0xff,
|
||||
0x16, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x77, 0x60, 0x05, 0x7b, 0x05, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -41,16 +41,17 @@ message Library {
|
||||
}
|
||||
|
||||
message Vulnerability {
|
||||
string vulnerability_id = 1;
|
||||
string pkg_name = 2;
|
||||
string installed_version = 3;
|
||||
string fixed_version = 4;
|
||||
string title = 5;
|
||||
string description = 6;
|
||||
Severity severity = 7;
|
||||
repeated string references = 8;
|
||||
Layer layer = 10;
|
||||
string severity_source = 11;
|
||||
string vulnerability_id = 1;
|
||||
string pkg_name = 2;
|
||||
string installed_version = 3;
|
||||
string fixed_version = 4;
|
||||
string title = 5;
|
||||
string description = 6;
|
||||
Severity severity = 7;
|
||||
repeated string references = 8;
|
||||
Layer layer = 10;
|
||||
string severity_source = 11;
|
||||
map<string, CVSS> cvss = 12;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
@@ -65,3 +66,10 @@ enum Severity {
|
||||
HIGH = 3;
|
||||
CRITICAL = 4;
|
||||
}
|
||||
|
||||
message CVSS {
|
||||
string v2_vector = 1;
|
||||
string v3_vector = 2;
|
||||
double v2_score = 3;
|
||||
double v3_score = 4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user