Compare commits

..

28 Commits

Author SHA1 Message Date
rahul2393
20f2bae49b Fix non-root directory permission denied error (#578)
* Fix non-root directory permission denied error

* Updated Readme

* Fixed Readme

* Updated readme to use latest tag for QA error

* updated Dockerfile

* Moved error to others section
2020-07-30 12:57:44 +03:00
Simarpreet Singh
8eb9df8447 .circleci: Add code coverage (#572)
Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-07-29 15:09:27 -07:00
rahul2393
88aaffa957 Added support of list-all-packages (#574)
* Added support of list-all-packages

* updated Readme

* Added library packages and fixed import name

* updated env var name

* Sorting packages in scan
2020-07-29 22:11:38 +03:00
Moch. Lutfi
469c0b41df fix: only show severity total from filter flags (#559)
* Add filter table output by severity flags

* Simplify filter output

* Fix unit test

* add filter unique severities

* remove wrong comment

* Remove utils and update logic

* chore(mod): tidy

Co-authored-by: knqyf263 <knqyf263@gmail.com>
2020-07-29 10:54:51 +03:00
Teppei Fukuda
4a34f72f22 Update README.md (#575) 2020-07-29 10:31:49 +03:00
Simarpreet Singh
4d721e1410 SARIF: Tweak format for GitHub UI (#571)
* sarif: Tweak format for GitHub UI

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

* sarif: Make sarif easier to use with a default template

This will help us use Trivy in places like GitHub Actions where
we cannot specify a template as input.

$ trivy image --format=sarif alpine:3.10.1

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

* Revert "sarif: Make sarif easier to use with a default template"

This reverts commit 5b5d1c8f7d.

* .dockerignore: Add un-needed large directories

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

* Dockerfile: Add sarif template.

This will let users run and save the output through the docker image

Example:
```
docker run --rm -it -v $(pwd):/tmp aquasec/trivy:latest image -f template --template "@contrib/sarif.tpl" --output="/tmp/sarif.test" alpine:3.10.2
```

Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-07-28 11:22:03 -07:00
rahul2393
9c91da8a2b Add non root user (#570) 2020-07-28 12:37:43 +03:00
Simarpreet Singh
5b9d942313 rpc: Add CVSS information to client/server (#564)
Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-07-26 15:06:25 +03:00
rahul2393
d6b37cb87e Fix --timeout flag (#569)
* Wrapped scan call inside context timeout

* timeout has default value
2020-07-22 21:33:30 +03:00
Teppei Fukuda
9c6f077818 feat(report): support OPA to filter vulnerabilities (#562)
* feat(cli): add --filter option

* feat(opa): support OPA

* test(opa): add a test case with OPA

* test: update a mock

* chore(mod): update dependencies

* chore(filter): add example Rego files

* chore(README): update

* chore(rego): apply opa fmt

* refactor: replace filter with policy

* chore(policy): update rego files

* fix(vulnerability): evaluate each vulnerability

* chore(README): update

* Update README.md

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

* Update README.md

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

* chore(README): update a TOC link

* fix: replace allow with ignore

* chore(README): update

Co-authored-by: Itay Shakury <itay@itaysk.com>
2020-07-22 21:10:44 +03:00
rahul2393
0b5d936dbe Fixed case when pre-release is in suffix (#565)
* Fixed case when pre-release is in suffix

* moved regex to global scope

* removed not needed code

* Update error to debug

* skip constraint when empty
2020-07-22 08:57:19 +03:00
rahul2393
6eebed33b2 improve ruby comparison version check. (#552)
* Implemented ruby comparison version check.

* Added semver package to validate and check version

* Added more tests

* Replaced go-version with semver

* Removing go-version from dependency

* Added check for ruby gem version format

* Updated semver model and patch rewrite process

* Refactoring
2020-07-19 18:03:56 +03:00
rahul2393
43085a80bc Added sarif template (#558)
* Added sarif template

* Updated readme

* fixed tests

* Added integration tests and fixed all sarif validations issues

* Added tests for endWithPeriod

* Fixed tests, and added sarif golden file

* removed optional newline sequence
2020-07-17 11:08:50 -07:00
Teppei Fukuda
4f90b114ea feat(vulnerability): add CWE-ID (#561)
* chore(mod): update dependency

* test(vulnerability): add CweIDs
2020-07-16 11:07:27 +03:00
Michal Slusarczyk
d9fa353a06 Fixing Error retrieving template from path when --format is not template but template is provided (#556) 2020-07-13 14:01:08 +03:00
Michal Slusarczyk
9a1d7460f6 Adding contrib/junit.tpl to docker image (#554) 2020-07-09 09:23:31 +03:00
Simarpreet Singh
d18d17b861 db: Update trivy-db to include CVSS score info (#530)
* mod: Update trivy-db to include CVSS score info

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

* mod: Update go.mod

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

* mod: Update trivy-db to latest

Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-07-07 08:16:42 -07:00
Liz Rice
4b57c0d4e6 docs: fix markdown (#553)
Correct markdown for MicroScanner link
2020-07-07 16:27:51 +03:00
rahul2393
ccd9b2d2c5 Added function to escape string in failure message title and descriptions (#551)
* Added function to escape string in failure message title and descriptions

* updated template to use xml.EscapeText

* Renamed template function
2020-07-06 12:43:11 +03:00
rahul2393
ec770cd819 Added JUNIT support (#541)
* added template for junit

* updated readme and junit format

* Added severity in testcase name instead of separate failure block
2020-06-25 17:23:04 +03:00
Teppei Fukuda
b7ec633fb2 chore(docs): mention air-gapped environment (#544)
* chore(docs): mention air-gapped environment

* Update docs/air-gap.md

Co-authored-by: Liz Rice <liz@lizrice.com>
2020-06-24 17:15:17 +03:00
Teppei Fukuda
7aabff1236 chore(README): add programming languages (#543) 2020-06-23 20:52:43 +03:00
Teppei Fukuda
9dc1bdffb1 fix(log): write error messages to stderr (#538) 2020-06-23 15:06:42 +03:00
Simarpreet Singh
2ac672a663 Use StoreMetadata from trivy-db (#509)
* db_test: Remove cruft

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

* db: Add StoreMetadata from trivy-db.

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

* mod: Update trivy-db dependency

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

* mod: Bump trivy-db version

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

* db: Eliminate metadata.Store

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

* db: Add a TODO to move things into trivy-db repo

Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-06-22 14:29:38 -07:00
Liz Rice
11ae6b29d5 docs: add more CI options to README (#535)
Add GitHub Actions and AWS CodePipeline to CI section of Readme
Correct a broken link to "Data sources"
2020-06-21 11:26:22 +03:00
Teppei Fukuda
f201f59e27 chore(Dockerfile): bump up alpine to 3.12 (#528) 2020-06-15 11:29:38 +03:00
Teppei Fukuda
25d45e1ac5 fix(alpine): replace go-deb-version with go-apk-version (#520)
* fix(alpine): add a failing test with go-deb-version

* fix(alpine): replace go-deb-version with go-apk-version

* chore(mod): update dependencies

* chore(mod): update go-apk-version
2020-06-11 12:55:34 +03:00
Oran Moshai
298ba99b8f fix: MissingBlobs is implemented different in FS and S3 the method log… (#522)
* fix: MissingBlobs is implemented diffrent in FS and S3 the method logic moved to cache.MissingBlobs

* fix(unittest): implement MockArtifactCache instead MockLocalArtifactCache

* fix(gofmt)

* fix naming convention

Co-authored-by: oranmoshai <oran.moshai@aquasec.com>
2020-06-10 10:38:37 +03:00
61 changed files with 2070 additions and 375 deletions

8
.circleci/codecov.yml Normal file
View File

@@ -0,0 +1,8 @@
coverage:
status:
project:
default:
informational: true
patch:
default:
informational: true

View File

@@ -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:

View File

@@ -1,2 +1,6 @@
.git
.github
.cache
.circleci
integration
imgs

1
.gitignore vendored
View File

@@ -18,4 +18,5 @@
thumbs.db
# test fixtures
coverage.txt
integration/testdata/fixtures/

View File

@@ -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"]

View File

@@ -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
View File

@@ -6,6 +6,7 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/aquasecurity/trivy)](https://goreportcard.com/report/github.com/aquasecurity/trivy)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/aquasecurity/trivy/blob/master/LICENSE)
[![Docker image](https://images.microbadger.com/badges/version/aquasec/trivy.svg)](https://microbadger.com/images/aquasec/trivy "Get your own version badge on microbadger.com")
[![codecov](https://codecov.io/gh/aquasecurity/trivy/branch/master/graph/badge.svg)](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 Aquas [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 Aquas [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

View 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
}

View 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
View 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
View 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
View 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
View File

@@ -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
View File

@@ -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=

View File

@@ -94,3 +94,5 @@ dockers:
- "--label=org.label-schema.vcs-ref={{ .FullCommit }}"
extra_files:
- contrib/gitlab.tpl
- contrib/junit.tpl
- contrib/sarif.tpl

View File

@@ -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{

View File

@@ -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
}

View 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"
}
]
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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"),
}
}

View File

@@ -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"),

View File

@@ -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 {

View File

@@ -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)
}
})
}
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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"

View File

@@ -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{

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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 &#34;DARN&#34; 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 &lt; 7.20.0 and curl &gt;= 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())
})
}

View File

@@ -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{

View File

@@ -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{

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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)
})
}
}

View File

@@ -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

View File

@@ -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{

View File

@@ -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 != "" {

View 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)
})
}
}

View File

@@ -3,4 +3,5 @@ package types
type ScanOptions struct {
VulnType []string
ScanRemovedPackages bool
ListAllPackages bool
}

View File

@@ -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) {

View File

@@ -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
}

View 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
View File

@@ -0,0 +1,5 @@
package trivy
ignore {
input.VulnerabilityID != "CVE-2019-0001"
}

View File

@@ -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 {

View File

@@ -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)
})
}

View File

@@ -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,
}

View File

@@ -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;
}