Compare commits

..

17 Commits

Author SHA1 Message Date
Teppei Fukuda
af584a8517 Revert "change mod genuinetools/reg to vanilla (#297)" (#321)
This reverts commit 1e9dcdb7d1.
2019-12-16 10:18:24 +02:00
jabielecki
1805a956a3 refactor for newest fanal (#305)
Changes for compatilibity with PR aquasecurity/fanal#47

Signed-off-by: Jakub Bielecki <jakub.bielecki@codilime.com>
2019-12-15 10:22:48 +02:00
Teppei Fukuda
74717b888e feat: support client/server mode (#295)
* chore(app): change dir

* feat(rpc): add a proto file and auto-generated files

* chore(dep): add dependencies

* fix(app): fix import path

* fix(integration): fix import path

* fix(protoc): use enum for severity

* chore(Makefile): add fmt andd protoc

* chore(clang): add .clang-format

* refactor: split functions for client/server (#296)

* refactor(db): split db.Download

* refactor(standalone): create a different package

* refactor(vulnerability): split FillAndFilter

* fix(protoc): use enum for severity

* chore(Makefile): add fmt andd protoc

* chore(clang): add .clang-format

* fix(db): remove an unused variable

* fix(db): expose the github client as an argument of constructor

* refactor(vulnerability): add the detail message

* feat(rpc): add rpc client (#302)

* fix(protoc): use enum for severity

* chore(Makefile): add fmt andd protoc

* chore(clang): add .clang-format

* feat(rpc): convert types

* feat(rpc): add rpc client

* token: Refactor to handle bad headers being set

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

* feat(rpc): add rpc server (#303)

* feat(rpc): add rpc server

* feat(utils): add CopyFile

* feat(server/config): add config struct

* feat(detector): add detector

* feat(scanner): delegate procedures to detector

* fix(scanner): fix the interface

* test(mock): add mocks

* test(rpc/server): add tests

* test(rpc/ospkg/server): add tests

* tets(os/detector): add tests

* refactor(library): move directories

* chore(dependency): add google/wire

* refactor(library): introduce google/wire

* refactor(ospkg/detector): move directory

* feat(rpc): add eosl

* refactor(ospkg): introduce google/wire

* refactor(wire): bind an interface

* refactor(client): use wire.Struct

* chore(Makefile): fix wire

* test(server): add AssertExpectations

* test(server): add AssertExpectations

* refactor(server): remove debug log

* refactor(error): add more context messages

* test(server): fix error message

* refactor(test): create a constructor of mock

* refactor(config): remove an unused variable

* test(config): add an assertion to test the config struct

* feat(client/server): add sub commands (#304)

* feat(rpc): add rpc server

* feat(utils): add CopyFile

* feat(server/config): add config struct

* feat(detector): add detector

* feat(scanner): delegate procedures to detector

* fix(scanner): fix the interface

* feat(client/server): add sub commands

* merge(server3)

* test(scan): remove an unused mock

* refactor(client): generate the constructor by wire

* fix(cli): change the default port

* fix(server): use auto-generated constructor

* feat(ospkg): return eosl

* test(integration): add integration tests for client/server (#306)

* fix(server): remove unnecessary options

* test(integration): add integration tests for client/server

* fix(server): wrap an error

* fix(server): change the update interval

* fix(server): display the error detail

* test(config): add an assertion to test the config struct

* fix(client): returns an error when failing to initizlie a logger

* test(ospkg/server): add eosl

* Squashed commit of the following:

* test(server): refactor and add tests (#307)

* test(github): create a mock

* test(db): create a mock

* test(server): add tests for DB hot update

* chore(db): add a log message

* refactor(db): introduce google/wire

* refactor(rpc): move directory

* refactor(injector): fix import name

* refactor(import): remove new lines

* fix(server): display the error detail

* fix(server): change the update interval

* fix(server): wrap an error

* test(integration): add integration tests for client/server

* fix(server): remove unnecessary options

* refactor(server): return an error when failing to initialize a logger

* refactor(server): remove unused error

* fix(client/server): fix default port

* chore(README): add client/server

* chore(README): update
2019-12-13 15:00:11 +02:00
Masahiro Fujimura
24fc88ced2 Fix conduct strategy (#308) 2019-12-09 10:49:35 +02:00
jabielecki
1e9dcdb7d1 change mod genuinetools/reg to vanilla (#297)
Instead of using tomoyamachi's fork we can now use the vanilla upstream
package genuinetools/reg. This package gets better maintenance and is
the same as aquasecurity/fanal uses.

Signed-off-by: Jakub Bielecki <jakub.bielecki@codilime.com>
2019-11-28 09:20:57 +02:00
DDd
7233b5f419 Update Gitlab example for Trivy 0.2.0 (#270)
* Update Gitlab example for Trivy 0.2.0

* updating PR with commented

Also using fixed version of Docker since stable can create issues.
2019-11-26 10:23:58 +02:00
Teppei Fukuda
3a53a88139 refactor(app): use internal and separate configurations (#291)
* refactor(cmd): move app to internal

* refactor(config): inject logger

* test(config): add tests
2019-11-21 17:49:43 -08:00
Teppei Fukuda
6cbbb22ab4 fix(alpine): handle rc version (#289)
* fix(alpine): handle rc version

* chore(mod): update dependencies
2019-11-21 11:20:24 +02:00
Teppei Fukuda
b6a8af5b20 chore(windows): remove (#278) 2019-11-20 22:00:04 -08:00
Masahiro Fujimura
30c1a00225 Update readme (#287)
* Add oracle support for README

* Fix amazon linux target packages
2019-11-19 20:53:05 +02:00
Masahiro Fujimura
b345342369 Add oracle linux support (#286)
* Add oracle

* Add oracle

* Add golden json

* Add integration test

* Update go.{mod,sum}

* go mod tidy

* Use k8s/utils clock.Clock interface

* Fix Detect vulnerability oracle
2019-11-19 17:18:25 +02:00
Teppei Fukuda
438680f3e4 fix(reset): reset before initializing DB (#275) 2019-11-17 08:55:13 +02:00
Teppei Fukuda
740c2c4069 chore(log): add debug messages (#284) 2019-11-15 16:30:28 -08:00
Teppei Fukuda
35429e3c46 chore(logo): replace with new logo (#269)
* chore(logo): replace with new logo

* chore(readme): change size
2019-11-15 18:25:32 +02:00
Teppei Fukuda
fb265417d0 chore(clear-cache): add an explanation (#276) 2019-11-15 18:08:58 +02:00
Teppei Fukuda
15af65b9c5 feat(github): add GITHUB_TOKEN for rate limiting (#281) 2019-11-15 16:45:07 +02:00
Teppei Fukuda
c2fdfab3ed fix(lockfile): files under vendor dir (#279) 2019-11-15 16:03:15 +02:00
97 changed files with 8885 additions and 979 deletions

5
.clang-format Normal file
View File

@@ -0,0 +1,5 @@
---
Language: Proto
BasedOnStyle: Google
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true

View File

@@ -3,9 +3,17 @@ LDFLAGS=-ldflags "-s -w -X=main.version=$(VERSION)"
GOPATH=$(shell go env GOPATH)
GOBIN=$(GOPATH)/bin
GOSRC=$(GOPATH)/src
u := $(if $(update),-u)
$(GOBIN)/wire:
GO111MODULE=off go get github.com/google/wire/cmd/wire
.PHONY: wire
wire: $(GOBIN)/wire
wire gen ./...
.PHONY: deps
deps:
go get ${u} -d
@@ -29,10 +37,18 @@ test-integration: integration/testdata/fixtures/*.tar.gz
lint: $(GOBIN)/golangci-lint
$(GOBIN)/golangci-lint run
.PHONY: fmt
fmt:
find ./ -name "*.proto" | xargs clang-format -i
.PHONY: build
build:
go build $(LDFLAGS) ./cmd/trivy
.PHONY: protoc
protoc:
protoc --proto_path=$(GOSRC):. --twirp_out=. --go_out=. ./rpc/detector/service.proto
.PHONY: install
install:
go install $(LDFLAGS) ./cmd/trivy

190
README.md
View File

@@ -1,4 +1,4 @@
<img src="imgs/logo.png" width="300">
<img src="imgs/logo.png" width="150">
[![GitHub release](https://img.shields.io/github/release/aquasecurity/trivy.svg)](https://github.com/aquasecurity/trivy/releases/latest)
@@ -22,23 +22,28 @@ A Simple and Comprehensive Vulnerability Scanner for Containers, Suitable for CI
- [Debian/Ubuntu](#debianubuntu)
- [Arch Linux](#arch-linux)
- [Mac OS X / Homebrew](#homebrew)
- [Binary (Including Windows)](#binary-including-windows)
- [Binary](#binary)
- [From source](#from-source)
- [Quick Start](#quick-start)
- [Basic](#basic)
- [Docker](#docker)
- [Examples](#examples)
- [Scan an image](#scan-an-image)
- [Scan an image file](#scan-an-image-file)
- [Save the results as JSON](#save-the-results-as-json)
- [Filter the vulnerabilities by severities](#filter-the-vulnerabilities-by-severities)
- [Filter the vulnerabilities by type](#filter-the-vulnerabilities-by-type)
- [Skip an update of vulnerability DB](#skip-update-of-vulnerability-db)
- [Ignore unfixed vulnerabilities](#ignore-unfixed-vulnerabilities)
- [Specify exit code](#specify-exit-code)
- [Ignore the specified vulnerabilities](#ignore-the-specified-vulnerabilities)
- [Clear image caches](#clear-image-caches)
- [Reset](#reset)
- [Standalone](#standalone)
- [Scan an image](#scan-an-image)
- [Scan an image file](#scan-an-image-file)
- [Save the results as JSON](#save-the-results-as-json)
- [Filter the vulnerabilities by severities](#filter-the-vulnerabilities-by-severities)
- [Filter the vulnerabilities by type](#filter-the-vulnerabilities-by-type)
- [Skip an update of vulnerability DB](#skip-update-of-vulnerability-db)
- [Ignore unfixed vulnerabilities](#ignore-unfixed-vulnerabilities)
- [Specify exit code](#specify-exit-code)
- [Ignore the specified vulnerabilities](#ignore-the-specified-vulnerabilities)
- [Clear image caches](#clear-image-caches)
- [Reset](#reset)
- [Lightweight DB](#use-lightweight-db)
- [Client/Server](#client--server)
- [Server](#server)
- [Client](#client)
- [Continuous Integration (CI)](#continuous-integration-ci)
- [Travis CI](#travis-ci)
- [CircleCI](#circleci)
@@ -71,7 +76,7 @@ See [here](#continuous-integration-ci) for details.
# Features
- Detect comprehensive vulnerabilities
- OS packages (Alpine, **Red Hat Universal Base Image**, Red Hat Enterprise Linux, CentOS, Debian, Ubuntu, Amazon Linux and Distroless)
- OS packages (Alpine, **Red Hat Universal Base Image**, Red Hat Enterprise Linux, CentOS, Oracle Linux, Debian, Ubuntu, Amazon Linux and Distroless)
- **Application dependencies** (Bundler, Composer, Pipenv, Poetry, npm, yarn and Cargo)
- Simple
- Specify only an image name
@@ -152,7 +157,7 @@ You can use homebrew on macOS.
$ brew install aquasecurity/trivy/trivy
```
## Binary (Including Windows)
## Binary
Get the latest version from [this page](https://github.com/aquasecurity/trivy/releases/latest), and download the archive file for your operating system/architecture. Unpack the archive, and put the binary somewhere in your `$PATH` (on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on.
@@ -254,6 +259,8 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
# Examples
## Standalone
### Scan an image
Simply specify an image name (and a tag).
@@ -1007,30 +1014,18 @@ $ trivy --cache-dir /tmp/trivy/ python:3.4-alpine3.9
The `--clear-cache` option removes image caches. This option is useful if the image which has the same tag is updated (such as when using `latest` tag).
**The scan is not performed.**
```
$ trivy --clear-cache python:3.7
$ trivy --clear-cache
```
<details>
<summary>Result</summary>
```
2019-05-16T12:55:24.749+0900 INFO Removing image caches...
2019-05-16T12:55:24.769+0900 INFO Updating vulnerability database...
2019-05-16T12:56:14.055+0900 INFO Detecting Debian vulnerabilities...
python:3.7 (debian 9.9)
=======================
Total: 3076 (UNKNOWN: 0, LOW: 127, MEDIUM: 2358, HIGH: 578, CRITICAL: 13)
+------------------------------+---------------------+----------+--------------------------+------------------+-------------------------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+------------------------------+---------------------+----------+--------------------------+------------------+-------------------------------------------------------+
| apt | CVE-2011-3374 | LOW | 1.4.9 | | |
+------------------------------+---------------------+ +--------------------------+------------------+-------------------------------------------------------+
| bash | TEMP-0841856-B18BAF | | 4.4-5 | | |
+------------------------------+---------------------+----------+--------------------------+------------------+-------------------------------------------------------+
...
2019-11-15T15:13:26.209+0200 INFO Reopening vulnerability DB
2019-11-15T15:13:26.209+0200 INFO Removing image caches...
```
</details>
@@ -1090,6 +1085,46 @@ Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 0, CRITICAL: 0)
```
</details>
## Client / Server
Trivy has client/server mode. Trivy server has vulnerability database and Trivy client doesn't have to download vulnerability database. It is useful if you want to scan images at multiple locations and do not want to download the database at every location.
### Server
At first, you need to launch Trivy server. It downloads vulnerability database automatically and continue to fetch the latest DB in the background.
```
$ trivy server --listen localhost:8080
2019-12-12T15:17:06.551+0200 INFO Need to update DB
2019-12-12T15:17:56.706+0200 INFO Reopening DB...
2019-12-12T15:17:56.707+0200 INFO Listening localhost:8080...
```
### Client
Then, specify the remote address.
```
$ trivy client --remote http://localhost:8080 alpine:3.10
```
<details>
<summary>Result</summary>
```
alpine:3.10 (alpine 3.10.2)
===========================
Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 0, CRITICAL: 0)
+---------+------------------+----------+-------------------+---------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
+---------+------------------+----------+-------------------+---------------+
| openssl | CVE-2019-1549 | MEDIUM | 1.1.1c-r0 | 1.1.1d-r0 |
+ +------------------+ + + +
| | CVE-2019-1563 | | | |
+ +------------------+----------+ + +
| | CVE-2019-1547 | LOW | | |
+---------+------------------+----------+-------------------+---------------+
```
</details>
### Deprecated options
`--only-update`, `--refresh` and `--auto-refresh` are deprecated since they are unnecessary now. These options will be removed at the next version
@@ -1175,23 +1210,30 @@ stages:
trivy:
stage: test
image: docker:stable-git
image: docker:19.03.1
services:
- name: docker:dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
command: ["dockerd-entrypoint.sh"]
variables:
DOCKER_HOST: tcp://docker:2375/
DOCKER_DRIVER: overlay2
# See https://github.com/docker-library/docker/pull/166
DOCKER_TLS_CERTDIR: ""
before_script:
- docker build -t trivy-ci-test:${CI_COMMIT_REF_NAME} .
- apk add --no-cache curl
- export VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
- echo $VERSION
- wget https://github.com/aquasecurity/trivy/releases/download/v${VERSION}/trivy_${VERSION}_Linux-64bit.tar.gz
- tar zxvf trivy_${VERSION}_Linux-64bit.tar.gz
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- ./trivy --exit-code 0 --severity HIGH --no-progress trivy-ci-test:${CI_COMMIT_REF_NAME}
- ./trivy --exit-code 1 --severity CRITICAL --no-progress trivy-ci-test:${CI_COMMIT_REF_NAME}
- docker build -t trivy-ci-test:$CI_COMMIT_SHA .
- ./trivy --exit-code 0 --cache-dir $CI_PROJECT_DIR/.trivycache/ --no-progress --severity HIGH trivy-ci-test:$CI_COMMIT_SHA
- ./trivy --exit-code 1 --severity CRITICAL --no-progress trivy-ci-test:$CI_COMMIT_SHA
cache:
directories:
- $HOME/.cache/trivy
paths:
- $CI_PROJECT_DIR/.trivycache/
```
## Authorization for Private Docker Registry
@@ -1252,12 +1294,13 @@ The unfixed/unfixable vulnerabilities mean that the patch has not yet been provi
| Red Hat Universal Base Image | 7, 8 | Installed by yum/rpm | YES |
| Red Hat Enterprise Linux | 6, 7, 8 | Installed by yum/rpm | YES |
| CentOS | 6, 7 | Installed by yum/rpm | YES |
| Amazon Linux | 1, 2 | Installed by apt/apt-get/dpkg | NO |
| Oracle Linux | 5, 6, 7, 8 | Installed by yum/rpm | NO |
| Amazon Linux | 1, 2 | Installed by yum/rpm | NO |
| Debian GNU/Linux | wheezy, jessie, stretch, buster | Installed by apt/apt-get/dpkg | YES |
| Ubuntu | 12.04, 14.04, 16.04, 18.04, 18.10, 19.04 | Installed by apt/apt-get/dpkg | YES |
| Distroless | Any | Installed by apt/apt-get/dpkg | YES |
RHEL, CentOS and Amazon Linux package information is stored in a binary format, and Trivy uses the `rpm` executable to parse this information when scanning an image based on RHEL or CentOS. The Trivy container image includes `rpm`, and the installers include it as a dependency. If you installed the `trivy` binary using `wget` or `curl`, or if you build it from source, you will also need to ensure that `rpm` is available.
RHEL, CentOS, Oracle Linux and Amazon Linux package information is stored in a binary format, and Trivy uses the `rpm` executable to parse this information when scanning an image based on RHEL or CentOS. The Trivy container image includes `rpm`, and the installers include it as a dependency. If you installed the `trivy` binary using `wget` or `curl`, or if you build it from source, you will also need to ensure that `rpm` is available.
Distroless: https://github.com/GoogleContainerTools/distroless
@@ -1301,6 +1344,7 @@ Trivy scans a tar image with the following format.
- https://github.com/RustSec/advisory-db
# Usage
## Standalone
```
NAME:
@@ -1337,6 +1381,53 @@ OPTIONS:
```
## Sub commands
Trivy has two sub commands, client and server.
```
NAME:
trivy client - client mode
USAGE:
trivy client [command options] [arguments...]
OPTIONS:
--template value, -t value output template [$TRIVY_TEMPLATE]
--format value, -f value format (table, json, template) (default: "table") [$TRIVY_FORMAT]
--input value, -i value input file path instead of image name [$TRIVY_INPUT]
--severity value, -s value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
--output value, -o value output file name [$TRIVY_OUTPUT]
--exit-code value Exit code when vulnerabilities were found (default: 0) [$TRIVY_EXIT_CODE]
--clear-cache, -c clear image caches without scanning [$TRIVY_CLEAR_CACHE]
--quiet, -q suppress progress bar and log output [$TRIVY_QUIET]
--ignore-unfixed display only fixed vulnerabilities [$TRIVY_IGNORE_UNFIXED]
--debug, -d debug mode [$TRIVY_DEBUG]
--vuln-type value comma-separated list of vulnerability types (os,library) (default: "os,library") [$TRIVY_VULN_TYPE]
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
--cache-dir value use as cache directory, but image cache is stored in /path/to/cache/fanal (default: "/Users/teppei/Library/Caches/trivy") [$TRIVY_CACHE_DIR]
--timeout value docker timeout (default: 1m0s) [$TRIVY_TIMEOUT]
--token value for authentication [$TRIVY_TOKEN]
--remote value server address (default: "http://localhost:4954") [$TRIVY_REMOTE]
```
```
NAME:
trivy server - server mode
USAGE:
trivy server [command options] [arguments...]
OPTIONS:
--skip-update skip db update [$TRIVY_SKIP_UPDATE]
--download-db-only download/update vulnerability database but don't run a scan [$TRIVY_DOWNLOAD_DB_ONLY]
--reset remove all caches and database [$TRIVY_RESET]
--quiet, -q suppress progress bar and log output [$TRIVY_QUIET]
--debug, -d debug mode [$TRIVY_DEBUG]
--cache-dir value use as cache directory, but image cache is stored in /path/to/cache/fanal (default: "/Users/teppei/Library/Caches/trivy") [$TRIVY_CACHE_DIR]
--token value for authentication [$TRIVY_TOKEN]
--listen value listen address (default: "localhost:4954") [$TRIVY_LISTEN]
```
# Comparison with other scanners
## Overview
@@ -1441,7 +1532,7 @@ $ brew untap knqyf263/trivy
$ brew install aquasecurity/trivy/trivy
```
## Binary (Including Windows)
## Binary
No need to fix.
@@ -1485,6 +1576,15 @@ $ brew install aquasecurity/trivy/trivy
## Others
### GitHub Rate limiting
Specify GITHUB_TOKEN for authentication
https://developer.github.com/v3/#rate-limiting
```
$ GITHUB_TOKEN=XXXXXXXXXX trivy alpine:3.10
```
### Unknown error
Try again with `--reset` option:

View File

@@ -4,7 +4,8 @@ import (
l "log"
"os"
"github.com/aquasecurity/trivy/pkg"
"github.com/aquasecurity/trivy/internal"
"github.com/aquasecurity/trivy/pkg/log"
)
@@ -13,7 +14,7 @@ var (
)
func main() {
app := pkg.NewApp(version)
app := internal.NewApp(version)
err := app.Run(os.Args)
if err != nil {
if log.Logger != nil {

15
go.mod
View File

@@ -3,25 +3,32 @@ module github.com/aquasecurity/trivy
go 1.13
require (
github.com/aquasecurity/fanal v0.0.0-20191015084852-e80236018d26
github.com/aquasecurity/fanal v0.0.0-20191205044128-99e4876e56b0
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy-db v0.0.0-20191101193735-bb56553762c0
github.com/aquasecurity/trivy-db v0.0.0-20191120190201-a6645984b409
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91
github.com/caarlos0/env/v6 v6.0.0
github.com/genuinetools/reg v0.16.0
github.com/golang/protobuf v1.3.1
github.com/google/go-github/v28 v28.1.1
github.com/google/wire v0.3.0
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/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
github.com/stretchr/testify v1.4.0
github.com/twitchtv/twirp v5.9.0+incompatible
github.com/urfave/cli v1.20.0
go.uber.org/zap v1.9.1
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5
go.etcd.io/bbolt v1.3.3 // indirect
go.uber.org/atomic v1.5.1 // indirect
go.uber.org/multierr v1.4.0 // indirect
go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421
golang.org/x/sys v0.0.0-20191020152052-9984515f0562 // indirect
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328 // indirect
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/yaml.v2 v2.2.4 // indirect

51
go.sum
View File

@@ -17,19 +17,21 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6/go.mod h1:enEz4FFetw4XAbkffaYgyCVq1556R9Ry+noqT4rq9BE=
github.com/aquasecurity/fanal v0.0.0-20191015084852-e80236018d26 h1:HvyiDHbYDm094Oo59MWIWtZ3Lt2Uu6nQ06IsG2jvIrg=
github.com/aquasecurity/fanal v0.0.0-20191015084852-e80236018d26/go.mod h1:dD1Ny21eY5FSDyERfUIMwdgYhg6Lnw611VOwDHmTSoQ=
github.com/aquasecurity/fanal v0.0.0-20191205044128-99e4876e56b0 h1:wjQXLf/TGvwd/YVNciqTwOQV3aywS9oC/oCo6N92E8Y=
github.com/aquasecurity/fanal v0.0.0-20191205044128-99e4876e56b0/go.mod h1:fV5n9NH3ICFxMrVEoCOg+yKmwjww6SnkdlZb9cT/sVo=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ulc/gvfWm4ylhVaR7MxOwujRjA6et7KhmUbSgUFf4=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ=
github.com/aquasecurity/trivy v0.1.6/go.mod h1:5hobyhxLzDtxruHzPxpND2PUKOssvGUdE9BocpJUwo4=
github.com/aquasecurity/trivy-db v0.0.0-20191101193735-bb56553762c0 h1:G6DzbsaARDzEuT3SdUdXw6GBH3RHhhkoaX1YQtwqYyI=
github.com/aquasecurity/trivy-db v0.0.0-20191101193735-bb56553762c0/go.mod h1:PCxSRIDg26j0v3NgjjFbA3BqrGVLSEu1Fb/n/0RzXzg=
github.com/aquasecurity/trivy-db v0.0.0-20191120190201-a6645984b409 h1:RrFBrw3qZXH/AIEtEYF1WgYhXae3VPPaaPIJic5p1gw=
github.com/aquasecurity/trivy-db v0.0.0-20191120190201-a6645984b409/go.mod h1:vYzX1UhX0o29E+/nuFJTgUJBM5UA7I/NftnbBcYyYRE=
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 h1:ukTLOeMC0aVxbJWVg6hOsVJ0VPIo8w++PbNsze/pqF8=
@@ -125,6 +127,10 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/wire v0.3.0 h1:imGQZGEVEHpje5056+K+cgdO72p0LQv2xIIFXNGUf60=
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
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=
@@ -177,8 +183,12 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed h1:fCWISZq4YN4ulCJx7x0KB15rqxLEe3mtNJL8cSOGKZU=
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed/go.mod h1:SDJ4hurDYyQ9/7nc+eCYtXqdufgK4Cq9TJlwPklqEYA=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
@@ -239,6 +249,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/httpfs v0.0.0-20181222201310-74dc9339e414/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
@@ -265,6 +276,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00 h1:0e4vRd9YqnQBIAIAE39jLKDWffRfJWxloyWwcaMAQho=
github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00/go.mod h1:RQE7h2jyIxekQZ24/wad0c9RGP+KSq4XzHh7h83ALi8=
github.com/twitchtv/twirp v5.9.0+incompatible h1:KBCo4NYCpE9alO1HAEcgninDnw/0AhPT1rZnHkkSqi8=
github.com/twitchtv/twirp v5.9.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
@@ -272,23 +285,41 @@ 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=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -297,8 +328,10 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -342,7 +375,14 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190503185657-3b6f9c0030f7/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328 h1:t3X42h9e6xdbrCD/gPyWqAXr2BEpdJqRd1brThaaxII=
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373 h1:PPwnA7z1Pjf7XYaBP9GL1VAMZmcIWyFz7QCMSIIa3Bg=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
@@ -370,6 +410,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
@@ -393,6 +434,8 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA=
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=

View File

@@ -12,7 +12,6 @@ builds:
goos:
- darwin
- linux
- windows
- freebsd
- openbsd
goarch:
@@ -43,7 +42,6 @@ nfpms:
arm64: ARM64
darwin: macOS
linux: Linux
windows: Windows
openbsd: OpenBSD
netbsd: NetBSD
freebsd: FreeBSD
@@ -52,9 +50,6 @@ nfpms:
archives:
-
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
replacements:
amd64: 64bit
@@ -63,7 +58,6 @@ archives:
arm64: ARM64
darwin: macOS
linux: Linux
windows: Windows
openbsd: OpenBSD
netbsd: NetBSD
freebsd: FreeBSD

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,381 @@
// +build integration
package integration
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/aquasecurity/trivy/internal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestClientServer(t *testing.T) {
type args struct {
Version string
IgnoreUnfixed bool
Severity []string
IgnoreIDs []string
Input string
ClientToken string
ServerToken string
}
cases := []struct {
name string
testArgs args
golden string
wantErr string
}{
{
name: "alpine 3.10 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/alpine-310.tar.gz",
},
golden: "testdata/alpine-310.json.golden",
},
{
name: "alpine 3.10 integration with token",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/alpine-310.tar.gz",
ClientToken: "token",
ServerToken: "token",
},
golden: "testdata/alpine-310.json.golden",
},
{
name: "alpine 3.10 integration with --ignore-unfixed option",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Input: "testdata/fixtures/alpine-310.tar.gz",
},
golden: "testdata/alpine-310-ignore-unfixed.json.golden",
},
{
name: "alpine 3.10 integration with medium and high severity",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Severity: []string{"MEDIUM", "HIGH"},
Input: "testdata/fixtures/alpine-310.tar.gz",
},
golden: "testdata/alpine-310-medium-high.json.golden",
},
{
name: "alpine 3.10 integration with .trivyignore",
testArgs: args{
Version: "dev",
IgnoreUnfixed: false,
IgnoreIDs: []string{"CVE-2019-1549", "CVE-2019-1563"},
Input: "testdata/fixtures/alpine-310.tar.gz",
},
golden: "testdata/alpine-310-ignore-cveids.json.golden",
},
{
name: "alpine 3.9 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/alpine-39.tar.gz",
},
golden: "testdata/alpine-39.json.golden",
},
{
name: "debian buster integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/debian-buster.tar.gz",
},
golden: "testdata/debian-buster.json.golden",
},
{
name: "debian buster integration with --ignore-unfixed option",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Input: "testdata/fixtures/debian-buster.tar.gz",
},
golden: "testdata/debian-buster-ignore-unfixed.json.golden",
},
{
name: "debian stretch integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/debian-stretch.tar.gz",
},
golden: "testdata/debian-stretch.json.golden",
},
{
name: "ubuntu 18.04 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/ubuntu-1804.tar.gz",
},
golden: "testdata/ubuntu-1804.json.golden",
},
{
name: "ubuntu 18.04 integration with --ignore-unfixed option",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Input: "testdata/fixtures/ubuntu-1804.tar.gz",
},
golden: "testdata/ubuntu-1804-ignore-unfixed.json.golden",
},
{
name: "ubuntu 16.04 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/ubuntu-1604.tar.gz",
},
golden: "testdata/ubuntu-1604.json.golden",
},
{
name: "centos 7 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/centos-7.tar.gz",
},
golden: "testdata/centos-7.json.golden",
},
{
name: "centos 7 integration with --ignore-unfixed option",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Input: "testdata/fixtures/centos-7.tar.gz",
},
golden: "testdata/centos-7-ignore-unfixed.json.golden",
},
{
name: "centos 7 integration with critical severity",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Severity: []string{"CRITICAL"},
Input: "testdata/fixtures/centos-7.tar.gz",
},
golden: "testdata/centos-7-critical.json.golden",
},
{
name: "centos 7 integration with low and high severity",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Severity: []string{"LOW", "HIGH"},
Input: "testdata/fixtures/centos-7.tar.gz",
},
golden: "testdata/centos-7-low-high.json.golden",
},
{
name: "centos 6 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/centos-6.tar.gz",
},
golden: "testdata/centos-6.json.golden",
},
{
name: "ubi 7 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/ubi-7.tar.gz",
},
golden: "testdata/ubi-7.json.golden",
},
{
name: "distroless base integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/distroless-base.tar.gz",
},
golden: "testdata/distroless-base.json.golden",
},
{
name: "distroless base integration with --ignore-unfixed option",
testArgs: args{
Version: "dev",
IgnoreUnfixed: true,
Input: "testdata/fixtures/distroless-base.tar.gz",
},
golden: "testdata/distroless-base-ignore-unfixed.json.golden",
},
{
name: "distroless python27 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/distroless-python27.tar.gz",
},
golden: "testdata/distroless-python27.json.golden",
},
{
name: "amazon 1 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/amazon-1.tar.gz",
},
golden: "testdata/amazon-1.json.golden",
},
{
name: "amazon 2 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/amazon-2.tar.gz",
},
golden: "testdata/amazon-2.json.golden",
},
{
name: "oracle 6 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/oraclelinux-6-slim.tar.gz",
},
golden: "testdata/oraclelinux-6-slim.json.golden",
},
{
name: "oracle 7 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/oraclelinux-7-slim.tar.gz",
},
golden: "testdata/oraclelinux-7-slim.json.golden",
},
{
name: "oracle 8 integration",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/oraclelinux-8-slim.tar.gz",
},
golden: "testdata/oraclelinux-8-slim.json.golden",
},
{
name: "invalid token",
testArgs: args{
Version: "dev",
Input: "testdata/fixtures/distroless-base.tar.gz",
ClientToken: "invalidtoken",
ServerToken: "token",
},
wantErr: "twirp error unauthenticated: invalid token",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
// Copy DB file
cacheDir := gunzipDB()
defer os.RemoveAll(cacheDir)
port, err := getFreePort()
require.NoError(t, err, c.name)
addr := fmt.Sprintf("localhost:%d", port)
go func() {
// Setup CLI App
app := internal.NewApp(c.testArgs.Version)
app.Writer = ioutil.Discard
osArgs := setupServer(addr, c.testArgs.ServerToken, cacheDir)
// Run Trivy server
require.NoError(t, app.Run(osArgs), c.name)
}()
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
require.NoError(t, waitPort(ctx, addr), c.name)
// Setup CLI App
app := internal.NewApp(c.testArgs.Version)
app.Writer = ioutil.Discard
osArgs, outputFile, cleanup := setupClient(t, c.testArgs.IgnoreUnfixed, c.testArgs.Severity,
c.testArgs.IgnoreIDs, addr, c.testArgs.ClientToken, c.testArgs.Input, cacheDir, c.golden)
defer cleanup()
// Run Trivy client
err = app.Run(osArgs)
if c.wantErr != "" {
require.NotNil(t, err, c.name)
assert.Contains(t, err.Error(), c.wantErr, c.name)
return
} else {
assert.NoError(t, err, c.name)
}
// Compare want and got
want, err := ioutil.ReadFile(c.golden)
assert.NoError(t, err)
got, err := ioutil.ReadFile(outputFile)
assert.NoError(t, err)
assert.JSONEq(t, string(want), string(got))
})
}
}
func setupServer(addr, token, cacheDir string) []string {
osArgs := []string{"trivy", "server", "--skip-update", "--cache-dir", cacheDir, "--listen", addr}
if token != "" {
osArgs = append(osArgs, []string{"--token", token}...)
}
return osArgs
}
func setupClient(t *testing.T, ignoreUnfixed bool, severity, ignoreIDs []string,
addr, token, input, cacheDir, golden string) ([]string, string, func()) {
t.Helper()
osArgs := []string{"trivy", "client", "--cache-dir", cacheDir,
"--format", "json", "--remote", "http://" + addr}
if ignoreUnfixed {
osArgs = append(osArgs, "--ignore-unfixed")
}
if len(severity) != 0 {
osArgs = append(osArgs,
[]string{"--severity", strings.Join(severity, ",")}...,
)
}
var err error
var ignoreTmpDir string
if len(ignoreIDs) != 0 {
ignoreTmpDir, err = ioutil.TempDir("", "ignore")
require.NoError(t, err, "failed to create a temp dir")
trivyIgnore := filepath.Join(ignoreTmpDir, ".trivyignore")
err = ioutil.WriteFile(trivyIgnore, []byte(strings.Join(ignoreIDs, "\n")), 0444)
require.NoError(t, err, "failed to write .trivyignore")
osArgs = append(osArgs, []string{"--ignorefile", trivyIgnore}...)
}
if token != "" {
osArgs = append(osArgs, []string{"--token", token}...)
}
if input != "" {
osArgs = append(osArgs, []string{"--input", input}...)
}
// Setup the output file
var outputFile string
if *update {
outputFile = golden
} else {
output, _ := ioutil.TempFile("", "integration")
assert.Nil(t, output.Close())
outputFile = output.Name()
}
cleanup := func() {
_ = os.Remove(ignoreTmpDir)
_ = os.Remove(outputFile)
}
osArgs = append(osArgs, []string{"--output", outputFile}...)
return osArgs, outputFile, cleanup
}

View File

@@ -0,0 +1,80 @@
// +build integration
package integration
import (
"compress/gzip"
"context"
"flag"
"io"
"io/ioutil"
"log"
"net"
"os"
"path/filepath"
"time"
)
var update = flag.Bool("update", false, "update golden files")
func gunzipDB() string {
gz, err := os.Open("testdata/trivy.db.gz")
if err != nil {
log.Panic(err)
}
zr, err := gzip.NewReader(gz)
if err != nil {
log.Panic(err)
}
tmpDir, err := ioutil.TempDir("", "integration")
if err != nil {
log.Panic(err)
}
dbDir := filepath.Join(tmpDir, "db")
err = os.MkdirAll(dbDir, 0700)
if err != nil {
log.Panic(err)
}
file, err := os.Create(filepath.Join(dbDir, "trivy.db"))
if err != nil {
log.Panic(err)
}
defer file.Close()
_, err = io.Copy(file, zr)
if err != nil {
log.Panic(err)
}
return tmpDir
}
func getFreePort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}
func waitPort(ctx context.Context, addr string) error {
for {
conn, err := net.Dial("tcp", addr)
if err == nil && conn != nil {
return nil
}
select {
case <-ctx.Done():
return err
default:
time.Sleep(1 * time.Second)
}
}
}

View File

@@ -1,58 +1,18 @@
// +build integration
package integration_test
package integration
import (
"compress/gzip"
"flag"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"testing"
"github.com/aquasecurity/trivy/internal"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/trivy/pkg"
)
var update = flag.Bool("update", false, "update golden files")
func gunzipDB() string {
gz, err := os.Open("testdata/trivy.db.gz")
if err != nil {
log.Panic(err)
}
zr, err := gzip.NewReader(gz)
if err != nil {
log.Panic(err)
}
tmpDir, err := ioutil.TempDir("", "integration")
if err != nil {
log.Panic(err)
}
dbDir := filepath.Join(tmpDir, "db")
err = os.MkdirAll(dbDir, 0700)
if err != nil {
log.Panic(err)
}
file, err := os.Create(filepath.Join(dbDir, "trivy.db"))
if err != nil {
log.Panic(err)
}
defer file.Close()
_, err = io.Copy(file, zr)
if err != nil {
log.Panic(err)
}
return tmpDir
}
func TestRun_WithTar(t *testing.T) {
type args struct {
Version string
@@ -301,6 +261,36 @@ func TestRun_WithTar(t *testing.T) {
},
golden: "testdata/amazon-2.json.golden",
},
{
name: "oracle 6 integration",
testArgs: args{
Version: "dev",
SkipUpdate: true,
Format: "json",
Input: "testdata/fixtures/oraclelinux-6-slim.tar.gz",
},
golden: "testdata/oraclelinux-6-slim.json.golden",
},
{
name: "oracle 7 integration",
testArgs: args{
Version: "dev",
SkipUpdate: true,
Format: "json",
Input: "testdata/fixtures/oraclelinux-7-slim.tar.gz",
},
golden: "testdata/oraclelinux-7-slim.json.golden",
},
{
name: "oracle 8 integration",
testArgs: args{
Version: "dev",
SkipUpdate: true,
Format: "json",
Input: "testdata/fixtures/oraclelinux-8-slim.tar.gz",
},
golden: "testdata/oraclelinux-8-slim.json.golden",
},
}
for _, c := range cases {
@@ -310,7 +300,7 @@ func TestRun_WithTar(t *testing.T) {
defer os.RemoveAll(cacheDir)
// Setup CLI App
app := pkg.NewApp(c.testArgs.Version)
app := internal.NewApp(c.testArgs.Version)
app.Writer = ioutil.Discard
osArgs := []string{"trivy", "--cache-dir", cacheDir, "--format", c.testArgs.Format}

View File

@@ -0,0 +1,166 @@
[
{
"Target": "testdata/fixtures/oraclelinux-6-slim.tar.gz (oracle 6.10)",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-3855",
"PkgName": "libssh2",
"InstalledVersion": "1.4.2-2.el6_7.1",
"FixedVersion": "1.4.2-3.0.1.el6_10.1",
"Title": "libssh2: Integer overflow in transport read resulting in out of bounds write",
"Description": "An integer overflow flaw which could lead to an out of bounds write was discovered in libssh2 before 1.8.1 in the way packets are read from the server. A remote attacker who compromises a SSH server may be able to execute code on the client system when a user connects to the server.",
"Severity": "CRITICAL",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"http://packetstormsecurity.com/files/152136/Slackware-Security-Advisory-libssh2-Updates.html",
"http://www.openwall.com/lists/oss-security/2019/03/18/3",
"http://www.securityfocus.com/bid/107485",
"https://access.redhat.com/errata/RHSA-2019:0679",
"https://access.redhat.com/errata/RHSA-2019:1175",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3855",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3855",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XCWEA5ZCLKRDUK62QVVYMFWLWKOPX3LO/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://seclists.org/bugtraq/2019/Mar/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.broadcom.com/support/fibre-channel-networking/security-advisories/brocade-security-advisory-2019-767",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3855.html"
]
},
{
"VulnerabilityID": "CVE-2019-3856",
"PkgName": "libssh2",
"InstalledVersion": "1.4.2-2.el6_7.1",
"FixedVersion": "1.4.2-3.0.1.el6_10.1",
"Title": "libssh2: Integer overflow in keyboard interactive handling resulting in out of bounds write",
"Description": "An integer overflow flaw, which could lead to an out of bounds write, was discovered in libssh2 before 1.8.1 in the way keyboard prompt requests are parsed. A remote attacker who compromises a SSH server may be able to execute code on the client system when a user connects to the server.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"https://access.redhat.com/errata/RHSA-2019:0679",
"https://access.redhat.com/errata/RHSA-2019:1175",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3856",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3856",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3856.html"
]
},
{
"VulnerabilityID": "CVE-2019-3857",
"PkgName": "libssh2",
"InstalledVersion": "1.4.2-2.el6_7.1",
"FixedVersion": "1.4.2-3.0.1.el6_10.1",
"Title": "libssh2: Integer overflow in SSH packet processing channel resulting in out of bounds write",
"Description": "An integer overflow flaw which could lead to an out of bounds write was discovered in libssh2 before 1.8.1 in the way SSH_MSG_CHANNEL_REQUEST packets with an exit signal are parsed. A remote attacker who compromises a SSH server may be able to execute code on the client system when a user connects to the server.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"https://access.redhat.com/errata/RHSA-2019:0679",
"https://access.redhat.com/errata/RHSA-2019:1175",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3857",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3857",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3857.html"
]
},
{
"VulnerabilityID": "CVE-2019-3862",
"PkgName": "libssh2",
"InstalledVersion": "1.4.2-2.el6_7.1",
"FixedVersion": "1.4.2-2.0.1.el6_7.1",
"Title": "libssh2: Out-of-bounds memory comparison with specially crafted message channel request",
"Description": "An out of bounds read flaw was discovered in libssh2 before 1.8.1 in the way SSH_MSG_CHANNEL_REQUEST packets with an exit status message and no payload are parsed. A remote attacker who compromises a SSH server may be able to cause a Denial of Service or read data in the client memory.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"http://packetstormsecurity.com/files/152136/Slackware-Security-Advisory-libssh2-Updates.html",
"http://www.openwall.com/lists/oss-security/2019/03/18/3",
"http://www.securityfocus.com/bid/107485",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3862",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3862",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XCWEA5ZCLKRDUK62QVVYMFWLWKOPX3LO/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://seclists.org/bugtraq/2019/Mar/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.broadcom.com/support/fibre-channel-networking/security-advisories/brocade-security-advisory-2019-767",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3862.html"
]
},
{
"VulnerabilityID": "CVE-2019-3863",
"PkgName": "libssh2",
"InstalledVersion": "1.4.2-2.el6_7.1",
"FixedVersion": "1.4.2-3.0.1.el6_10.1",
"Title": "libssh2: Integer overflow in user authenticate keyboard interactive allows out-of-bounds writes",
"Description": "A flaw was found in libssh2 before 1.8.1. A server could send a multiple keyboard interactive response messages whose total length are greater than unsigned char max characters. This value is used as an index to copy memory causing in an out of bounds memory write error.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"https://access.redhat.com/errata/RHSA-2019:0679",
"https://access.redhat.com/errata/RHSA-2019:1175",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3863",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3863",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3863.html"
]
},
{
"VulnerabilityID": "CVE-2019-1559",
"PkgName": "openssl",
"InstalledVersion": "1.0.1e-57.0.6.el6",
"FixedVersion": "1.0.1e-58.0.1.el6_10",
"Title": "openssl: 0-byte record padding oracle",
"Description": "If an application encounters a fatal protocol error and then calls SSL_shutdown() twice (once to send a close_notify, and once to receive one) then OpenSSL can respond differently to the calling application if a 0 byte record is received with invalid padding compared to if a 0 byte record is received with an invalid MAC. If the application then behaves differently based on that in a way that is detectable to the remote peer, then this amounts to a padding oracle that could be used to decrypt data. In order for this to be exploitable \"non-stitched\" ciphersuites must be in use. Stitched ciphersuites are optimised implementations of certain commonly used ciphersuites. Also the application must call SSL_shutdown() twice even if a protocol error has occurred (applications should not do this but some do anyway). Fixed in OpenSSL 1.0.2r (Affected 1.0.2-1.0.2q).",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00041.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00019.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00046.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00047.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-05/msg00049.html",
"http://www.securityfocus.com/bid/107174",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1559",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=e9bbefbf0f24c57645e7ad6a5a71ae649d18ac8e",
"https://github.com/RUB-NDS/TLS-Padding-Oracles",
"https://kc.mcafee.com/corporate/index?page=content\u0026id=SB10282",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00003.html",
"https://security.gentoo.org/glsa/201903-10",
"https://security.netapp.com/advisory/ntap-20190301-0001/",
"https://security.netapp.com/advisory/ntap-20190301-0002/",
"https://security.netapp.com/advisory/ntap-20190423-0002/",
"https://support.f5.com/csp/article/K18549143",
"https://usn.ubuntu.com/3899-1/",
"https://www.debian.org/security/2019/dsa-4400",
"https://www.openssl.org/news/secadv/20190226.txt",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html",
"https://www.tenable.com/security/tns-2019-02",
"https://www.tenable.com/security/tns-2019-03"
]
}
]
}
]

View File

@@ -0,0 +1,859 @@
[
{
"Target": "testdata/fixtures/oraclelinux-7-slim.tar.gz (oracle 7.6)",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2018-16842",
"PkgName": "curl",
"InstalledVersion": "7.29.0-51.0.1.el7_6.3",
"FixedVersion": "7.29.0-54.0.1.el7",
"Title": "curl: Heap-based buffer over-read in the curl tool warning formatting",
"Description": "Curl versions 7.14.1 through 7.61.1 are vulnerable to a heap-based buffer over-read in the tool_msgs.c:voutf() function that may result in information exposure and denial of service.",
"Severity": "MEDIUM",
"References": [
"http://www.securitytracker.com/id/1042014",
"https://access.redhat.com/errata/RHSA-2019:2181",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-16842",
"https://curl.haxx.se/docs/CVE-2018-16842.html",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16842",
"https://github.com/curl/curl/commit/d530e92f59ae9bb2d47066c3c460b25d2ffeb211",
"https://lists.debian.org/debian-lts-announce/2018/11/msg00005.html",
"https://security.gentoo.org/glsa/201903-03",
"https://usn.ubuntu.com/3805-1/",
"https://usn.ubuntu.com/3805-2/",
"https://www.debian.org/security/2018/dsa-4331"
]
},
{
"VulnerabilityID": "CVE-2018-16402",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: Double-free due to double decompression of sections in crafted ELF causes crash",
"Description": "libelf/elf_end.c in elfutils 0.173 allows remote attackers to cause a denial of service (double free and application crash) or possibly have unspecified other impact because it tries to decompress twice.",
"Severity": "HIGH",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16402",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23528",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2018-16062",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: Heap-based buffer over-read in libdw/dwarf_getaranges.c:dwarf_getaranges() via crafted file",
"Description": "dwarf_getaranges in dwarf_getaranges.c in libdw in elfutils before 2018-08-18 allows remote attackers to cause a denial of service (heap-based buffer over-read) via a crafted file.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00052.html",
"https://access.redhat.com/errata/RHSA-2019:2197",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16062",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23541",
"https://sourceware.org/git/?p=elfutils.git;a=commit;h=29e31978ba51c1051743a503ee325b5ebc03d7e9",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2018-16403",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: Heap-based buffer over-read in libdw/dwarf_getabbrev.c and libwd/dwarf_hasattr.c causes crash",
"Description": "libdw in elfutils 0.173 checks the end of the attributes list incorrectly in dwarf_getabbrev in dwarf_getabbrev.c and dwarf_hasattr in dwarf_hasattr.c, leading to a heap-based buffer over-read and an application crash.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00052.html",
"https://access.redhat.com/errata/RHSA-2019:2197",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16403",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23529",
"https://sourceware.org/git/?p=elfutils.git;a=commit;h=6983e59b727458a6c64d9659c85f08218bc4fcda",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2018-18310",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: invalid memory address dereference was discovered in dwfl_segment_report_module.c in libdwfl",
"Description": "An invalid memory address dereference was discovered in dwfl_segment_report_module.c in libdwfl in elfutils through v0.174. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file, as demonstrated by consider_notes.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-18310",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23752",
"https://sourceware.org/ml/elfutils-devel/2018-q4/msg00022.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2018-18520",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: eu-size cannot handle recursive ar files",
"Description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-18520",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23787",
"https://sourceware.org/ml/elfutils-devel/2018-q4/msg00057.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2018-18521",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: Divide-by-zero in arlib_add_symbols function in arlib.c",
"Description": "Divide-by-zero vulnerabilities in the function arlib_add_symbols() in arlib.c in elfutils 0.174 allow remote attackers to cause a denial of service (application crash) with a crafted ELF file, as demonstrated by eu-ranlib, because a zero sh_entsize is mishandled.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-18521",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=23786",
"https://sourceware.org/ml/elfutils-devel/2018-q4/msg00055.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2019-7149",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: heap-based buffer over-read in read_srclines in dwarf_getsrclines.c in libdw",
"Description": "A heap-based buffer over-read was discovered in the function read_srclines in dwarf_getsrclines.c in libdw in elfutils 0.175. A crafted input can cause segmentation faults, leading to denial-of-service, as demonstrated by eu-nm.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7149",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=24102",
"https://sourceware.org/ml/elfutils-devel/2019-q1/msg00068.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2019-7150",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: segmentation fault in elf64_xlatetom in libelf/elf32_xlatetom.c",
"Description": "An issue was discovered in elfutils 0.175. A segmentation fault can occur in the function elf64_xlatetom in libelf/elf32_xlatetom.c, due to dwfl_segment_report_module not checking whether the dyn data read from a core file is truncated. A crafted input can cause a program crash, leading to denial-of-service, as demonstrated by eu-stack.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7150",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=24103",
"https://sourceware.org/ml/elfutils-devel/2019-q1/msg00070.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2019-7664",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: out of bound write in elf_cvt_note in libelf/note_xlate.h",
"Description": "In elfutils 0.175, a negative-sized memcpy is attempted in elf_cvt_note in libelf/note_xlate.h because of an incorrect overflow check. Crafted elf input causes a segmentation fault, leading to denial of service (program crash).",
"Severity": "MEDIUM",
"References": [
"https://access.redhat.com/errata/RHSA-2019:2197",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7664",
"https://sourceware.org/bugzilla/show_bug.cgi?id=24084"
]
},
{
"VulnerabilityID": "CVE-2019-7665",
"PkgName": "elfutils-libelf",
"InstalledVersion": "0.172-2.el7",
"FixedVersion": "0.176-2.el7",
"Title": "elfutils: heap-based buffer over-read in function elf32_xlatetom in elf32_xlatetom.c",
"Description": "In elfutils 0.175, a heap-based buffer over-read was discovered in the function elf32_xlatetom in elf32_xlatetom.c in libelf. A crafted ELF input can cause a segmentation fault leading to denial of service (program crash) because ebl_core_note does not reject malformed core file notes.",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7665",
"https://lists.debian.org/debian-lts-announce/2019/02/msg00036.html",
"https://sourceware.org/bugzilla/show_bug.cgi?id=24089",
"https://sourceware.org/ml/elfutils-devel/2019-q1/msg00049.html",
"https://usn.ubuntu.com/4012-1/"
]
},
{
"VulnerabilityID": "CVE-2016-10739",
"PkgName": "glibc",
"InstalledVersion": "2.17-260.0.17.el7_6.6",
"FixedVersion": "2.17-292.0.1.el7",
"Title": "glibc: getaddrinfo should reject IP addresses with trailing characters",
"Description": "In the GNU C Library (aka glibc or libc6) through 2.28, the getaddrinfo function would successfully parse a string that contained an IPv4 address followed by whitespace and arbitrary characters, which could lead applications to incorrectly assume that it had parsed a valid string, without the possibility of embedded HTTP headers or other potentially dangerous substrings.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00082.html",
"http://www.securityfocus.com/bid/106672",
"https://access.redhat.com/errata/RHSA-2019:2118",
"https://bugzilla.redhat.com/show_bug.cgi?id=1347549",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10739",
"https://sourceware.org/bugzilla/show_bug.cgi?id=20018"
]
},
{
"VulnerabilityID": "CVE-2016-10739",
"PkgName": "glibc-common",
"InstalledVersion": "2.17-260.0.17.el7_6.6",
"FixedVersion": "2.17-292.0.1.el7",
"Title": "glibc: getaddrinfo should reject IP addresses with trailing characters",
"Description": "In the GNU C Library (aka glibc or libc6) through 2.28, the getaddrinfo function would successfully parse a string that contained an IPv4 address followed by whitespace and arbitrary characters, which could lead applications to incorrectly assume that it had parsed a valid string, without the possibility of embedded HTTP headers or other potentially dangerous substrings.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00082.html",
"http://www.securityfocus.com/bid/106672",
"https://access.redhat.com/errata/RHSA-2019:2118",
"https://bugzilla.redhat.com/show_bug.cgi?id=1347549",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10739",
"https://sourceware.org/bugzilla/show_bug.cgi?id=20018"
]
},
{
"VulnerabilityID": "CVE-2018-16842",
"PkgName": "libcurl",
"InstalledVersion": "7.29.0-51.0.1.el7_6.3",
"FixedVersion": "7.29.0-54.0.1.el7",
"Title": "curl: Heap-based buffer over-read in the curl tool warning formatting",
"Description": "Curl versions 7.14.1 through 7.61.1 are vulnerable to a heap-based buffer over-read in the tool_msgs.c:voutf() function that may result in information exposure and denial of service.",
"Severity": "MEDIUM",
"References": [
"http://www.securitytracker.com/id/1042014",
"https://access.redhat.com/errata/RHSA-2019:2181",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-16842",
"https://curl.haxx.se/docs/CVE-2018-16842.html",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16842",
"https://github.com/curl/curl/commit/d530e92f59ae9bb2d47066c3c460b25d2ffeb211",
"https://lists.debian.org/debian-lts-announce/2018/11/msg00005.html",
"https://security.gentoo.org/glsa/201903-03",
"https://usn.ubuntu.com/3805-1/",
"https://usn.ubuntu.com/3805-2/",
"https://www.debian.org/security/2018/dsa-4331"
]
},
{
"VulnerabilityID": "CVE-2019-3858",
"PkgName": "libssh2",
"InstalledVersion": "1.4.3-12.0.1.el7_6.3",
"FixedVersion": "1.8.0-3.el7",
"Title": "libssh2: Zero-byte allocation with a specially crafted SFTP packed leading to an out-of-bounds read",
"Description": "An out of bounds read flaw was discovered in libssh2 before 1.8.1 when a specially crafted SFTP packet is received from the server. A remote attacker who compromises a SSH server may be able to cause a Denial of Service or read data in the client memory.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"http://packetstormsecurity.com/files/152136/Slackware-Security-Advisory-libssh2-Updates.html",
"http://www.openwall.com/lists/oss-security/2019/03/18/3",
"http://www.securityfocus.com/bid/107485",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3858",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3858",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XCWEA5ZCLKRDUK62QVVYMFWLWKOPX3LO/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://seclists.org/bugtraq/2019/Mar/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.broadcom.com/support/fibre-channel-networking/security-advisories/brocade-security-advisory-2019-767",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3858.html"
]
},
{
"VulnerabilityID": "CVE-2019-3861",
"PkgName": "libssh2",
"InstalledVersion": "1.4.3-12.0.1.el7_6.3",
"FixedVersion": "1.8.0-3.el7",
"Title": "libssh2: Out-of-bounds reads with specially crafted SSH packets",
"Description": "An out of bounds read flaw was discovered in libssh2 before 1.8.1 in the way SSH packets with a padding length value greater than the packet length are parsed. A remote attacker who compromises a SSH server may be able to cause a Denial of Service or read data in the client memory.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00040.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00003.html",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3861",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3861",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00032.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5DK6VO2CEUTAJFYIKWNZKEKYMYR3NO2O/",
"https://seclists.org/bugtraq/2019/Apr/25",
"https://security.netapp.com/advisory/ntap-20190327-0005/",
"https://www.debian.org/security/2019/dsa-4431",
"https://www.libssh2.org/CVE-2019-3861.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nspr",
"InstalledVersion": "4.19.0-1.el7_5",
"FixedVersion": "4.21.0-1.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nspr",
"InstalledVersion": "4.19.0-1.el7_5",
"FixedVersion": "4.21.0-1.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss-softokn",
"InstalledVersion": "3.36.0-5.0.1.el7_5",
"FixedVersion": "3.44.0-5.0.1.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss-softokn",
"InstalledVersion": "3.36.0-5.0.1.el7_5",
"FixedVersion": "3.44.0-5.0.1.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss-softokn-freebl",
"InstalledVersion": "3.36.0-5.0.1.el7_5",
"FixedVersion": "3.44.0-5.0.1.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss-softokn-freebl",
"InstalledVersion": "3.36.0-5.0.1.el7_5",
"FixedVersion": "3.44.0-5.0.1.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss-sysinit",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss-sysinit",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss-tools",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss-tools",
"InstalledVersion": "3.36.0-7.1.el7_6",
"FixedVersion": "3.44.0-4.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-12404",
"PkgName": "nss-util",
"InstalledVersion": "3.36.0-1.1.el7_6",
"FixedVersion": "3.44.0-3.el7",
"Title": "nss: Cache side-channel variant of the Bleichenbacher attack",
"Description": "A cached side channel attack during handshakes using RSA encryption could allow for the decryption of encrypted content. This is a variant of the Adaptive Chosen Ciphertext attack (AKA Bleichenbacher attack) and affects all NSS versions prior to NSS 3.41.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-07/msg00021.html",
"http://www.securityfocus.com/bid/107260",
"https://bugzilla.mozilla.org/show_bug.cgi?id=CVE-2018-12404",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12404"
]
},
{
"VulnerabilityID": "CVE-2018-0495",
"PkgName": "nss-util",
"InstalledVersion": "3.36.0-1.1.el7_6",
"FixedVersion": "3.44.0-3.el7",
"Title": "ROHNP: Key Extraction Side Channel in Multiple Crypto Libraries",
"Description": "Libgcrypt before 1.7.10 and 1.8.x before 1.8.3 allows a memory-cache side-channel attack on ECDSA signatures that can be mitigated through the use of blinding during the signing process in the _gcry_ecc_ecdsa_sign function in cipher/ecc-ecdsa.c, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.",
"Severity": "LOW",
"References": [
"http://www.securitytracker.com/id/1041144",
"http://www.securitytracker.com/id/1041147",
"https://access.redhat.com/errata/RHSA-2018:3221",
"https://access.redhat.com/errata/RHSA-2018:3505",
"https://access.redhat.com/errata/RHSA-2019:1296",
"https://access.redhat.com/errata/RHSA-2019:1297",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495",
"https://dev.gnupg.org/T4011",
"https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=commit;h=9010d1576e278a4274ad3f4aa15776c28f6ba965",
"https://lists.debian.org/debian-lts-announce/2018/06/msg00013.html",
"https://lists.gnupg.org/pipermail/gnupg-announce/2018q2/000426.html",
"https://usn.ubuntu.com/3689-1/",
"https://usn.ubuntu.com/3689-2/",
"https://usn.ubuntu.com/3692-1/",
"https://usn.ubuntu.com/3692-2/",
"https://usn.ubuntu.com/3850-1/",
"https://usn.ubuntu.com/3850-2/",
"https://www.debian.org/security/2018/dsa-4231",
"https://www.nccgroup.trust/us/our-research/technical-advisory-return-of-the-hidden-number-problem/",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html"
]
},
{
"VulnerabilityID": "CVE-2018-0734",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.0.1.el7_6.1",
"FixedVersion": "1:1.0.2k-19.0.1.el7",
"Title": "openssl: timing side channel attack in the DSA signature algorithm",
"Description": "The OpenSSL DSA signature algorithm has been shown to be vulnerable to a timing side channel attack. An attacker could use variations in the signing algorithm to recover the private key. Fixed in OpenSSL 1.1.1a (Affected 1.1.1). Fixed in OpenSSL 1.1.0j (Affected 1.1.0-1.1.0i). Fixed in OpenSSL 1.0.2q (Affected 1.0.2-1.0.2p).",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00030.html",
"http://www.securityfocus.com/bid/105758",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0734",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=43e6a58d4991a451daf4891ff05a48735df871ac",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=8abfe72e8c1de1b95f50aa0d9134803b4d00070f",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=ef11e19d1365eea2b1851e6f540a0bf365d303e7",
"https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/",
"https://security.netapp.com/advisory/ntap-20181105-0002/",
"https://security.netapp.com/advisory/ntap-20190118-0002/",
"https://security.netapp.com/advisory/ntap-20190423-0002/",
"https://usn.ubuntu.com/3840-1/",
"https://www.debian.org/security/2018/dsa-4348",
"https://www.debian.org/security/2018/dsa-4355",
"https://www.openssl.org/news/secadv/20181030.txt",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html",
"https://www.oracle.com/technetwork/security-advisory/cpujan2019-5072801.html",
"https://www.tenable.com/security/tns-2018-16",
"https://www.tenable.com/security/tns-2018-17"
]
},
{
"VulnerabilityID": "CVE-2019-1559",
"PkgName": "openssl-libs",
"InstalledVersion": "1:1.0.2k-16.0.1.el7_6.1",
"FixedVersion": "1:1.0.2k-19.0.1.el7",
"Title": "openssl: 0-byte record padding oracle",
"Description": "If an application encounters a fatal protocol error and then calls SSL_shutdown() twice (once to send a close_notify, and once to receive one) then OpenSSL can respond differently to the calling application if a 0 byte record is received with invalid padding compared to if a 0 byte record is received with an invalid MAC. If the application then behaves differently based on that in a way that is detectable to the remote peer, then this amounts to a padding oracle that could be used to decrypt data. In order for this to be exploitable \"non-stitched\" ciphersuites must be in use. Stitched ciphersuites are optimised implementations of certain commonly used ciphersuites. Also the application must call SSL_shutdown() twice even if a protocol error has occurred (applications should not do this but some do anyway). Fixed in OpenSSL 1.0.2r (Affected 1.0.2-1.0.2q).",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-03/msg00041.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00019.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00046.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00047.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-05/msg00049.html",
"http://www.securityfocus.com/bid/107174",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1559",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=e9bbefbf0f24c57645e7ad6a5a71ae649d18ac8e",
"https://github.com/RUB-NDS/TLS-Padding-Oracles",
"https://kc.mcafee.com/corporate/index?page=content\u0026id=SB10282",
"https://lists.debian.org/debian-lts-announce/2019/03/msg00003.html",
"https://security.gentoo.org/glsa/201903-10",
"https://security.netapp.com/advisory/ntap-20190301-0001/",
"https://security.netapp.com/advisory/ntap-20190301-0002/",
"https://security.netapp.com/advisory/ntap-20190423-0002/",
"https://support.f5.com/csp/article/K18549143",
"https://usn.ubuntu.com/3899-1/",
"https://www.debian.org/security/2019/dsa-4400",
"https://www.openssl.org/news/secadv/20190226.txt",
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html",
"https://www.tenable.com/security/tns-2019-02",
"https://www.tenable.com/security/tns-2019-03"
]
},
{
"VulnerabilityID": "CVE-2019-5010",
"PkgName": "python",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: NULL pointer dereference using a specially crafted X509 certificate",
"Description": "A null pointer dereference vulnerability was found in the certificate parsing code in Python. This causes a denial of service to applications when parsing specially crafted certificates. This vulnerability is unlikely to be triggered if application enables SSL/TLS certificate validation and accepts certificates only from trusted root certificate authorities.",
"Severity": "HIGH",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5010",
"https://python-security.readthedocs.io/vuln/ssl-crl-dps-dos.html"
]
},
{
"VulnerabilityID": "CVE-2018-14647",
"PkgName": "python",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: Missing salt initialization in _elementtree.c module",
"Description": "Python's elementtree C accelerator failed to initialise Expat's hash salt during initialization. This could make it easy to conduct denial of service attacks against Expat by constructing an XML document that would cause pathological hash collisions in Expat's internal data structures, consuming large amounts CPU and RAM. Python 3.8, 3.7, 3.6, 3.5, 3.4, 2.7 are believed to be vulnerable.",
"Severity": "MEDIUM",
"References": [
"http://www.securityfocus.com/bid/105396",
"http://www.securitytracker.com/id/1041740",
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://access.redhat.com/errata/RHSA-2019:2030",
"https://bugs.python.org/issue34623",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-14647",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-14647",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00022.html",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00023.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RBJCB2HWOJLP3L7CUQHJHNBHLSVOXJE5/",
"https://usn.ubuntu.com/3817-1/",
"https://usn.ubuntu.com/3817-2/",
"https://www.debian.org/security/2018/dsa-4306",
"https://www.debian.org/security/2018/dsa-4307"
]
},
{
"VulnerabilityID": "CVE-2019-9740",
"PkgName": "python",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: CRLF injection via the query part of the url passed to urlopen()",
"Description": "An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \\r\\n (specifically in the query string after a ? character) followed by an HTTP header or a Redis command.",
"Severity": "MEDIUM",
"References": [
"http://www.securityfocus.com/bid/107466",
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://bugs.python.org/issue36276",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9740",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JMWSKTNOHSUOT3L25QFJAVCFYZX46FYK/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JXASHCDD4PQFKTMKQN4YOP5ZH366ABN4/"
]
},
{
"VulnerabilityID": "CVE-2019-9947",
"PkgName": "python",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: CRLF injection via the path part of the url passed to urlopen()",
"Description": "An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \\r\\n (specifically in the path component of a URL that lacks a ? character) followed by an HTTP header or a Redis command. This is similar to the CVE-2019-9740 query string issue.",
"Severity": "MEDIUM",
"References": [
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://bugs.python.org/issue35906",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9947",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JMWSKTNOHSUOT3L25QFJAVCFYZX46FYK/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JXASHCDD4PQFKTMKQN4YOP5ZH366ABN4/",
"https://security.netapp.com/advisory/ntap-20190404-0004/"
]
},
{
"VulnerabilityID": "CVE-2019-9948",
"PkgName": "python",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: Undocumented local_file protocol allows remote attackers to bypass protection mechanisms",
"Description": "urllib in Python 2.x through 2.7.16 supports the local_file: scheme, which makes it easier for remote attackers to bypass protection mechanisms that blacklist file: URIs, as demonstrated by triggering a urllib.urlopen('local_file:///etc/passwd') call.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00092.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00050.html",
"http://www.securityfocus.com/bid/107549",
"https://bugs.python.org/issue35907",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9948",
"https://github.com/python/cpython/pull/11842",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00022.html",
"https://security.netapp.com/advisory/ntap-20190404-0004/"
]
},
{
"VulnerabilityID": "CVE-2019-5010",
"PkgName": "python-libs",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: NULL pointer dereference using a specially crafted X509 certificate",
"Description": "A null pointer dereference vulnerability was found in the certificate parsing code in Python. This causes a denial of service to applications when parsing specially crafted certificates. This vulnerability is unlikely to be triggered if application enables SSL/TLS certificate validation and accepts certificates only from trusted root certificate authorities.",
"Severity": "HIGH",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5010",
"https://python-security.readthedocs.io/vuln/ssl-crl-dps-dos.html"
]
},
{
"VulnerabilityID": "CVE-2018-14647",
"PkgName": "python-libs",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: Missing salt initialization in _elementtree.c module",
"Description": "Python's elementtree C accelerator failed to initialise Expat's hash salt during initialization. This could make it easy to conduct denial of service attacks against Expat by constructing an XML document that would cause pathological hash collisions in Expat's internal data structures, consuming large amounts CPU and RAM. Python 3.8, 3.7, 3.6, 3.5, 3.4, 2.7 are believed to be vulnerable.",
"Severity": "MEDIUM",
"References": [
"http://www.securityfocus.com/bid/105396",
"http://www.securitytracker.com/id/1041740",
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://access.redhat.com/errata/RHSA-2019:2030",
"https://bugs.python.org/issue34623",
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-14647",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-14647",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00022.html",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00023.html",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RBJCB2HWOJLP3L7CUQHJHNBHLSVOXJE5/",
"https://usn.ubuntu.com/3817-1/",
"https://usn.ubuntu.com/3817-2/",
"https://www.debian.org/security/2018/dsa-4306",
"https://www.debian.org/security/2018/dsa-4307"
]
},
{
"VulnerabilityID": "CVE-2019-9740",
"PkgName": "python-libs",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: CRLF injection via the query part of the url passed to urlopen()",
"Description": "An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \\r\\n (specifically in the query string after a ? character) followed by an HTTP header or a Redis command.",
"Severity": "MEDIUM",
"References": [
"http://www.securityfocus.com/bid/107466",
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://bugs.python.org/issue36276",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9740",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JMWSKTNOHSUOT3L25QFJAVCFYZX46FYK/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JXASHCDD4PQFKTMKQN4YOP5ZH366ABN4/"
]
},
{
"VulnerabilityID": "CVE-2019-9947",
"PkgName": "python-libs",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: CRLF injection via the path part of the url passed to urlopen()",
"Description": "An issue was discovered in urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3. CRLF injection is possible if the attacker controls a url parameter, as demonstrated by the first argument to urllib.request.urlopen with \\r\\n (specifically in the path component of a URL that lacks a ? character) followed by an HTTP header or a Redis command. This is similar to the CVE-2019-9740 query string issue.",
"Severity": "MEDIUM",
"References": [
"https://access.redhat.com/errata/RHSA-2019:1260",
"https://bugs.python.org/issue35906",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9947",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JMWSKTNOHSUOT3L25QFJAVCFYZX46FYK/",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JXASHCDD4PQFKTMKQN4YOP5ZH366ABN4/",
"https://security.netapp.com/advisory/ntap-20190404-0004/"
]
},
{
"VulnerabilityID": "CVE-2019-9948",
"PkgName": "python-libs",
"InstalledVersion": "2.7.5-80.0.1.el7_6",
"FixedVersion": "2.7.5-86.0.1.el7",
"Title": "python: Undocumented local_file protocol allows remote attackers to bypass protection mechanisms",
"Description": "urllib in Python 2.x through 2.7.16 supports the local_file: scheme, which makes it easier for remote attackers to bypass protection mechanisms that blacklist file: URIs, as demonstrated by triggering a urllib.urlopen('local_file:///etc/passwd') call.",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00092.html",
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00050.html",
"http://www.securityfocus.com/bid/107549",
"https://bugs.python.org/issue35907",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9948",
"https://github.com/python/cpython/pull/11842",
"https://lists.debian.org/debian-lts-announce/2019/06/msg00022.html",
"https://security.netapp.com/advisory/ntap-20190404-0004/"
]
}
]
}
]

View File

@@ -0,0 +1,6 @@
[
{
"Target": "testdata/fixtures/oraclelinux-8-slim.tar.gz (oracle 8.0)",
"Vulnerabilities": null
}
]

Binary file not shown.

280
internal/app.go Normal file
View File

@@ -0,0 +1,280 @@
package internal
import (
"strings"
"time"
"github.com/aquasecurity/trivy/internal/client"
"github.com/aquasecurity/trivy/internal/server"
"github.com/aquasecurity/trivy/internal/standalone"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
)
var (
templateFlag = cli.StringFlag{
Name: "template, t",
Value: "",
Usage: "output template",
EnvVar: "TRIVY_TEMPLATE",
}
formatFlag = cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: "format (table, json, template)",
EnvVar: "TRIVY_FORMAT",
}
inputFlag = cli.StringFlag{
Name: "input, i",
Value: "",
Usage: "input file path instead of image name",
EnvVar: "TRIVY_INPUT",
}
severityFlag = cli.StringFlag{
Name: "severity, s",
Value: strings.Join(types.SeverityNames, ","),
Usage: "severities of vulnerabilities to be displayed (comma separated)",
EnvVar: "TRIVY_SEVERITY",
}
outputFlag = cli.StringFlag{
Name: "output, o",
Usage: "output file name",
EnvVar: "TRIVY_OUTPUT",
}
exitCodeFlag = cli.IntFlag{
Name: "exit-code",
Usage: "Exit code when vulnerabilities were found",
Value: 0,
EnvVar: "TRIVY_EXIT_CODE",
}
skipUpdateFlag = cli.BoolFlag{
Name: "skip-update",
Usage: "skip db update",
EnvVar: "TRIVY_SKIP_UPDATE",
}
downloadDBOnlyFlag = cli.BoolFlag{
Name: "download-db-only",
Usage: "download/update vulnerability database but don't run a scan",
EnvVar: "TRIVY_DOWNLOAD_DB_ONLY",
}
resetFlag = cli.BoolFlag{
Name: "reset",
Usage: "remove all caches and database",
EnvVar: "TRIVY_RESET",
}
clearCacheFlag = cli.BoolFlag{
Name: "clear-cache, c",
Usage: "clear image caches without scanning",
EnvVar: "TRIVY_CLEAR_CACHE",
}
quietFlag = cli.BoolFlag{
Name: "quiet, q",
Usage: "suppress progress bar and log output",
EnvVar: "TRIVY_QUIET",
}
noProgressFlag = cli.BoolFlag{
Name: "no-progress",
Usage: "suppress progress bar",
EnvVar: "TRIVY_NO_PROGRESS",
}
ignoreUnfixedFlag = cli.BoolFlag{
Name: "ignore-unfixed",
Usage: "display only fixed vulnerabilities",
EnvVar: "TRIVY_IGNORE_UNFIXED",
}
debugFlag = cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",
EnvVar: "TRIVY_DEBUG",
}
vulnTypeFlag = cli.StringFlag{
Name: "vuln-type",
Value: "os,library",
Usage: "comma-separated list of vulnerability types (os,library)",
EnvVar: "TRIVY_VULN_TYPE",
}
cacheDirFlag = cli.StringFlag{
Name: "cache-dir",
Value: utils.DefaultCacheDir(),
Usage: "use as cache directory, but image cache is stored in /path/to/cache/fanal",
EnvVar: "TRIVY_CACHE_DIR",
}
ignoreFileFlag = cli.StringFlag{
Name: "ignorefile",
Value: vulnerability.DefaultIgnoreFile,
Usage: "specify .trivyignore file",
EnvVar: "TRIVY_IGNOREFILE",
}
timeoutFlag = cli.DurationFlag{
Name: "timeout",
Value: time.Second * 60,
Usage: "docker timeout",
EnvVar: "TRIVY_TIMEOUT",
}
lightFlag = cli.BoolFlag{
Name: "light",
Usage: "light mode: it's faster, but vulnerability descriptions and references are not displayed",
EnvVar: "TRIVY_LIGHT",
}
token = cli.StringFlag{
Name: "token",
Usage: "for authentication",
EnvVar: "TRIVY_TOKEN",
}
)
func NewApp(version string) *cli.App {
cli.AppHelpTemplate = `NAME:
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
OPTIONS:
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{$option}}{{end}}{{end}}
`
app := cli.NewApp()
app.Name = "trivy"
app.Version = version
app.ArgsUsage = "image_name"
app.Usage = "A simple and comprehensive vulnerability scanner for containers"
app.EnableBashCompletion = true
app.Flags = []cli.Flag{
templateFlag,
formatFlag,
inputFlag,
severityFlag,
outputFlag,
exitCodeFlag,
skipUpdateFlag,
downloadDBOnlyFlag,
resetFlag,
clearCacheFlag,
quietFlag,
noProgressFlag,
ignoreUnfixedFlag,
debugFlag,
vulnTypeFlag,
cacheDirFlag,
ignoreFileFlag,
timeoutFlag,
lightFlag,
// deprecated options
cli.StringFlag{
Name: "only-update",
Usage: "deprecated",
EnvVar: "TRIVY_ONLY_UPDATE",
},
cli.BoolFlag{
Name: "refresh",
Usage: "deprecated",
EnvVar: "TRIVY_REFRESH",
},
cli.BoolFlag{
Name: "auto-refresh",
Usage: "deprecated",
EnvVar: "TRIVY_AUTO_REFRESH",
},
}
app.Commands = []cli.Command{
NewClientCommand(),
NewServerCommand(),
}
app.Action = standalone.Run
return app
}
func NewClientCommand() cli.Command {
return cli.Command{
Name: "client",
Aliases: []string{"c"},
Usage: "client mode",
Action: client.Run,
Flags: []cli.Flag{
templateFlag,
formatFlag,
inputFlag,
severityFlag,
outputFlag,
exitCodeFlag,
clearCacheFlag,
quietFlag,
ignoreUnfixedFlag,
debugFlag,
vulnTypeFlag,
ignoreFileFlag,
cacheDirFlag,
timeoutFlag,
// original flags
token,
cli.StringFlag{
Name: "remote",
Value: "http://localhost:4954",
Usage: "server address",
EnvVar: "TRIVY_REMOTE",
},
},
}
}
func NewServerCommand() cli.Command {
return cli.Command{
Name: "server",
Aliases: []string{"s"},
Usage: "server mode",
Action: server.Run,
Flags: []cli.Flag{
skipUpdateFlag,
downloadDBOnlyFlag,
resetFlag,
quietFlag,
debugFlag,
cacheDirFlag,
// original flags
token,
cli.StringFlag{
Name: "listen",
Value: "localhost:4954",
Usage: "listen address",
EnvVar: "TRIVY_LISTEN",
},
},
}
}

View File

@@ -0,0 +1,142 @@
package config
import (
"os"
"strings"
"time"
"github.com/genuinetools/reg/registry"
"github.com/urfave/cli"
"go.uber.org/zap"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
)
type Config struct {
context *cli.Context
logger *zap.SugaredLogger
Quiet bool
Debug bool
CacheDir string
ClearCache bool
Input string
output string
Format string
Template string
Timeout time.Duration
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
RemoteAddr string
Token string
// these variables are generated by Init()
ImageName string
VulnType []string
Output *os.File
Severities []dbTypes.Severity
AppVersion string
}
func New(c *cli.Context) (Config, error) {
debug := c.Bool("debug")
quiet := c.Bool("quiet")
logger, err := log.NewLogger(debug, quiet)
if err != nil {
return Config{}, xerrors.New("failed to create a logger")
}
return Config{
context: c,
logger: logger,
Quiet: quiet,
Debug: debug,
CacheDir: c.String("cache-dir"),
ClearCache: c.Bool("clear-cache"),
Input: c.String("input"),
output: c.String("output"),
Format: c.String("format"),
Template: c.String("template"),
Timeout: c.Duration("timeout"),
vulnType: c.String("vuln-type"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
RemoteAddr: c.String("remote"),
Token: c.String("token"),
}, nil
}
func (c *Config) Init() (err error) {
c.Severities = c.splitSeverity(c.severities)
c.VulnType = strings.Split(c.vulnType, ",")
c.AppVersion = c.context.App.Version
if c.Quiet {
utils.Quiet = true
}
// --clear-cache doesn't conduct the scan
if c.ClearCache {
return nil
}
args := c.context.Args()
if c.Input == "" && len(args) == 0 {
c.logger.Error(`trivy requires at least 1 argument or --input option`)
cli.ShowAppHelp(c.context)
return xerrors.New("arguments error")
}
c.Output = os.Stdout
if c.output != "" {
if c.Output, err = os.Create(c.output); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
if c.Input == "" {
c.ImageName = args[0]
}
// Check whether 'latest' tag is used
if c.ImageName != "" {
image, err := registry.ParseImage(c.ImageName)
if err != nil {
return xerrors.Errorf("invalid image: %w", err)
}
if image.Tag == "latest" {
c.logger.Warn("You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed")
}
}
return nil
}
func (c *Config) splitSeverity(severity string) []dbTypes.Severity {
c.logger.Debugf("Severities: %s", severity)
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
c.logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -0,0 +1,229 @@
package config
import (
"flag"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
args []string
want Config
}{
{
name: "happy path",
args: []string{"-quiet", "--cache-dir", "/tmp/test"},
want: Config{
Quiet: true,
CacheDir: "/tmp/test",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &cli.App{}
set := flag.NewFlagSet("test", 0)
set.Bool("quiet", false, "")
set.String("cache-dir", "", "")
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got, err := New(c)
// avoid to compare these values because these values are pointer
tt.want.context = c
tt.want.logger = got.logger
assert.NoError(t, err, tt.name)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestConfig_Init(t *testing.T) {
type fields struct {
context *cli.Context
Quiet bool
NoProgress bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
ClearCache bool
Input string
output string
Format string
Template string
Timeout time.Duration
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
ImageName string
VulnType []string
Output *os.File
Severities []dbTypes.Severity
AppVersion string
onlyUpdate string
refresh bool
autoRefresh bool
}
tests := []struct {
name string
fields fields
args []string
logs []string
want Config
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
Quiet: true,
},
args: []string{"alpine:3.10"},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
severities: "CRITICAL",
ImageName: "alpine:3.10",
VulnType: []string{"os"},
vulnType: "os",
Output: os.Stdout,
Quiet: true,
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
vulnType: "os,library",
},
args: []string{"centos:7"},
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
severities: "CRITICAL,INVALID",
ImageName: "centos:7",
VulnType: []string{"os", "library"},
vulnType: "os,library",
Output: os.Stdout,
},
},
{
name: "with latest tag",
fields: fields{
onlyUpdate: "alpine",
severities: "LOW",
vulnType: "os,library",
},
args: []string{"gcr.io/distroless/base"},
logs: []string{
"You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed",
},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
severities: "LOW",
ImageName: "gcr.io/distroless/base",
VulnType: []string{"os", "library"},
vulnType: "os,library",
Output: os.Stdout,
},
},
{
name: "sad: no image name",
fields: fields{
severities: "MEDIUM",
},
logs: []string{
"trivy requires at least 1 argument or --input option",
},
wantErr: "arguments error",
},
{
name: "sad: invalid image name",
fields: fields{
severities: "HIGH",
},
args: []string{`!"#$%&'()`},
wantErr: "invalid image: parsing image",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
core, obs := observer.New(zap.InfoLevel)
logger := zap.New(core)
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c := &Config{
context: ctx,
logger: logger.Sugar(),
Quiet: tt.fields.Quiet,
Debug: tt.fields.Debug,
CacheDir: tt.fields.CacheDir,
ClearCache: tt.fields.ClearCache,
Input: tt.fields.Input,
output: tt.fields.output,
Format: tt.fields.Format,
Template: tt.fields.Template,
Timeout: tt.fields.Timeout,
vulnType: tt.fields.vulnType,
severities: tt.fields.severities,
IgnoreFile: tt.fields.IgnoreFile,
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
ExitCode: tt.fields.ExitCode,
ImageName: tt.fields.ImageName,
Output: tt.fields.Output,
}
err := c.Init()
// tests log messages
var gotMessages []string
for _, entry := range obs.AllUntimed() {
gotMessages = append(gotMessages, entry.Message)
}
assert.Equal(t, tt.logs, gotMessages, tt.name)
// test the error
switch {
case tt.wantErr != "":
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
tt.want.context = ctx
tt.want.logger = logger.Sugar()
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

21
internal/client/inject.go Normal file
View File

@@ -0,0 +1,21 @@
// +build wireinject
package client
import (
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/google/wire"
)
func initializeScanner(ospkgToken ospkg.Token, libToken library.Token, ospkgURL ospkg.RemoteURL, libURL library.RemoteURL) scanner.Scanner {
wire.Build(scanner.ClientSet)
return scanner.Scanner{}
}
func initializeVulnerabilityClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}
}

73
internal/client/run.go Normal file
View File

@@ -0,0 +1,73 @@
package client
import (
"os"
"github.com/urfave/cli"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/internal/client/config"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
)
func Run(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}
return run(c)
}
func run(c config.Config) (err error) {
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
return xerrors.Errorf("failed to initialize a logger: %w", err)
}
// initialize config
if err = c.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
// configure cache dir
utils.SetCacheDir(c.CacheDir)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
scanOptions := types.ScanOptions{
VulnType: c.VulnType,
Timeout: c.Timeout,
RemoteURL: c.RemoteAddr,
Token: c.Token,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
scanner := initializeScanner(ospkg.Token(c.Token), library.Token(c.Token),
ospkg.RemoteURL(c.RemoteAddr), library.RemoteURL(c.RemoteAddr))
results, err := scanner.ScanImage(c.ImageName, c.Input, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}
vulnClient := initializeVulnerabilityClient()
for i := range results {
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
}
if err = report.WriteResults(c.Format, c.Output, results, c.Template, false); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
if c.ExitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(c.ExitCode)
}
}
}
return nil
}

View File

@@ -0,0 +1,35 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package client
import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
library2 "github.com/aquasecurity/trivy/pkg/scanner/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/vulnerability"
)
// Injectors from inject.go:
func initializeScanner(ospkgToken ospkg.Token, libToken library.Token, ospkgURL ospkg.RemoteURL, libURL library.RemoteURL) scanner.Scanner {
osDetector := ospkg.NewProtobufClient(ospkgURL)
detector := ospkg.NewDetector(ospkgToken, osDetector)
ospkgScanner := ospkg2.NewScanner(detector)
libDetector := library.NewProtobufClient(libURL)
libraryDetector := library.NewDetector(libToken, libDetector)
libraryScanner := library2.NewScanner(libraryDetector)
scannerScanner := scanner.NewScanner(ospkgScanner, libraryScanner)
return scannerScanner
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
return client
}

View File

@@ -0,0 +1,13 @@
// +build wireinject
package operation
import (
"github.com/aquasecurity/trivy/pkg/db"
"github.com/google/wire"
)
func initializeDBClient() db.Client {
wire.Build(db.SuperSet)
return db.Client{}
}

View File

@@ -0,0 +1,74 @@
package operation
import (
"context"
"os"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
)
func Reset() (err error) {
log.Logger.Info("Resetting...")
cache := cache.Initialize(utils.CacheDir())
if err = cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
if err = os.RemoveAll(utils.CacheDir()); err != nil {
return xerrors.New("failed to remove cache")
}
return nil
}
func ClearCache() error {
log.Logger.Info("Removing image caches...")
cache := cache.Initialize(utils.CacheDir())
if err := cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
return nil
}
func DownloadDB(appVersion, cacheDir string, light, skipUpdate bool) error {
client := initializeDBClient()
ctx := context.Background()
needsUpdate, err := client.NeedsUpdate(ctx, appVersion, light, skipUpdate)
if err != nil {
return xerrors.Errorf("database error: %w", err)
}
if needsUpdate {
log.Logger.Info("Need to update DB")
if err = db.Close(); err != nil {
return xerrors.Errorf("failed db close: %w", err)
}
if err := client.Download(ctx, cacheDir, light); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
log.Logger.Info("Reopening DB...")
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("failed db close: %w", err)
}
}
// for debug
if err := showDBInfo(); err != nil {
return xerrors.Errorf("failed to show database info")
}
return nil
}
func showDBInfo() error {
metadata, err := db.Config{}.GetMetadata()
if err != nil {
return xerrors.Errorf("something wrong with DB: %w", err)
}
log.Logger.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
return nil
}

View File

@@ -0,0 +1,23 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package operation
import (
db2 "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
"k8s.io/utils/clock"
)
// Injectors from inject.go:
func initializeDBClient() db.Client {
config := db2.Config{}
client := github.NewClient()
realClock := clock.RealClock{}
dbClient := db.NewClient(config, client, realClock)
return dbClient
}

View File

@@ -0,0 +1,55 @@
package config
import (
"github.com/urfave/cli"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/utils"
)
type Config struct {
context *cli.Context
Quiet bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
Listen string
Token string
// these variables are generated by Init()
AppVersion string
}
func New(c *cli.Context) Config {
debug := c.Bool("debug")
quiet := c.Bool("quiet")
return Config{
context: c,
Quiet: quiet,
Debug: debug,
CacheDir: c.String("cache-dir"),
Reset: c.Bool("reset"),
DownloadDBOnly: c.Bool("download-db-only"),
SkipUpdate: c.Bool("skip-update"),
Listen: c.String("listen"),
Token: c.String("token"),
}
}
func (c *Config) Init() (err error) {
if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("The --skip-update and --download-db-only option can not be specified both")
}
c.AppVersion = c.context.App.Version
// A server always suppresses a progress bar
utils.Quiet = true
return nil
}

View File

@@ -0,0 +1,159 @@
package config
import (
"flag"
"os"
"testing"
"time"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
args []string
want Config
}{
{
name: "happy path",
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update"},
want: Config{
Quiet: true,
Reset: true,
SkipUpdate: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &cli.App{}
set := flag.NewFlagSet("test", 0)
set.Bool("quiet", false, "")
set.Bool("no-progress", false, "")
set.Bool("reset", false, "")
set.Bool("skip-update", false, "")
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
tt.want.context = c
got := New(c)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestConfig_Init(t *testing.T) {
type fields struct {
context *cli.Context
Quiet bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
ClearCache bool
Input string
output string
Format string
Template string
Timeout time.Duration
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
ImageName string
VulnType []string
Output *os.File
Severities []dbTypes.Severity
AppVersion string
onlyUpdate string
refresh bool
autoRefresh bool
}
tests := []struct {
name string
fields fields
args []string
logs []string
want Config
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
Quiet: true,
},
args: []string{"alpine:3.10"},
want: Config{
AppVersion: "0.0.0",
Quiet: true,
},
},
{
name: "happy path: reset",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
Reset: true,
},
args: []string{"alpine:3.10"},
want: Config{
AppVersion: "0.0.0",
Reset: true,
},
},
{
name: "sad: skip and download db",
fields: fields{
refresh: true,
SkipUpdate: true,
DownloadDBOnly: true,
},
args: []string{"alpine:3.10"},
wantErr: "The --skip-update and --download-db-only option can not be specified both",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c := &Config{
context: ctx,
Quiet: tt.fields.Quiet,
Debug: tt.fields.Debug,
CacheDir: tt.fields.CacheDir,
Reset: tt.fields.Reset,
DownloadDBOnly: tt.fields.DownloadDBOnly,
SkipUpdate: tt.fields.SkipUpdate,
}
err := c.Init()
// test the error
switch {
case tt.wantErr != "":
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
tt.want.context = ctx
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

51
internal/server/run.go Normal file
View File

@@ -0,0 +1,51 @@
package server
import (
"github.com/aquasecurity/trivy/pkg/rpc/server"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/operation"
"github.com/aquasecurity/trivy/internal/server/config"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
"golang.org/x/xerrors"
)
func Run(ctx *cli.Context) error {
return run(config.New(ctx))
}
func run(c config.Config) (err error) {
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
return xerrors.Errorf("failed to initialize a logger: %w", err)
}
// initialize config
if err = c.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
// configure cache dir
utils.SetCacheDir(c.CacheDir)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
if c.Reset {
return operation.Reset()
}
if err = db.Init(c.CacheDir); err != nil {
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
}
// download the database file
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, false, c.SkipUpdate); err != nil {
return err
}
if c.DownloadDBOnly {
return nil
}
return server.ListenAndServe(c.Listen, c)
}

View File

@@ -0,0 +1,164 @@
package config
import (
"os"
"strings"
"time"
"github.com/genuinetools/reg/registry"
"github.com/urfave/cli"
"go.uber.org/zap"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
)
type Config struct {
context *cli.Context
logger *zap.SugaredLogger
Quiet bool
NoProgress bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
ClearCache bool
Input string
output string
Format string
Template string
Timeout time.Duration
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
// these variables are generated by Init()
ImageName string
VulnType []string
Output *os.File
Severities []dbTypes.Severity
AppVersion string
// deprecated
onlyUpdate string
// deprecated
refresh bool
// deprecated
autoRefresh bool
}
func New(c *cli.Context) (Config, error) {
debug := c.Bool("debug")
quiet := c.Bool("quiet")
logger, err := log.NewLogger(debug, quiet)
if err != nil {
return Config{}, xerrors.New("failed to create a logger")
}
return Config{
context: c,
logger: logger,
Quiet: quiet,
NoProgress: c.Bool("no-progress"),
Debug: debug,
CacheDir: c.String("cache-dir"),
Reset: c.Bool("reset"),
DownloadDBOnly: c.Bool("download-db-only"),
SkipUpdate: c.Bool("skip-update"),
ClearCache: c.Bool("clear-cache"),
Input: c.String("input"),
output: c.String("output"),
Format: c.String("format"),
Template: c.String("template"),
Timeout: c.Duration("timeout"),
vulnType: c.String("vuln-type"),
Light: c.Bool("light"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),
autoRefresh: c.Bool("auto-refresh"),
}, nil
}
func (c *Config) Init() (err error) {
if c.onlyUpdate != "" || c.refresh || c.autoRefresh {
c.logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
}
if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("The --skip-update and --download-db-only option can not be specified both")
}
c.Severities = c.splitSeverity(c.severities)
c.VulnType = strings.Split(c.vulnType, ",")
c.AppVersion = c.context.App.Version
if c.Quiet || c.NoProgress {
utils.Quiet = true
}
// --clear-cache, --download-db-only and --reset don't conduct the scan
if c.ClearCache || c.DownloadDBOnly || c.Reset {
return nil
}
args := c.context.Args()
if c.Input == "" && len(args) == 0 {
c.logger.Error(`trivy requires at least 1 argument or --input option`)
cli.ShowAppHelp(c.context)
return xerrors.New("arguments error")
}
c.Output = os.Stdout
if c.output != "" {
if c.Output, err = os.Create(c.output); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
if c.Input == "" {
c.ImageName = args[0]
}
// Check whether 'latest' tag is used
if c.ImageName != "" {
image, err := registry.ParseImage(c.ImageName)
if err != nil {
return xerrors.Errorf("invalid image: %w", err)
}
if image.Tag == "latest" {
c.logger.Warn("You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed")
}
}
return nil
}
func (c *Config) splitSeverity(severity string) []dbTypes.Severity {
c.logger.Debugf("Severities: %s", severity)
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
c.logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -0,0 +1,288 @@
package config
import (
"flag"
"os"
"testing"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
args []string
want Config
}{
{
name: "happy path",
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update"},
want: Config{
Quiet: true,
NoProgress: true,
Reset: true,
SkipUpdate: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &cli.App{}
set := flag.NewFlagSet("test", 0)
set.Bool("quiet", false, "")
set.Bool("no-progress", false, "")
set.Bool("reset", false, "")
set.Bool("skip-update", false, "")
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
tt.want.context = c
_, err := New(c)
assert.NoError(t, err, tt.name)
})
}
}
func TestConfig_Init(t *testing.T) {
type fields struct {
context *cli.Context
Quiet bool
NoProgress bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
ClearCache bool
Input string
output string
Format string
Template string
Timeout time.Duration
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
ImageName string
VulnType []string
Output *os.File
Severities []dbTypes.Severity
AppVersion string
onlyUpdate string
refresh bool
autoRefresh bool
}
tests := []struct {
name string
fields fields
args []string
logs []string
want Config
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
Quiet: true,
},
args: []string{"alpine:3.10"},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
severities: "CRITICAL",
ImageName: "alpine:3.10",
VulnType: []string{"os"},
vulnType: "os",
Output: os.Stdout,
Quiet: true,
},
},
{
name: "happy path: reset",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
Reset: true,
},
args: []string{"alpine:3.10"},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
severities: "CRITICAL",
VulnType: []string{"os"},
vulnType: "os",
Reset: true,
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
vulnType: "os,library",
},
args: []string{"centos:7"},
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
severities: "CRITICAL,INVALID",
ImageName: "centos:7",
VulnType: []string{"os", "library"},
vulnType: "os,library",
Output: os.Stdout,
},
},
{
name: "deprecated options",
fields: fields{
onlyUpdate: "alpine",
severities: "LOW",
vulnType: "os,library",
},
args: []string{"debian:buster"},
logs: []string{
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
severities: "LOW",
ImageName: "debian:buster",
VulnType: []string{"os", "library"},
vulnType: "os,library",
Output: os.Stdout,
onlyUpdate: "alpine",
},
},
{
name: "with latest tag",
fields: fields{
onlyUpdate: "alpine",
severities: "LOW",
vulnType: "os,library",
},
args: []string{"gcr.io/distroless/base"},
logs: []string{
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
"You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed",
},
want: Config{
AppVersion: "0.0.0",
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
severities: "LOW",
ImageName: "gcr.io/distroless/base",
VulnType: []string{"os", "library"},
vulnType: "os,library",
Output: os.Stdout,
onlyUpdate: "alpine",
},
},
{
name: "sad: skip and download db",
fields: fields{
refresh: true,
SkipUpdate: true,
DownloadDBOnly: true,
},
args: []string{"alpine:3.10"},
logs: []string{
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
},
wantErr: "The --skip-update and --download-db-only option can not be specified both",
},
{
name: "sad: no image name",
fields: fields{
severities: "MEDIUM",
},
logs: []string{
"trivy requires at least 1 argument or --input option",
},
wantErr: "arguments error",
},
{
name: "sad: invalid image name",
fields: fields{
severities: "HIGH",
},
args: []string{`!"#$%&'()`},
wantErr: "invalid image: parsing image",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
core, obs := observer.New(zap.InfoLevel)
logger := zap.New(core)
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c := &Config{
context: ctx,
logger: logger.Sugar(),
Quiet: tt.fields.Quiet,
NoProgress: tt.fields.NoProgress,
Debug: tt.fields.Debug,
CacheDir: tt.fields.CacheDir,
Reset: tt.fields.Reset,
DownloadDBOnly: tt.fields.DownloadDBOnly,
SkipUpdate: tt.fields.SkipUpdate,
ClearCache: tt.fields.ClearCache,
Input: tt.fields.Input,
output: tt.fields.output,
Format: tt.fields.Format,
Template: tt.fields.Template,
Timeout: tt.fields.Timeout,
vulnType: tt.fields.vulnType,
Light: tt.fields.Light,
severities: tt.fields.severities,
IgnoreFile: tt.fields.IgnoreFile,
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
ExitCode: tt.fields.ExitCode,
ImageName: tt.fields.ImageName,
Output: tt.fields.Output,
onlyUpdate: tt.fields.onlyUpdate,
refresh: tt.fields.refresh,
autoRefresh: tt.fields.autoRefresh,
}
err := c.Init()
// tests log messages
var gotMessages []string
for _, entry := range obs.AllUntimed() {
gotMessages = append(gotMessages, entry.Message)
}
assert.Equal(t, tt.logs, gotMessages, tt.name)
// test the error
switch {
case tt.wantErr != "":
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
tt.want.context = ctx
tt.want.logger = logger.Sugar()
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

View File

@@ -0,0 +1,19 @@
// +build wireinject
package standalone
import (
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/google/wire"
)
func initializeScanner() scanner.Scanner {
wire.Build(scanner.StandaloneSet)
return scanner.Scanner{}
}
func initializeVulnerabilityClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}
}

View File

@@ -0,0 +1,92 @@
package standalone
import (
l "log"
"os"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/operation"
"github.com/aquasecurity/trivy/internal/standalone/config"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
"golang.org/x/xerrors"
)
func Run(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}
return run(c)
}
func run(c config.Config) (err error) {
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
l.Fatal(err)
}
// initialize config
if err = c.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
// configure cache dir
utils.SetCacheDir(c.CacheDir)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
if c.Reset {
return operation.Reset()
}
if c.ClearCache {
return operation.ClearCache()
}
if err = db.Init(c.CacheDir); err != nil {
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
}
// download the database file
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, c.Light, c.SkipUpdate); err != nil {
return err
}
if c.DownloadDBOnly {
return nil
}
scanOptions := types.ScanOptions{
VulnType: c.VulnType,
Timeout: c.Timeout,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
scanner := initializeScanner()
results, err := scanner.ScanImage(c.ImageName, c.Input, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}
vulnClient := initializeVulnerabilityClient()
for i := range results {
vulnClient.FillInfo(results[i].Vulnerabilities, c.Light)
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
}
if err = report.WriteResults(c.Format, c.Output, results, c.Template, c.Light); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
if c.ExitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(c.ExitCode)
}
}
}
return nil
}

View File

@@ -0,0 +1,34 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package standalone
import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
library2 "github.com/aquasecurity/trivy/pkg/scanner/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/vulnerability"
)
// Injectors from inject.go:
func initializeScanner() scanner.Scanner {
detector := ospkg.Detector{}
ospkgScanner := ospkg2.NewScanner(detector)
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
libraryScanner := library2.NewScanner(libraryDetector)
scannerScanner := scanner.NewScanner(ospkgScanner, libraryScanner)
return scannerScanner
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
return client
}

View File

@@ -1,164 +0,0 @@
package pkg
import (
"strings"
"time"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
)
func NewApp(version string) *cli.App {
cli.AppHelpTemplate = `NAME:
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
OPTIONS:
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{$option}}{{end}}{{end}}
`
app := cli.NewApp()
app.Name = "trivy"
app.Version = version
app.ArgsUsage = "image_name"
app.Usage = "A simple and comprehensive vulnerability scanner for containers"
app.EnableBashCompletion = true
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "template, t",
Value: "",
Usage: "output template",
EnvVar: "TRIVY_TEMPLATE",
},
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: "format (table, json, template)",
EnvVar: "TRIVY_FORMAT",
},
cli.StringFlag{
Name: "input, i",
Value: "",
Usage: "input file path instead of image name",
EnvVar: "TRIVY_INPUT",
},
cli.StringFlag{
Name: "severity, s",
Value: strings.Join(types.SeverityNames, ","),
Usage: "severities of vulnerabilities to be displayed (comma separated)",
EnvVar: "TRIVY_SEVERITY",
},
cli.StringFlag{
Name: "output, o",
Usage: "output file name",
EnvVar: "TRIVY_OUTPUT",
},
cli.IntFlag{
Name: "exit-code",
Usage: "Exit code when vulnerabilities were found",
Value: 0,
EnvVar: "TRIVY_EXIT_CODE",
},
cli.BoolFlag{
Name: "skip-update",
Usage: "skip db update",
EnvVar: "TRIVY_SKIP_UPDATE",
},
cli.BoolFlag{
Name: "download-db-only",
Usage: "download/update vulnerability database but don't run a scan",
EnvVar: "TRIVY_DOWNLOAD_DB_ONLY",
},
cli.BoolFlag{
Name: "reset",
Usage: "remove all caches and database",
EnvVar: "TRIVY_RESET",
},
cli.BoolFlag{
Name: "clear-cache, c",
Usage: "clear image caches",
EnvVar: "TRIVY_CLEAR_CACHE",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "suppress progress bar and log output",
EnvVar: "TRIVY_QUIET",
},
cli.BoolFlag{
Name: "no-progress",
Usage: "suppress progress bar",
EnvVar: "TRIVY_NO_PROGRESS",
},
cli.BoolFlag{
Name: "ignore-unfixed",
Usage: "display only fixed vulnerabilities",
EnvVar: "TRIVY_IGNORE_UNFIXED",
},
cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",
EnvVar: "TRIVY_DEBUG",
},
cli.StringFlag{
Name: "vuln-type",
Value: "os,library",
Usage: "comma-separated list of vulnerability types (os,library)",
EnvVar: "TRIVY_VULN_TYPE",
},
cli.StringFlag{
Name: "cache-dir",
Value: utils.DefaultCacheDir(),
Usage: "use as cache directory, but image cache is stored in /path/to/cache/fanal",
EnvVar: "TRIVY_CACHE_DIR",
},
cli.StringFlag{
Name: "ignorefile",
Value: vulnerability.DefaultIgnoreFile,
Usage: "specify .trivyignore file",
EnvVar: "TRIVY_IGNOREFILE",
},
cli.DurationFlag{
Name: "timeout",
Value: time.Second * 60,
Usage: "docker timeout",
EnvVar: "TRIVY_TIMEOUT",
},
cli.BoolFlag{
Name: "light",
Usage: "light mode: it's faster, but vulnerability descriptions and references are not displayed",
},
// deprecated options
cli.StringFlag{
Name: "only-update",
Usage: "deprecated",
EnvVar: "TRIVY_ONLY_UPDATE",
},
cli.BoolFlag{
Name: "refresh",
Usage: "deprecated",
EnvVar: "TRIVY_REFRESH",
},
cli.BoolFlag{
Name: "auto-refresh",
Usage: "deprecated",
EnvVar: "TRIVY_AUTO_REFRESH",
},
}
app.Action = Run
return app
}

View File

@@ -5,10 +5,11 @@ import (
"context"
"io"
"os"
"path/filepath"
"k8s.io/utils/clock"
"github.com/google/wire"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
@@ -21,35 +22,42 @@ const (
lightDB = "trivy-light.db.gz"
)
var SuperSet = wire.NewSet(
wire.Struct(new(clock.RealClock)),
wire.Bind(new(clock.Clock), new(clock.RealClock)),
wire.Struct(new(db.Config)),
github.NewClient,
wire.Bind(new(github.Operation), new(github.Client)),
NewClient,
wire.Bind(new(Operation), new(Client)),
)
type Operation interface {
NeedsUpdate(ctx context.Context, cliVersion string, light, skip bool) (bool, error)
Download(ctx context.Context, cacheDir string, light bool) error
}
type dbOperation interface {
GetMetadata() (db.Metadata, error)
}
type GitHubOperation interface {
DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, error)
}
type Client struct {
dbc Operation
dbc dbOperation
githubClient github.Operation
clock clock.Clock
githubClient GitHubOperation
}
func NewClient() Client {
func NewClient(dbc db.Config, githubClient github.Operation, clock clock.Clock) Client {
return Client{
dbc: db.Config{},
clock: clock.RealClock{},
githubClient: github.NewClient(),
dbc: dbc,
githubClient: githubClient,
clock: clock,
}
}
func (c Client) Download(ctx context.Context, cliVersion, cacheDir string, light, skip bool) error {
func (c Client) NeedsUpdate(ctx context.Context, cliVersion string, light, skip bool) (bool, error) {
dbType := db.TypeFull
dbFile := fullDB
message := " Downloading Full DB file..."
if light {
dbFile = lightDB
message = " Downloading Lightweight DB file..."
dbType = db.TypeLight
}
@@ -58,54 +66,48 @@ func (c Client) Download(ctx context.Context, cliVersion, cacheDir string, light
log.Logger.Debug("This is the first run")
if skip {
log.Logger.Error("The first run cannot skip downloading DB")
return xerrors.New("--skip-update cannot be specified on the first run")
return false, xerrors.New("--skip-update cannot be specified on the first run")
}
metadata = db.Metadata{} // suppress a warning
}
if db.SchemaVersion < metadata.Version {
log.Logger.Errorf("Trivy version (%s) is old. Update to the latest version.", cliVersion)
return xerrors.Errorf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
return false, xerrors.Errorf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
metadata.Version, db.SchemaVersion)
}
if skip {
if db.SchemaVersion != metadata.Version {
log.Logger.Error("The local DB is old and needs to be updated")
return xerrors.New("--skip-update cannot be specified with the old DB")
return false, xerrors.New("--skip-update cannot be specified with the old DB")
} else if metadata.Type != dbType {
if dbType == db.TypeFull {
log.Logger.Error("The local DB is a lightweight DB. You have to download a full DB")
} else {
log.Logger.Error("The local DB is a full DB. You have to download a lightweight DB")
}
return xerrors.New("--skip-update cannot be specified with the different schema DB")
return false, xerrors.New("--skip-update cannot be specified with the different schema DB")
}
return nil
return false, nil
}
if db.SchemaVersion == metadata.Version && metadata.Type == dbType &&
c.clock.Now().Before(metadata.NextUpdate) {
log.Logger.Debug("DB update was skipped because DB is the latest")
return nil
return false, nil
}
if err = c.download(ctx, cacheDir, message, dbFile); err != nil {
return xerrors.Errorf("failed to download the DB file: %w", err)
}
log.Logger.Info("Reopening vulnerability DB")
if err = db.Close(); err != nil {
return xerrors.Errorf("unable to close old DB: %w", err)
}
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("unable to open new DB: %w", err)
}
return nil
return true, nil
}
func (c Client) download(ctx context.Context, cacheDir, message, dbFile string) error {
func (c Client) Download(ctx context.Context, cacheDir string, light bool) error {
dbFile := fullDB
message := " Downloading Full DB file..."
if light {
dbFile = lightDB
message = " Downloading Lightweight DB file..."
}
spinner := utils.NewSpinner(message)
spinner.Start()
defer spinner.Stop()
@@ -122,6 +124,11 @@ func (c Client) download(ctx context.Context, cacheDir, message, dbFile string)
}
dbPath := db.Path(cacheDir)
dbDir := filepath.Dir(dbPath)
if err = os.MkdirAll(dbDir, 0700); err != nil {
return xerrors.Errorf("failed to mkdir: %w", err)
}
file, err := os.Create(dbPath)
if err != nil {
return xerrors.Errorf("unable to open DB file: %w", err)

22
pkg/db/db_mock.go Normal file
View File

@@ -0,0 +1,22 @@
package db
import (
"context"
"github.com/stretchr/testify/mock"
)
type MockClient struct {
mock.Mock
}
func (_m *MockClient) NeedsUpdate(a context.Context, b string, c, d bool) (bool, error) {
ret := _m.Called(a, b, c, d)
return ret.Bool(0), ret.Error(1)
}
func (_m *MockClient) Download(a context.Context, b string, c bool) error {
ret := _m.Called(a, b, c)
return ret.Error(0)
}

View File

@@ -17,6 +17,7 @@ import (
clocktesting "k8s.io/utils/clock/testing"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -39,47 +40,20 @@ func (_m *MockConfig) GetMetadata() (db.Metadata, error) {
return metadata, ret.Error(1)
}
type MockGitHubClient struct {
mock.Mock
}
func (_m *MockGitHubClient) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, error) {
ret := _m.Called(ctx, fileName)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
rc, ok := ret0.(io.ReadCloser)
if !ok {
return nil, ret.Error(1)
}
return rc, ret.Error(1)
}
func TestClient_Download(t *testing.T) {
func TestClient_NeedsUpdate(t *testing.T) {
type getMetadataOutput struct {
metadata db.Metadata
err error
}
type downloadDBOutput struct {
fileName string
err error
}
type downloadDB struct {
input string
output downloadDBOutput
}
testCases := []struct {
name string
light bool
skip bool
clock clock.Clock
getMetadata getMetadataOutput
downloadDB []downloadDB
expectedContent []byte
expectedError error
name string
light bool
skip bool
clock clock.Clock
getMetadata getMetadataOutput
expected bool
expectedError error
}{
{
name: "happy path",
@@ -92,14 +66,7 @@ func TestClient_Download(t *testing.T) {
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
fileName: "testdata/test.db.gz",
},
},
},
expected: true,
},
{
name: "happy path for first run",
@@ -109,14 +76,7 @@ func TestClient_Download(t *testing.T) {
metadata: db.Metadata{},
err: errors.New("get metadata failed"),
},
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
fileName: "testdata/test.db.gz",
},
},
},
expected: true,
},
{
name: "happy path with different type",
@@ -129,14 +89,7 @@ func TestClient_Download(t *testing.T) {
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
downloadDB: []downloadDB{
{
input: lightDB,
output: downloadDBOutput{
fileName: "testdata/test.db.gz",
},
},
},
expected: true,
},
{
name: "happy path with old schema version",
@@ -149,14 +102,7 @@ func TestClient_Download(t *testing.T) {
NextUpdate: time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
downloadDB: []downloadDB{
{
input: lightDB,
output: downloadDBOutput{
fileName: "testdata/test.db.gz",
},
},
},
expected: true,
},
{
name: "happy path with --skip-update",
@@ -169,7 +115,8 @@ func TestClient_Download(t *testing.T) {
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
skip: true,
skip: true,
expected: false,
},
{
name: "skip downloading DB",
@@ -182,6 +129,7 @@ func TestClient_Download(t *testing.T) {
NextUpdate: time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC),
},
},
expected: false,
},
{
name: "newer schema version",
@@ -196,48 +144,6 @@ func TestClient_Download(t *testing.T) {
},
expectedError: xerrors.New("the version of DB schema doesn't match. Local DB: 2, Expected: 1"),
},
{
name: "DownloadDB returns an error",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
getMetadata: getMetadataOutput{
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
err: xerrors.New("download failed"),
},
},
},
expectedError: xerrors.New("failed to download the DB file: failed to download vulnerability DB: download failed"),
},
{
name: "invalid gzip",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
getMetadata: getMetadataOutput{
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC),
},
},
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
fileName: "testdata/invalid.db.gz",
},
},
},
expectedError: xerrors.New("unable to open new DB: failed to open db: invalid database"),
},
{
name: "--skip-update on the first run",
light: false,
@@ -274,7 +180,96 @@ func TestClient_Download(t *testing.T) {
mockConfig.On("GetMetadata").Return(
tc.getMetadata.metadata, tc.getMetadata.err)
mockGitHubConfig := new(MockGitHubClient)
dir, err := ioutil.TempDir("", "db")
require.NoError(t, err, tc.name)
defer os.RemoveAll(dir)
err = db.Init(dir)
require.NoError(t, err, tc.name)
client := Client{
dbc: mockConfig,
clock: tc.clock,
}
needsUpdate, err := client.NeedsUpdate(context.Background(), "test", tc.light, tc.skip)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
default:
assert.NoError(t, err, tc.name)
}
assert.Equal(t, tc.expected, needsUpdate)
mockConfig.AssertExpectations(t)
})
}
}
func TestClient_Download(t *testing.T) {
type downloadDBOutput struct {
fileName string
err error
}
type downloadDB struct {
input string
output downloadDBOutput
}
testCases := []struct {
name string
light bool
downloadDB []downloadDB
expectedContent []byte
expectedError error
}{
{
name: "happy path",
light: false,
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
fileName: "testdata/test.db.gz",
},
},
},
},
{
name: "DownloadDB returns an error",
light: false,
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
err: xerrors.New("download failed"),
},
},
},
expectedError: xerrors.New("failed to download vulnerability DB: download failed"),
},
{
name: "invalid gzip",
light: false,
downloadDB: []downloadDB{
{
input: fullDB,
output: downloadDBOutput{
fileName: "testdata/invalid.db.gz",
},
},
},
expectedError: xerrors.New("invalid gzip file: unexpected EOF"),
},
}
err := log.InitLogger(false, true)
require.NoError(t, err, "failed to init logger")
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockGitHubClient := new(github.MockClient)
for _, dd := range tc.downloadDB {
var rc io.ReadCloser
if dd.output.fileName != "" {
@@ -283,7 +278,7 @@ func TestClient_Download(t *testing.T) {
rc = f
}
mockGitHubConfig.On("DownloadDB", mock.Anything, dd.input).Return(
mockGitHubClient.On("DownloadDB", mock.Anything, dd.input).Return(
rc, dd.output.err,
)
}
@@ -295,14 +290,9 @@ func TestClient_Download(t *testing.T) {
err = db.Init(dir)
require.NoError(t, err, tc.name)
client := Client{
dbc: mockConfig,
clock: tc.clock,
githubClient: mockGitHubConfig,
}
client := NewClient(db.Config{}, mockGitHubClient, nil)
ctx := context.Background()
err = client.Download(ctx, "test", dir, tc.light, tc.skip)
err = client.Download(ctx, dir, tc.light)
switch {
case tc.expectedError != nil:
@@ -311,8 +301,7 @@ func TestClient_Download(t *testing.T) {
assert.NoError(t, err, tc.name)
}
mockConfig.AssertExpectations(t)
mockGitHubConfig.AssertExpectations(t)
mockGitHubClient.AssertExpectations(t)
})
}
}

Binary file not shown.

View File

@@ -0,0 +1,71 @@
package library
import (
"path/filepath"
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/knqyf263/go-version"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/types"
)
var SuperSet = wire.NewSet(
wire.Struct(new(DriverFactory)),
wire.Bind(new(Factory), new(DriverFactory)),
NewDetector,
wire.Bind(new(Operation), new(Detector)),
)
type Operation interface {
Detect(string, []ptypes.Library) ([]types.DetectedVulnerability, error)
}
type Detector struct {
driverFactory Factory
}
func NewDetector(factory Factory) Detector {
return Detector{driverFactory: factory}
}
func (d Detector) Detect(filePath string, pkgs []ptypes.Library) ([]types.DetectedVulnerability, error) {
log.Logger.Debugf("Detecting library vulnerabilities, path: %s", filePath)
driver := d.driverFactory.NewDriver(filepath.Base(filePath))
if driver == nil {
return nil, xerrors.New("unknown file type")
}
vulns, err := detect(driver, pkgs)
if err != nil {
return nil, xerrors.Errorf("failed to scan %s vulnerabilities: %w", driver.Type(), err)
}
return vulns, nil
}
func detect(driver Driver, libs []ptypes.Library) ([]types.DetectedVulnerability, error) {
log.Logger.Infof("Detecting %s vulnerabilities...", driver.Type())
var vulnerabilities []types.DetectedVulnerability
for _, lib := range libs {
v, err := version.NewVersion(lib.Version)
if err != nil {
log.Logger.Debugf("invalid version, library: %s, version: %s, error: %s\n",
lib.Name, lib.Version, err)
continue
}
vulns, err := driver.Detect(lib.Name, v)
if err != nil {
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", driver.Type(), err)
}
vulnerabilities = append(vulnerabilities, vulns...)
}
return vulnerabilities, nil
}

View File

@@ -0,0 +1,46 @@
package library
import (
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/mock"
)
type MockDetector struct {
mock.Mock
}
type DetectInput struct {
FilePath string
Libs []ptypes.Library
}
type DetectOutput struct {
Vulns []types.DetectedVulnerability
Err error
}
type DetectExpectation struct {
Args DetectInput
ReturnArgs DetectOutput
}
func NewMockDetector(detectExpectations []DetectExpectation) *MockDetector {
mockDetector := new(MockDetector)
for _, e := range detectExpectations {
mockDetector.On("Detect", e.Args.FilePath, e.Args.Libs).Return(
e.ReturnArgs.Vulns, e.ReturnArgs.Err)
}
return mockDetector
}
func (_m *MockDetector) Detect(a string, b []ptypes.Library) ([]types.DetectedVulnerability, error) {
ret := _m.Called(a, b)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
vulns, ok := ret0.([]types.DetectedVulnerability)
if !ok {
return nil, ret.Error(1)
}
return vulns, ret.Error(1)
}

View File

@@ -0,0 +1,50 @@
package library
import (
"os"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
"github.com/aquasecurity/trivy/pkg/detector/library/cargo"
"github.com/aquasecurity/trivy/pkg/detector/library/composer"
"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"
)
type Driver interface {
ParseLockfile(*os.File) ([]ptypes.Library, error)
Detect(string, *version.Version) ([]types.DetectedVulnerability, error)
Type() string
}
type Factory interface {
NewDriver(filename string) Driver
}
type DriverFactory struct{}
func (d DriverFactory) NewDriver(filename string) Driver {
// TODO: use DI
var scanner Driver
switch filename {
case "Gemfile.lock":
scanner = bundler.NewScanner()
case "Cargo.lock":
scanner = cargo.NewScanner()
case "composer.lock":
scanner = composer.NewScanner()
case "package-lock.json":
scanner = node.NewScanner(node.ScannerTypeNpm)
case "yarn.lock":
scanner = node.NewScanner(node.ScannerTypeYarn)
case "Pipfile.lock":
scanner = python.NewScanner(python.ScannerTypePipenv)
case "poetry.lock":
scanner = python.NewScanner(python.ScannerTypePoetry)
default:
return nil
}
return scanner
}

View File

@@ -4,7 +4,7 @@ import (
"strings"
"time"
version "github.com/knqyf263/go-rpm-version"
version "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
@@ -65,10 +65,19 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Detecte
}
installed := utils.FormatVersion(pkg)
installedVersion := version.NewVersion(installed)
installedVersion, err := version.NewVersion(installed)
if err != nil {
log.Logger.Debugf("failed to parse Alpine Linux installed package version: %s", err)
continue
}
for _, adv := range advisories {
fixedVersion := version.NewVersion(adv.FixedVersion)
f := strings.Replace(adv.FixedVersion, "_rc", "~rc", 1)
fixedVersion, err := version.NewVersion(f)
if err != nil {
log.Logger.Debugf("failed to parse Alpine Linux fixed version: %s", err)
continue
}
if installedVersion.LessThan(fixedVersion) {
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,

View File

@@ -0,0 +1,247 @@
package alpine
import (
"errors"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
}
func TestScanner_Detect(t *testing.T) {
type args struct {
osVer string
pkgs []analyzer.Package
}
type getInput struct {
osVer string
pkgName string
}
type getOutput struct {
advisories []dbTypes.Advisory
err error
}
type get struct {
input getInput
output getOutput
}
type mocks struct {
get []get
}
tests := []struct {
name string
args args
mocks mocks
want []types.DetectedVulnerability
wantErr string
}{
{
name: "happy path",
args: args{
osVer: "3.10.2",
pkgs: []analyzer.Package{
{
Name: "ansible",
Version: "2.6.4",
},
{
Name: "invalid",
Version: "invalid", // skipped
},
},
},
mocks: mocks{
get: []get{
{
input: getInput{
osVer: "3.10",
pkgName: "ansible",
},
output: getOutput{
advisories: []dbTypes.Advisory{
{
VulnerabilityID: "CVE-2018-10875",
FixedVersion: "2.6.3-r0",
},
{
VulnerabilityID: "CVE-2019-10217",
FixedVersion: "2.8.4-r0",
},
{
VulnerabilityID: "CVE-2019-INVALID",
FixedVersion: "invalid", // skipped
},
},
},
},
{
input: getInput{
osVer: "3.10",
pkgName: "invalid",
},
output: getOutput{advisories: []dbTypes.Advisory{{}}},
},
},
},
want: []types.DetectedVulnerability{
{
PkgName: "ansible",
VulnerabilityID: "CVE-2019-10217",
InstalledVersion: "2.6.4",
FixedVersion: "2.8.4-r0",
},
},
},
{
name: "contain rc",
args: args{
osVer: "3.9",
pkgs: []analyzer.Package{
{
Name: "jq",
Version: "1.6-r0",
},
},
},
mocks: mocks{
get: []get{
{
input: getInput{
osVer: "3.9",
pkgName: "jq",
},
output: getOutput{
advisories: []dbTypes.Advisory{
{
VulnerabilityID: "CVE-2016-4074",
FixedVersion: "1.6_rc1-r0",
},
{
VulnerabilityID: "CVE-2019-9999",
FixedVersion: "1.6_rc2",
},
},
},
},
{
input: getInput{
osVer: "3.10",
pkgName: "invalid",
},
output: getOutput{advisories: []dbTypes.Advisory{{}}},
},
},
},
},
{
name: "Get returns an error",
args: args{
osVer: "3.8.1",
pkgs: []analyzer.Package{
{
Name: "jq",
Version: "1.6-r0",
},
},
},
mocks: mocks{
get: []get{
{
input: getInput{
osVer: "3.8",
pkgName: "jq",
},
output: getOutput{err: errors.New("error")},
},
},
},
wantErr: "failed to get alpine advisories",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockVulnSrc := new(dbTypes.MockVulnSrc)
for _, g := range tt.mocks.get {
mockVulnSrc.On("Get", g.input.osVer, g.input.pkgName).Return(
g.output.advisories, g.output.err)
}
s := &Scanner{
vs: mockVulnSrc,
}
got, err := s.Detect(tt.args.osVer, tt.args.pkgs)
switch {
case tt.wantErr != "":
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
default:
assert.NoError(t, err, tt.name)
}
assert.ElementsMatch(t, got, tt.want, tt.name)
})
}
}
func TestScanner_IsSupportedVersion(t *testing.T) {
vectors := map[string]struct {
now time.Time
osFamily string
osVersion string
expected bool
}{
"alpine3.6": {
now: time.Date(2019, 3, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.6",
expected: true,
},
"alpine3.6 with EOL": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.6.5",
expected: false,
},
"alpine3.9": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.9.0",
expected: true,
},
"alpine3.10": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.10",
expected: true,
},
"unknown": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "unknown",
expected: false,
},
}
for testName, v := range vectors {
s := NewScanner()
t.Run(testName, func(t *testing.T) {
actual := s.isSupportedVersion(v.now, v.osFamily, v.osVersion)
if actual != v.expected {
t.Errorf("[%s] got %v, want %v", testName, actual, v.expected)
}
})
}
}

View File

@@ -0,0 +1,76 @@
package ospkg
import (
"github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/amazon"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/oracle"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/redhat"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
fos "github.com/aquasecurity/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/types"
)
var (
ErrUnsupportedOS = xerrors.New("unsupported os")
SuperSet = wire.NewSet(
wire.Struct(new(Detector)),
wire.Bind(new(Operation), new(Detector)),
)
)
type Operation interface {
Detect(string, string, []analyzer.Package) ([]types.DetectedVulnerability, bool, error)
}
type Driver interface {
Detect(string, []analyzer.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(string, string) bool
}
type Detector struct{}
func (d Detector) Detect(osFamily, osName string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
driver := newDriver(osFamily, osName)
if driver == nil {
return nil, false, ErrUnsupportedOS
}
eosl := !driver.IsSupportedVersion(osFamily, osName)
vulns, err := driver.Detect(osName, pkgs)
if err != nil {
return nil, false, xerrors.Errorf("failed detection: %w", err)
}
return vulns, eosl, nil
}
func newDriver(osFamily, osName string) Driver {
// TODO: use DI and change struct names
var d Driver
switch osFamily {
case fos.Alpine:
d = alpine.NewScanner()
case fos.Debian:
d = debian.NewScanner()
case fos.Ubuntu:
d = ubuntu.NewScanner()
case fos.RedHat, fos.CentOS:
d = redhat.NewScanner()
case fos.Amazon:
d = amazon.NewScanner()
case fos.Oracle:
d = oracle.NewScanner()
default:
log.Logger.Warnf("unsupported os : %s", osFamily)
return nil
}
return d
}

View File

@@ -0,0 +1,48 @@
package ospkg
import (
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/mock"
)
type MockDetector struct {
mock.Mock
}
type DetectInput struct {
OSFamily string
OSName string
Pkgs []analyzer.Package
}
type DetectOutput struct {
Vulns []types.DetectedVulnerability
Eosl bool
Err error
}
type DetectExpectation struct {
Args DetectInput
ReturnArgs DetectOutput
}
func NewMockDetector(detectExpectations []DetectExpectation) *MockDetector {
mockDetector := new(MockDetector)
for _, e := range detectExpectations {
mockDetector.On("Detect", e.Args.OSFamily, e.Args.OSName, e.Args.Pkgs).Return(
e.ReturnArgs.Vulns, e.ReturnArgs.Eosl, e.ReturnArgs.Err)
}
return mockDetector
}
func (_m *MockDetector) Detect(a, b string, c []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
ret := _m.Called(a, b, c)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, false, ret.Error(2)
}
vulns, ok := ret0.([]types.DetectedVulnerability)
if !ok {
return nil, false, ret.Error(2)
}
return vulns, ret.Bool(1), ret.Error(2)
}

View File

@@ -0,0 +1,94 @@
package oracle
import (
"strings"
"time"
oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
"k8s.io/utils/clock"
)
var (
eolDates = map[string]time.Time{
// Source:
// https://www.oracle.com/a/ocom/docs/elsp-lifetime-069338.pdf
// https://community.oracle.com/docs/DOC-917964
"3": time.Date(2011, 12, 31, 23, 59, 59, 0, time.UTC),
"4": time.Date(2013, 12, 31, 23, 59, 59, 0, time.UTC),
"5": time.Date(2017, 12, 31, 23, 59, 59, 0, time.UTC),
"6": time.Date(2021, 3, 21, 23, 59, 59, 0, time.UTC),
"7": time.Date(2024, 7, 23, 23, 59, 59, 0, time.UTC),
"8": time.Date(2029, 7, 18, 23, 59, 59, 0, time.UTC),
}
)
type Scanner struct {
vs dbTypes.VulnSrc
clock clock.Clock
}
func NewScanner() *Scanner {
return &Scanner{
vs: oracleoval.NewVulnSrc(),
clock: clock.RealClock{},
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Oracle Linux vulnerabilities...")
if strings.Count(osVer, ".") > 0 {
osVer = osVer[:strings.Index(osVer, ".")]
}
log.Logger.Debugf("Oracle Linux: os version: %s", osVer)
log.Logger.Debugf("Oracle Linux: the number of packages: %d", len(pkgs))
var vulns []types.DetectedVulnerability
for _, pkg := range pkgs {
advisories, err := s.vs.Get(osVer, pkg.SrcName)
if err != nil {
return nil, xerrors.Errorf("failed to get Oracle Linux advisory: %w", err)
}
installed := utils.FormatVersion(pkg)
installedVersion := version.NewVersion(installed)
for _, adv := range advisories {
fixedVersion := version.NewVersion(adv.FixedVersion)
vuln := types.DetectedVulnerability{
VulnerabilityID: adv.VulnerabilityID,
PkgName: pkg.Name,
InstalledVersion: installed,
}
if installedVersion.LessThan(fixedVersion) {
vuln.FixedVersion = adv.FixedVersion
vulns = append(vulns, vuln)
}
}
}
return vulns, nil
}
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
if strings.Count(osVer, ".") > 0 {
osVer = osVer[:strings.Index(osVer, ".")]
}
eol, ok := eolDates[osVer]
if !ok {
log.Logger.Warnf("This OS version is not on the EOL list: %s %s", osFamily, osVer)
return false
}
return s.clock.Now().Before(eol)
}

View File

@@ -0,0 +1,96 @@
package oracle
import (
"os"
"testing"
"time"
oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
"github.com/aquasecurity/trivy/pkg/log"
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
}
func TestScanner_IsSupportedVersion(t *testing.T) {
vectors := map[string]struct {
clock clock.Clock
osFamily string
osVersion string
expected bool
}{
"oracle3": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "3",
expected: false,
},
"oracle4": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "4",
expected: false,
},
"oracle5": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "5",
expected: false,
},
"oracle6": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "6",
expected: true,
},
"oracle7": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "7",
expected: true,
},
"oracle7.6": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "7.6",
expected: true,
},
"oracle8": {
clock: clocktesting.NewFakeClock(time.Date(2029, 7, 18, 23, 59, 58, 59, time.UTC)),
osFamily: "oracle",
osVersion: "8",
expected: true,
},
"oracle8-same-time": {
clock: clocktesting.NewFakeClock(time.Date(2029, 7, 18, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "8",
expected: false,
},
"unknown": {
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
osFamily: "oracle",
osVersion: "unknown",
expected: false,
},
}
for testName, v := range vectors {
s := &Scanner{
vs: oracleoval.NewVulnSrc(),
clock: v.clock,
}
t.Run(testName, func(t *testing.T) {
actual := s.IsSupportedVersion(v.osFamily, v.osVersion)
if actual != v.expected {
t.Errorf("[%s] got %v, want %v", testName, actual, v.expected)
}
})
}
}

View File

@@ -5,12 +5,14 @@ import (
"fmt"
"io"
"net/http"
"os"
"sort"
"strings"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/google/go-github/v28/github"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
)
@@ -39,12 +41,24 @@ func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser,
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id)
}
type Operation interface {
DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, error)
}
type Client struct {
Repository RepositoryInterface
}
func NewClient() Client {
gc := github.NewClient(nil)
var client *http.Client
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken != "" {
log.Logger.Info("Use your github token")
ctx := context.Background()
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
client = oauth2.NewClient(ctx, ts)
}
gc := github.NewClient(client)
repo := Repository{
repository: gc.Repositories,

25
pkg/github/github_mock.go Normal file
View File

@@ -0,0 +1,25 @@
package github
import (
"context"
"io"
"github.com/stretchr/testify/mock"
)
type MockClient struct {
mock.Mock
}
func (_m *MockClient) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, error) {
ret := _m.Called(ctx, fileName)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
rc, ok := ret0.(io.ReadCloser)
if !ok {
return nil, ret.Error(1)
}
return rc, ret.Error(1)
}

View File

@@ -15,7 +15,7 @@ var (
func InitLogger(debug, disable bool) (err error) {
debugOption = debug
Logger, err = newLogger(debug, disable)
Logger, err = NewLogger(debug, disable)
if err != nil {
return xerrors.Errorf("error in new logger: %w", err)
}
@@ -23,7 +23,7 @@ func InitLogger(debug, disable bool) (err error) {
}
func newLogger(debug, disable bool) (*zap.SugaredLogger, error) {
func NewLogger(debug, disable bool) (*zap.SugaredLogger, error) {
level := zap.NewAtomicLevel()
if debug {
level.SetLevel(zapcore.DebugLevel)
@@ -56,6 +56,7 @@ func newLogger(debug, disable bool) (*zap.SugaredLogger, error) {
myConfig.OutputPaths = []string{os.DevNull}
myConfig.ErrorOutputPaths = []string{os.DevNull}
}
logger, err := myConfig.Build()
if err != nil {
return nil, xerrors.Errorf("failed to build zap config: %w", err)

View File

@@ -0,0 +1,53 @@
package library
import (
"context"
"net/http"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/google/wire"
"golang.org/x/xerrors"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
detector "github.com/aquasecurity/trivy/pkg/detector/library"
r "github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/types"
rpc "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
NewProtobufClient,
NewDetector,
wire.Bind(new(detector.Operation), new(Detector)),
)
type RemoteURL string
func NewProtobufClient(remoteURL RemoteURL) rpc.LibDetector {
return rpc.NewLibDetectorProtobufClient(string(remoteURL), &http.Client{})
}
type Token string
type Detector struct {
token Token
client rpc.LibDetector
}
func NewDetector(token Token, detector rpc.LibDetector) Detector {
return Detector{token: token, client: detector}
}
func (d Detector) Detect(filePath string, libs []ptypes.Library) ([]types.DetectedVulnerability, error) {
ctx := client.WithToken(context.Background(), string(d.token))
res, err := d.client.Detect(ctx, &rpc.LibDetectRequest{
FilePath: filePath,
Libraries: r.ConvertToRpcLibraries(libs),
})
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcVulns(res.Vulnerabilities), nil
}

View File

@@ -0,0 +1,157 @@
package library
import (
"context"
"testing"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/stretchr/testify/mock"
)
type mockDetector struct {
mock.Mock
}
func (_m *mockDetector) Detect(a context.Context, b *detector.LibDetectRequest) (*detector.DetectResponse, error) {
ret := _m.Called(a, b)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
res, ok := ret0.(*detector.DetectResponse)
if !ok {
return nil, ret.Error(1)
}
return res, ret.Error(1)
}
func TestDetectClient_Detect(t *testing.T) {
type detectInput struct {
req *detector.LibDetectRequest
}
type detectOutput struct {
res *detector.DetectResponse
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
token Token
}
type args struct {
filePath string
libs []ptypes.Library
}
tests := []struct {
name string
fields fields
args args
detect detect
want []types.DetectedVulnerability
wantErr string
}{
{
name: "happy path",
fields: fields{
token: "token",
},
args: args{
filePath: "app/Pipfile.lock",
libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
detect: detect{
input: detectInput{req: &detector.LibDetectRequest{
FilePath: "app/Pipfile.lock",
Libraries: []*detector.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
output: detectOutput{
res: &detector.DetectResponse{
Vulnerabilities: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "django",
InstalledVersion: "3.0.0",
FixedVersion: "3.0.1",
Title: "RCE",
Description: "Remote Code Execution",
Severity: detector.Severity_CRITICAL,
},
},
},
},
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "django",
InstalledVersion: "3.0.0",
FixedVersion: "3.0.1",
Vulnerability: dbTypes.Vulnerability{
Title: "RCE",
Description: "Remote Code Execution",
Severity: "CRITICAL",
},
},
},
},
{
name: "Detect returns an error",
fields: fields{},
args: args{
filePath: "app/Pipfile.lock",
libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
detect: detect{
input: detectInput{req: &detector.LibDetectRequest{
FilePath: "app/Pipfile.lock",
Libraries: []*detector.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities via RPC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(mockDetector)
mockDetector.On("Detect", mock.Anything, tt.detect.input.req).Return(
tt.detect.output.res, tt.detect.output.err)
d := NewDetector(tt.fields.token, mockDetector)
got, err := d.Detect(tt.args.filePath, tt.args.libs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

View File

@@ -0,0 +1,54 @@
package ospkg
import (
"context"
"net/http"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
r "github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/types"
rpc "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
NewProtobufClient,
NewDetector,
wire.Bind(new(detector.Operation), new(Detector)),
)
type RemoteURL string
func NewProtobufClient(remoteURL RemoteURL) rpc.OSDetector {
return rpc.NewOSDetectorProtobufClient(string(remoteURL), &http.Client{})
}
type Token string
type Detector struct {
token Token
client rpc.OSDetector
}
func NewDetector(token Token, detector rpc.OSDetector) Detector {
return Detector{token: token, client: detector}
}
func (d Detector) Detect(osFamily, osName string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
ctx := client.WithToken(context.Background(), string(d.token))
res, err := d.client.Detect(ctx, &rpc.OSDetectRequest{
OsFamily: osFamily,
OsName: osName,
Packages: r.ConvertToRpcPkgs(pkgs),
})
if err != nil {
return nil, false, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcVulns(res.Vulnerabilities), res.Eosl, nil
}

View File

@@ -0,0 +1,184 @@
package ospkg
import (
"context"
"testing"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/stretchr/testify/mock"
)
type mockDetector struct {
mock.Mock
}
func (_m *mockDetector) Detect(a context.Context, b *detector.OSDetectRequest) (*detector.DetectResponse, error) {
ret := _m.Called(a, b)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
res, ok := ret0.(*detector.DetectResponse)
if !ok {
return nil, ret.Error(1)
}
return res, ret.Error(1)
}
func TestDetectClient_Detect(t *testing.T) {
type detectInput struct {
req *detector.OSDetectRequest
}
type detectOutput struct {
res *detector.DetectResponse
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
token Token
}
type args struct {
osFamily string
osName string
pkgs []analyzer.Package
}
tests := []struct {
name string
fields fields
args args
detect detect
want []types.DetectedVulnerability
wantErr string
}{
{
name: "happy path",
fields: fields{
token: "token",
},
args: args{
osFamily: "alpine",
osName: "3.10.2",
pkgs: []analyzer.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
detect: detect{
input: detectInput{
req: &detector.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*detector.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
},
output: detectOutput{
res: &detector.DetectResponse{
Vulnerabilities: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "bash",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "RCE",
Description: "Remote Code Execution",
Severity: detector.Severity_HIGH,
},
},
},
},
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "bash",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "RCE",
Description: "Remote Code Execution",
Severity: "HIGH",
},
},
},
},
{
name: "Detect returns an error",
fields: fields{},
args: args{
osFamily: "alpine",
osName: "3.10.2",
pkgs: []analyzer.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
detect: detect{
input: detectInput{
req: &detector.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*detector.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities via RPC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(mockDetector)
mockDetector.On("Detect", mock.Anything, tt.detect.input.req).Return(
tt.detect.output.res, tt.detect.output.err)
d := NewDetector(tt.fields.token, mockDetector)
got, _, err := d.Detect(tt.args.osFamily, tt.args.osName, tt.args.pkgs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

35
pkg/rpc/client/token.go Normal file
View File

@@ -0,0 +1,35 @@
package client
import (
"context"
"net/http"
"github.com/twitchtv/twirp"
"github.com/aquasecurity/trivy/pkg/log"
)
var (
buildRequestHeaderFunc = buildRequestHeader
)
func buildRequestHeader(inputHeaders map[string]string) http.Header {
header := make(http.Header)
for k, v := range inputHeaders {
header.Set(k, v)
}
return header
}
func WithToken(ctx context.Context, token string) context.Context {
// Prepare custom header
header := buildRequestHeaderFunc(map[string]string{"Trivy-Token": token})
// Attach the headers to a context
ctxWithToken, err := twirp.WithHTTPRequestHeaders(ctx, header)
if err != nil {
log.Logger.Warnf("twirp error setting headers: %s", err)
return ctx
}
return ctxWithToken
}

View File

@@ -0,0 +1,72 @@
package client
import (
"context"
"net/http"
"os"
"testing"
"github.com/twitchtv/twirp"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
_ = log.InitLogger(false, true)
os.Exit(m.Run())
}
func TestWithToken(t *testing.T) {
type args struct {
ctx context.Context
token string
}
tests := []struct {
name string
args args
buildRequestHeaderFunc func(map[string]string) http.Header
want http.Header
}{
{
name: "happy path",
args: args{
ctx: context.Background(),
token: "token",
},
want: http.Header{
"Trivy-Token": []string{"token"},
},
buildRequestHeaderFunc: buildRequestHeader,
},
{
name: "sad path, invalid headers passed in",
args: args{
ctx: context.Background(),
token: "token",
},
want: http.Header(nil),
buildRequestHeaderFunc: func(m map[string]string) http.Header {
header := make(http.Header)
for k, v := range m {
header.Set(k, v)
}
// add an extra header that is reserved for twirp
header.Set("Content-Type", "foobar")
return header
},
},
}
for _, tt := range tests {
oldbuildRequestHeaderFunc := buildRequestHeaderFunc
defer func() {
buildRequestHeaderFunc = oldbuildRequestHeaderFunc
}()
buildRequestHeaderFunc = tt.buildRequestHeaderFunc
gotCtx := WithToken(tt.args.ctx, tt.args.token)
header, _ := twirp.HTTPRequestHeaders(gotCtx)
assert.Equal(t, tt.want, header, tt.name)
}
}

110
pkg/rpc/convert.go Normal file
View File

@@ -0,0 +1,110 @@
package rpc
import (
"github.com/aquasecurity/fanal/analyzer"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
)
func ConvertToRpcPkgs(pkgs []analyzer.Package) []*detector.Package {
var rpcPkgs []*detector.Package
for _, pkg := range pkgs {
rpcPkgs = append(rpcPkgs, &detector.Package{
Name: pkg.Name,
Version: pkg.Version,
Release: pkg.Release,
Epoch: int32(pkg.Epoch),
Arch: pkg.Arch,
SrcName: pkg.SrcName,
SrcVersion: pkg.SrcVersion,
SrcRelease: pkg.SrcRelease,
SrcEpoch: int32(pkg.SrcEpoch),
})
}
return rpcPkgs
}
func ConvertFromRpcPkgs(rpcPkgs []*detector.Package) []analyzer.Package {
var pkgs []analyzer.Package
for _, pkg := range rpcPkgs {
pkgs = append(pkgs, analyzer.Package{
Name: pkg.Name,
Version: pkg.Version,
Release: pkg.Release,
Epoch: int(pkg.Epoch),
Arch: pkg.Arch,
SrcName: pkg.SrcName,
SrcVersion: pkg.SrcVersion,
SrcRelease: pkg.SrcRelease,
SrcEpoch: int(pkg.SrcEpoch),
})
}
return pkgs
}
func ConvertFromRpcLibraries(rpcLibs []*detector.Library) []ptypes.Library {
var libs []ptypes.Library
for _, l := range rpcLibs {
libs = append(libs, ptypes.Library{
Name: l.Name,
Version: l.Version,
})
}
return libs
}
func ConvertToRpcLibraries(libs []ptypes.Library) []*detector.Library {
var rpcLibs []*detector.Library
for _, l := range libs {
rpcLibs = append(rpcLibs, &detector.Library{
Name: l.Name,
Version: l.Version,
})
}
return rpcLibs
}
func ConvertFromRpcVulns(rpcVulns []*detector.Vulnerability) []types.DetectedVulnerability {
var vulns []types.DetectedVulnerability
for _, vuln := range rpcVulns {
severity := dbTypes.Severity(vuln.Severity)
vulns = append(vulns, types.DetectedVulnerability{
VulnerabilityID: vuln.VulnerabilityId,
PkgName: vuln.PkgName,
InstalledVersion: vuln.InstalledVersion,
FixedVersion: vuln.FixedVersion,
Vulnerability: dbTypes.Vulnerability{
Title: vuln.Title,
Description: vuln.Description,
Severity: severity.String(),
References: vuln.References,
},
})
}
return vulns
}
func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*detector.Vulnerability {
var rpcVulns []*detector.Vulnerability
for _, vuln := range vulns {
severity, err := dbTypes.NewSeverity(vuln.Severity)
if err != nil {
log.Logger.Warn(err)
}
rpcVulns = append(rpcVulns, &detector.Vulnerability{
VulnerabilityId: vuln.VulnerabilityID,
PkgName: vuln.PkgName,
InstalledVersion: vuln.InstalledVersion,
FixedVersion: vuln.FixedVersion,
Title: vuln.Title,
Description: vuln.Description,
Severity: detector.Severity(severity),
References: vuln.References,
})
}
return rpcVulns
}

309
pkg/rpc/convert_test.go Normal file
View File

@@ -0,0 +1,309 @@
package rpc
import (
"os"
"testing"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/fanal/analyzer"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
code := m.Run()
os.Exit(code)
}
func TestConvertToRpcPkgs(t *testing.T) {
type args struct {
pkgs []analyzer.Package
}
tests := []struct {
name string
args args
want []*detector.Package
}{
{
name: "happy path",
args: args{
pkgs: []analyzer.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
},
},
want: []*detector.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcPkgs(tt.args.pkgs)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestConvertFromRpcPkgs(t *testing.T) {
type args struct {
rpcPkgs []*detector.Package
}
tests := []struct {
name string
args args
want []analyzer.Package
}{
{
args: args{
rpcPkgs: []*detector.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
},
},
want: []analyzer.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcPkgs(tt.args.rpcPkgs)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestConvertFromRpcLibraries(t *testing.T) {
type args struct {
rpcLibs []*detector.Library
}
tests := []struct {
name string
args args
want []ptypes.Library
}{
{
name: "happy path",
args: args{
rpcLibs: []*detector.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
},
want: []ptypes.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcLibraries(tt.args.rpcLibs)
assert.Equal(t, got, tt.want, tt.name)
})
}
}
func TestConvertToRpcLibraries(t *testing.T) {
type args struct {
libs []ptypes.Library
}
tests := []struct {
name string
args args
want []*detector.Library
}{
{
name: "happy path",
args: args{
libs: []ptypes.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
},
want: []*detector.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcLibraries(tt.args.libs)
assert.Equal(t, got, tt.want, tt.name)
})
}
}
func TestConvertFromRpcVulns(t *testing.T) {
type args struct {
rpcVulns []*detector.Vulnerability
}
tests := []struct {
name string
args args
want []types.DetectedVulnerability
}{
{
name: "happy path",
args: args{
rpcVulns: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_CRITICAL,
References: []string{"http://example.com"},
},
},
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "DoS",
Description: "Denial of Service",
Severity: "CRITICAL",
References: []string{"http://example.com"},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcVulns(tt.args.rpcVulns)
assert.Equal(t, got, tt.want, tt.name)
})
}
}
func TestConvertToRpcVulns(t *testing.T) {
type args struct {
vulns []types.DetectedVulnerability
}
tests := []struct {
name string
args args
want []*detector.Vulnerability
}{
{
name: "happy path",
args: args{
vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "DoS",
Description: "Denial of Service",
Severity: "MEDIUM",
References: []string{"http://example.com"},
},
},
},
},
want: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_MEDIUM,
References: []string{"http://example.com"},
},
},
},
{
name: "invalid severity",
args: args{
vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "DoS",
Description: "Denial of Service",
Severity: "INVALID",
References: []string{"http://example.com"},
},
},
},
},
want: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_UNKNOWN,
References: []string{"http://example.com"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcVulns(tt.args.vulns)
assert.Equal(t, got, tt.want, tt.name)
})
}
}

25
pkg/rpc/server/inject.go Normal file
View File

@@ -0,0 +1,25 @@
// +build wireinject
package server
import (
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/rpc/server/library"
"github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
)
func initializeOspkgServer() *ospkg.Server {
wire.Build(ospkg.SuperSet)
return &ospkg.Server{}
}
func initializeLibServer() *library.Server {
wire.Build(library.SuperSet)
return &library.Server{}
}
func initializeDBWorker() dbWorker {
wire.Build(SuperSet)
return dbWorker{}
}

View File

@@ -0,0 +1,42 @@
package library
import (
"context"
"github.com/google/wire"
"golang.org/x/xerrors"
detector "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/vulnerability"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
detector.SuperSet,
vulnerability.SuperSet,
NewServer,
)
type Server struct {
detector detector.Operation
vulnClient vulnerability.Operation
}
func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
return &Server{detector: detector, vulnClient: vulnClient}
}
func (s *Server) Detect(ctx context.Context, req *proto.LibDetectRequest) (res *proto.DetectResponse, err error) {
vulns, err := s.detector.Detect(req.FilePath, rpc.ConvertFromRpcLibraries(req.Libraries))
if err != nil {
err = xerrors.Errorf("failed to detect library vulnerabilities: %w", err)
log.Logger.Error(err)
return nil, err
}
s.vulnClient.FillInfo(vulns, false)
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRpcVulns(vulns)}, nil
}

View File

@@ -0,0 +1,135 @@
package library
import (
"context"
"os"
"testing"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/log"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
code := m.Run()
os.Exit(code)
}
func TestServer_Detect(t *testing.T) {
type args struct {
req *proto.LibDetectRequest
}
tests := []struct {
name string
args args
detect library.DetectExpectation
wantRes *proto.DetectResponse
wantErr string
}{
{
name: "happy path",
args: args{
req: &proto.LibDetectRequest{
FilePath: "app/Pipfile.lock",
Libraries: []*proto.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
detect: library.DetectExpectation{
Args: library.DetectInput{
FilePath: "app/Pipfile.lock",
Libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
ReturnArgs: library.DetectOutput{
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "test",
InstalledVersion: "1",
FixedVersion: "2",
Vulnerability: dbTypes.Vulnerability{
Title: "title",
Description: "description",
Severity: "MEDIUM",
References: []string{"http://example.com"},
},
},
},
},
},
wantRes: &proto.DetectResponse{
Vulnerabilities: []*proto.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "test",
InstalledVersion: "1",
FixedVersion: "2",
Title: "title",
Description: "description",
Severity: proto.Severity_MEDIUM,
References: []string{"http://example.com"},
},
},
},
},
{
name: "Detect returns an error",
args: args{
req: &proto.LibDetectRequest{
FilePath: "app/Pipfile.lock",
Libraries: []*proto.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
detect: library.DetectExpectation{
Args: library.DetectInput{
FilePath: "app/Pipfile.lock",
Libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
ReturnArgs: library.DetectOutput{
Err: xerrors.New("error"),
},
},
wantErr: "failed to detect library vulnerabilities",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := library.NewMockDetector([]library.DetectExpectation{tt.detect})
mockVulnClient := vulnerability.NewMockVulnClient()
s := NewServer(mockDetector, mockVulnClient)
ctx := context.TODO()
gotRes, err := s.Detect(ctx, tt.args.req)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.wantRes, gotRes, tt.name)
mockDetector.AssertExpectations(t)
mockVulnClient.AssertExpectations(t)
})
}
}

View File

@@ -0,0 +1,42 @@
package ospkg
import (
"context"
"github.com/google/wire"
"golang.org/x/xerrors"
detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/vulnerability"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
detector.SuperSet,
vulnerability.SuperSet,
NewServer,
)
type Server struct {
detector detector.Operation
vulnClient vulnerability.Operation
}
func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
return &Server{detector: detector, vulnClient: vulnClient}
}
func (s *Server) Detect(ctx context.Context, req *proto.OSDetectRequest) (res *proto.DetectResponse, err error) {
vulns, eosl, err := s.detector.Detect(req.OsFamily, req.OsName, rpc.ConvertFromRpcPkgs(req.Packages))
if err != nil {
err = xerrors.Errorf("failed to detect vulnerabilities of OS packages: %w", err)
log.Logger.Error(err)
return nil, err
}
s.vulnClient.FillInfo(vulns, false)
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRpcVulns(vulns), Eosl: eosl}, nil
}

View File

@@ -0,0 +1,125 @@
package ospkg
import (
"context"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
code := m.Run()
os.Exit(code)
}
func TestServer_Detect(t *testing.T) {
type args struct {
req *proto.OSDetectRequest
}
tests := []struct {
name string
args args
detect ospkg.DetectExpectation
wantRes *proto.DetectResponse
wantErr string
}{
{
name: "happy path",
args: args{
req: &proto.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*proto.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
},
detect: ospkg.DetectExpectation{
Args: ospkg.DetectInput{
OSFamily: "alpine",
OSName: "3.10.2",
Pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
ReturnArgs: ospkg.DetectOutput{
Eosl: false,
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "musl",
Vulnerability: dbTypes.Vulnerability{
Severity: "HIGH",
}},
},
},
},
wantRes: &proto.DetectResponse{
Vulnerabilities: []*proto.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "musl",
Severity: proto.Severity_HIGH,
},
},
},
},
{
name: "Detect returns an error",
args: args{
req: &proto.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*proto.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
},
detect: ospkg.DetectExpectation{
Args: ospkg.DetectInput{
OSFamily: "alpine",
OSName: "3.10.2",
Pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
ReturnArgs: ospkg.DetectOutput{
Err: xerrors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities of OS packages: error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := ospkg.NewMockDetector([]ospkg.DetectExpectation{tt.detect})
mockVulnClient := vulnerability.NewMockVulnClient()
s := NewServer(mockDetector, mockVulnClient)
gotRes, err := s.Detect(context.TODO(), tt.args.req)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.wantRes, gotRes, tt.name)
mockDetector.AssertExpectations(t)
mockVulnClient.AssertExpectations(t)
})
}
}

137
pkg/rpc/server/server.go Normal file
View File

@@ -0,0 +1,137 @@
package server
import (
"context"
"io/ioutil"
"net/http"
"os"
"sync"
"time"
"github.com/google/wire"
"github.com/twitchtv/twirp"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/server/config"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
rpc "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
dbFile.SuperSet,
newDBWorker,
)
func ListenAndServe(addr string, c config.Config) error {
requestWg := &sync.WaitGroup{}
dbUpdateWg := &sync.WaitGroup{}
withWaitGroup := func(base http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Stop processing requests during DB update
dbUpdateWg.Wait()
// Wait for all requests to be processed before DB update
requestWg.Add(1)
defer requestWg.Done()
base.ServeHTTP(w, r)
})
}
go func() {
worker := initializeDBWorker()
ctx := context.Background()
for {
time.Sleep(1 * time.Hour)
if err := worker.update(ctx, c.AppVersion, c.CacheDir, dbUpdateWg, requestWg); err != nil {
log.Logger.Errorf("%+v\n", err)
}
}
}()
mux := http.NewServeMux()
osHandler := rpc.NewOSDetectorServer(initializeOspkgServer(), nil)
mux.Handle(rpc.OSDetectorPathPrefix, withToken(withWaitGroup(osHandler), c.Token))
libHandler := rpc.NewLibDetectorServer(initializeLibServer(), nil)
mux.Handle(rpc.LibDetectorPathPrefix, withToken(withWaitGroup(libHandler), c.Token))
log.Logger.Infof("Listening %s...", addr)
return http.ListenAndServe(addr, mux)
}
func withToken(base http.Handler, token string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get("Trivy-Token") {
rpc.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
})
}
type dbWorker struct {
dbClient dbFile.Operation
}
func newDBWorker(dbClient dbFile.Operation) dbWorker {
return dbWorker{dbClient: dbClient}
}
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
dbUpdateWg, requestWg *sync.WaitGroup) error {
needsUpdate, err := w.dbClient.NeedsUpdate(ctx, appVersion, false, false)
if err != nil {
return xerrors.Errorf("failed to check if db needs an update")
} else if !needsUpdate {
return nil
}
log.Logger.Info("Updating DB...")
if err = w.hotUpdate(ctx, cacheDir, dbUpdateWg, requestWg); err != nil {
return xerrors.Errorf("failed DB hot update")
}
return nil
}
func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, requestWg *sync.WaitGroup) error {
tmpDir, err := ioutil.TempDir("", "db")
if err != nil {
return xerrors.Errorf("failed to create a temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
if err := w.dbClient.Download(ctx, tmpDir, false); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
log.Logger.Info("Suspending all requests during DB update")
dbUpdateWg.Add(1)
defer dbUpdateWg.Done()
log.Logger.Info("Waiting for all requests to be processed before DB update...")
requestWg.Wait()
if err = db.Close(); err != nil {
return xerrors.Errorf("failed to close DB: %w", err)
}
if _, err = utils.CopyFile(db.Path(tmpDir), db.Path(cacheDir)); err != nil {
return xerrors.Errorf("failed to copy the database file: %w", err)
}
log.Logger.Info("Reopening DB...")
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("failed to open DB: %w", err)
}
return nil
}

View File

@@ -0,0 +1,157 @@
package server
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
}
func Test_dbWorker_update(t *testing.T) {
type needsUpdateInput struct {
appVersion string
skip bool
}
type needsUpdateOutput struct {
needsUpdate bool
err error
}
type needsUpdate struct {
input needsUpdateInput
output needsUpdateOutput
}
type download struct {
call bool
err error
}
type args struct {
appVersion string
}
tests := []struct {
name string
needsUpdate needsUpdate
download download
args args
want db.Metadata
wantErr string
}{
{
name: "happy path",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
},
download: download{
call: true,
},
args: args{appVersion: "1"},
want: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
{
name: "not update",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: false},
},
args: args{appVersion: "1"},
},
{
name: "NeedsUpdate returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{err: xerrors.New("fail")},
},
args: args{appVersion: "1"},
wantErr: "failed to check if db needs an update",
},
{
name: "Download returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
},
download: download{
call: true,
err: xerrors.New("fail"),
},
args: args{appVersion: "1"},
wantErr: "failed DB hot update",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir, err := ioutil.TempDir("", "server-test")
require.NoError(t, err, tt.name)
require.NoError(t, db.Init(cacheDir), tt.name)
mockDBClient := new(dbFile.MockClient)
mockDBClient.On("NeedsUpdate", mock.Anything,
tt.needsUpdate.input.appVersion, false, tt.needsUpdate.input.skip).Return(
tt.needsUpdate.output.needsUpdate, tt.needsUpdate.output.err)
if tt.download.call {
mockDBClient.On("Download", mock.Anything, mock.Anything, false).Run(
func(args mock.Arguments) {
// fake download: copy testdata/new.db to tmpDir/db/trivy.db
content, err := ioutil.ReadFile("testdata/new.db")
require.NoError(t, err, tt.name)
tmpDir := args.String(1)
dbPath := db.Path(tmpDir)
require.NoError(t, os.MkdirAll(filepath.Dir(dbPath), 0777), tt.name)
err = ioutil.WriteFile(dbPath, content, 0444)
require.NoError(t, err, tt.name)
}).Return(tt.download.err)
}
w := newDBWorker(mockDBClient)
var dbUpdateWg, requestWg sync.WaitGroup
err = w.update(context.Background(), tt.args.appVersion, cacheDir,
&dbUpdateWg, &requestWg)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
if !tt.download.call {
return
}
dbc := db.Config{}
got, err := dbc.GetMetadata()
assert.NoError(t, err, tt.name)
assert.Equal(t, tt.want, got, tt.name)
mockDBClient.AssertExpectations(t)
})
}
}

BIN
pkg/rpc/server/testdata/new.db vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,46 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package server
import (
"github.com/aquasecurity/trivy-db/pkg/db"
db2 "github.com/aquasecurity/trivy/pkg/db"
library2 "github.com/aquasecurity/trivy/pkg/detector/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/rpc/server/library"
"github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"k8s.io/utils/clock"
)
// Injectors from inject.go:
func initializeOspkgServer() *ospkg.Server {
detector := ospkg2.Detector{}
config := db.Config{}
client := vulnerability.NewClient(config)
server := ospkg.NewServer(detector, client)
return server
}
func initializeLibServer() *library.Server {
driverFactory := library2.DriverFactory{}
detector := library2.NewDetector(driverFactory)
config := db.Config{}
client := vulnerability.NewClient(config)
server := library.NewServer(detector, client)
return server
}
func initializeDBWorker() dbWorker {
config := db.Config{}
client := github.NewClient()
realClock := clock.RealClock{}
dbClient := db2.NewClient(config, client, realClock)
serverDbWorker := newDBWorker(dbClient)
return serverDbWorker
}

View File

@@ -1,164 +0,0 @@
package pkg
import (
"context"
l "log"
"os"
"strings"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/genuinetools/reg/registry"
"github.com/urfave/cli"
"golang.org/x/xerrors"
)
func Run(c *cli.Context) (err error) {
if c.Bool("quiet") || c.Bool("no-progress") {
utils.Quiet = true
}
debug := c.Bool("debug")
if err = log.InitLogger(debug, c.Bool("quiet")); err != nil {
l.Fatal(err)
}
if c.String("only-update") != "" || c.Bool("refresh") || c.Bool("auto-refresh") {
log.Logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
}
cacheDir := c.String("cache-dir")
utils.SetCacheDir(c.String("cache-dir"))
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
}
downloadDBOnly := c.Bool("download-db-only")
skipUpdate := c.Bool("skip-update")
if skipUpdate && downloadDBOnly {
return xerrors.New("The --skip-update and --download-db-only option can not be specified both")
}
light := c.Bool("light")
client := dbFile.NewClient()
ctx := context.Background()
if err = client.Download(ctx, c.App.Version, cacheDir, light, skipUpdate); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
if downloadDBOnly {
return nil
}
reset := c.Bool("reset")
if reset {
log.Logger.Info("Resetting...")
if err = cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
if err = os.RemoveAll(utils.CacheDir()); err != nil {
return xerrors.New("failed to remove cache")
}
return nil
}
clearCache := c.Bool("clear-cache")
if clearCache {
log.Logger.Info("Removing image caches...")
if err = cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
return nil
}
args := c.Args()
filePath := c.String("input")
if filePath == "" && len(args) == 0 {
log.Logger.Info(`trivy requires at least 1 argument or --input option.`)
cli.ShowAppHelpAndExit(c, 1)
}
o := c.String("output")
output := os.Stdout
if o != "" {
if output, err = os.Create(o); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
var imageName string
if filePath == "" {
imageName = args[0]
}
// Check whether 'latest' tag is used
if imageName != "" {
image, err := registry.ParseImage(imageName)
if err != nil {
return xerrors.Errorf("invalid image: %w", err)
}
if image.Tag == "latest" && !clearCache {
log.Logger.Warn("You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed")
}
}
timeout := c.Duration("timeout")
scanOptions := types.ScanOptions{
VulnType: strings.Split(c.String("vuln-type"), ","),
Timeout: timeout,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
results, err := scanner.ScanImage(imageName, filePath, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}
severities := splitSeverity(c.String("severity"))
ignoreFile := c.String("ignorefile")
ignoreUnfixed := c.Bool("ignore-unfixed")
vulnClient := vulnerability.NewClient()
for i := range results {
results[i].Vulnerabilities = vulnClient.FillAndFilter(results[i].Vulnerabilities,
severities, ignoreUnfixed, ignoreFile, light)
}
format := c.String("format")
template := c.String("template")
if err = report.WriteResults(format, output, results, template, light); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
exitCode := c.Int("exit-code")
if exitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(exitCode)
}
}
}
return nil
}
func splitSeverity(severity string) []dbTypes.Severity {
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
log.Logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -1,8 +1,10 @@
package library
import (
"io/ioutil"
"os"
"path/filepath"
detector "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/fanal/analyzer"
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
@@ -13,64 +15,29 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
"github.com/aquasecurity/fanal/extractor"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/library/bundler"
"github.com/aquasecurity/trivy/pkg/scanner/library/cargo"
"github.com/aquasecurity/trivy/pkg/scanner/library/composer"
"github.com/aquasecurity/trivy/pkg/scanner/library/node"
"github.com/aquasecurity/trivy/pkg/scanner/library/python"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/knqyf263/go-version"
"golang.org/x/xerrors"
)
type Scanner interface {
ParseLockfile(*os.File) ([]ptypes.Library, error)
Detect(string, *version.Version) ([]types.DetectedVulnerability, error)
Type() string
type Scanner struct {
detector detector.Operation
}
func NewScanner(filename string) Scanner {
var scanner Scanner
switch filename {
case "Gemfile.lock":
scanner = bundler.NewScanner()
case "Cargo.lock":
scanner = cargo.NewScanner()
case "composer.lock":
scanner = composer.NewScanner()
case "package-lock.json":
scanner = node.NewScanner(node.ScannerTypeNpm)
case "yarn.lock":
scanner = node.NewScanner(node.ScannerTypeYarn)
case "Pipfile.lock":
scanner = python.NewScanner(python.ScannerTypePipenv)
case "poetry.lock":
scanner = python.NewScanner(python.ScannerTypePoetry)
default:
return nil
}
return scanner
func NewScanner(detector detector.Operation) Scanner {
return Scanner{detector: detector}
}
func Scan(files extractor.FileMap, scanOptions types.ScanOptions) (map[string][]types.DetectedVulnerability, error) {
func (s Scanner) Scan(files extractor.FileMap) (map[string][]types.DetectedVulnerability, error) {
results, err := analyzer.GetLibraries(files)
if err != nil {
return nil, xerrors.Errorf("failed to analyze libraries: %w", err)
}
vulnerabilities := map[string][]types.DetectedVulnerability{}
for path, pkgs := range results {
log.Logger.Debugf("Detecting library vulnerabilities, path: %s", path)
scanner := NewScanner(filepath.Base(string(path)))
if scanner == nil {
return nil, xerrors.New("unknown file type")
}
vulns, err := scan(scanner, pkgs)
for path, libs := range results {
vulns, err := s.detector.Detect(string(path), libs)
if err != nil {
return nil, xerrors.Errorf("failed to scan %s vulnerabilities: %w", scanner.Type(), err)
return nil, xerrors.Errorf("failed library scan: %w", err)
}
vulnerabilities[string(path)] = vulns
@@ -78,40 +45,23 @@ func Scan(files extractor.FileMap, scanOptions types.ScanOptions) (map[string][]
return vulnerabilities, nil
}
func ScanFile(f *os.File) ([]types.DetectedVulnerability, error) {
scanner := NewScanner(filepath.Base(f.Name()))
if scanner == nil {
return nil, xerrors.New("unknown file type")
func (s Scanner) ScanFile(f *os.File) ([]types.DetectedVulnerability, error) {
content, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
files := extractor.FileMap{
f.Name(): content,
}
pkgs, err := scanner.ParseLockfile(f)
results, err := s.Scan(files)
if err != nil {
return nil, err
}
vulns, err := scan(scanner, pkgs)
if err != nil {
return nil, err
// need only 1 result
for _, vulns := range results {
return vulns, nil
}
return vulns, nil
}
func scan(scanner Scanner, pkgs []ptypes.Library) ([]types.DetectedVulnerability, error) {
log.Logger.Infof("Detecting %s vulnerabilities...", scanner.Type())
var vulnerabilities []types.DetectedVulnerability
for _, pkg := range pkgs {
v, err := version.NewVersion(pkg.Version)
if err != nil {
log.Logger.Debug(err)
continue
}
vulns, err := scanner.Detect(pkg.Name, v)
if err != nil {
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
}
vulnerabilities = append(vulnerabilities, vulns...)
}
return vulnerabilities, nil
return nil, nil
}

View File

@@ -0,0 +1,201 @@
package library
import (
"testing"
library2 "github.com/aquasecurity/trivy/pkg/detector/library"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/fanal/extractor"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestScanner_Scan(t *testing.T) {
type detectInput struct {
filePath string
libs []ptypes.Library
}
type detectOutput struct {
vulns []types.DetectedVulnerability
err error
}
type detect struct {
input detectInput
output detectOutput
}
type args struct {
files extractor.FileMap
}
tests := []struct {
name string
args args
detect []detect
want map[string][]types.DetectedVulnerability
wantErr string
}{
{
name: "happy",
args: args{
files: extractor.FileMap{
"app/Pipfile.lock": []byte(`{
"_meta": {
"hash": {
"sha256": "ad1805ab0e16cf08032c3fe45eeaa29b79e9c196650411977af14e31b12ff0cd"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"django": {
"hashes": [
"sha256:665457d4146bbd34ae9d2970fa3b37082d7b225b0671bfd24c337458f229db78",
"sha256:bde46d4dbc410678e89bc95ea5d312dd6eb4c37d0fa0e19c9415cad94addf22f"
],
"index": "pypi",
"version": "==3.0.0"
}
}
}
`),
"app/package-lock.json": []byte(`{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
}`),
},
},
detect: []detect{
{
input: detectInput{
filePath: "app/Pipfile.lock",
libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
},
},
},
{
input: detectInput{
filePath: "app/package-lock.json",
libs: []ptypes.Library{
{Name: "react", Version: "16.8.6"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0002"},
{VulnerabilityID: "CVE-2019-0003"},
},
},
},
},
want: map[string][]types.DetectedVulnerability{
"app/Pipfile.lock": {{VulnerabilityID: "CVE-2019-0001"}},
"app/package-lock.json": {
{VulnerabilityID: "CVE-2019-0002"},
{VulnerabilityID: "CVE-2019-0003"},
},
},
},
{
name: "broken lock file",
args: args{
files: extractor.FileMap{
"app/Pipfile.lock": []byte(`{broken}`),
},
},
wantErr: "failed to analyze libraries",
},
{
name: "Detect returns an error",
args: args{
files: extractor.FileMap{
"app/package-lock.json": []byte(`{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
}`),
},
},
detect: []detect{
{
input: detectInput{
filePath: "app/package-lock.json",
libs: []ptypes.Library{
{Name: "react", Version: "16.8.6"},
},
},
output: detectOutput{err: xerrors.New("error")},
},
},
wantErr: "failed library scan",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(library2.MockDetector)
for _, d := range tt.detect {
mockDetector.On("Detect", d.input.filePath, d.input.libs).Return(
d.output.vulns, d.output.err)
}
s := Scanner{
detector: mockDetector,
}
got, err := s.Scan(tt.args.files)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

View File

@@ -1,64 +0,0 @@
package alpine
import (
"os"
"testing"
"time"
"github.com/aquasecurity/trivy/pkg/log"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
}
func TestScanner_IsSupportedVersion(t *testing.T) {
vectors := map[string]struct {
now time.Time
osFamily string
osVersion string
expected bool
}{
"alpine3.6": {
now: time.Date(2019, 3, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.6",
expected: true,
},
"alpine3.6 with EOL": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.6.5",
expected: false,
},
"alpine3.9": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.9.0",
expected: true,
},
"alpine3.10": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "3.10",
expected: true,
},
"unknown": {
now: time.Date(2019, 5, 2, 23, 59, 59, 0, time.UTC),
osFamily: "alpine",
osVersion: "unknown",
expected: false,
},
}
for testName, v := range vectors {
s := NewScanner()
t.Run(testName, func(t *testing.T) {
actual := s.isSupportedVersion(v.now, v.osFamily, v.osVersion)
if actual != v.expected {
t.Errorf("[%s] got %v, want %v", testName, actual, v.expected)
}
})
}
}

View File

@@ -1,9 +1,10 @@
package ospkg
import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
_ "github.com/aquasecurity/fanal/analyzer/command/apk"
fos "github.com/aquasecurity/fanal/analyzer/os"
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
@@ -13,44 +14,26 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
"github.com/aquasecurity/fanal/extractor"
ftypes "github.com/aquasecurity/fanal/types"
detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg/alpine"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg/amazon"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg/debian"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg/redhat"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
)
type Scanner interface {
Detect(string, []analyzer.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(string, string) bool
type Scanner struct {
detector detector.Operation
}
func Scan(files extractor.FileMap) (string, string, []types.DetectedVulnerability, error) {
func NewScanner(detector detector.Operation) Scanner {
return Scanner{detector: detector}
}
func (s Scanner) Scan(files extractor.FileMap) (string, string, []types.DetectedVulnerability, error) {
os, err := analyzer.GetOS(files)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to analyze OS: %w", err)
}
log.Logger.Debugf("OS family: %s, OS version: %s", os.Family, os.Name)
var s Scanner
switch os.Family {
case fos.Alpine:
s = alpine.NewScanner()
case fos.Debian:
s = debian.NewScanner()
case fos.Ubuntu:
s = ubuntu.NewScanner()
case fos.RedHat, fos.CentOS:
s = redhat.NewScanner()
case fos.Amazon:
s = amazon.NewScanner()
default:
log.Logger.Warnf("unsupported os : %s", os.Family)
return "", "", nil, nil
}
pkgs, err := analyzer.GetPackages(files)
if err != nil {
if xerrors.Is(err, ftypes.ErrNoRpmCmd) {
@@ -69,15 +52,14 @@ func Scan(files extractor.FileMap) (string, string, []types.DetectedVulnerabilit
pkgs = mergePkgs(pkgs, pkgsFromCommands)
log.Logger.Debugf("the number of packages: %d", len(pkgs))
if !s.IsSupportedVersion(os.Family, os.Name) {
log.Logger.Warnf("This OS version is no longer supported by the distribution: %s %s", os.Family, os.Name)
log.Logger.Warnf("The vulnerability detection may be insufficient because security updates are not provided")
}
vulns, err := s.Detect(os.Name, pkgs)
vulns, eosl, err := s.detector.Detect(os.Family, os.Name, pkgs)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
if eosl {
log.Logger.Warnf("This OS version is no longer supported by the distribution: %s %s", os.Family, os.Name)
log.Logger.Warnf("The vulnerability detection may be insufficient because security updates are not provided")
}
return os.Family, os.Name, vulns, nil
}

View File

@@ -0,0 +1,213 @@
package ospkg
import (
"os"
"testing"
ospkg2 "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"golang.org/x/xerrors"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestMain(m *testing.M) {
log.InitLogger(false, true)
code := m.Run()
os.Exit(code)
}
func TestScanner_Scan(t *testing.T) {
type detectInput struct {
osFamily string
osName string
pkgs []analyzer.Package
}
type detectOutput struct {
vulns []types.DetectedVulnerability
eosl bool
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
files extractor.FileMap
}
type want struct {
osFamily string
osName string
vulns []types.DetectedVulnerability
err string
}
tests := []struct {
name string
fields fields
detect detect
want want
}{
{
name: "happy path",
fields: fields{
files: extractor.FileMap{
"etc/alpine-release": []byte("3.10.2"),
"lib/apk/db/installed": []byte(`C:Q11Ing8/u1VIdY9czSxaDO9wJg72I=
P:musl
V:1.1.22-r3
A:x86_64
S:368204
I:598016
T:the musl c library (libc) implementation
U:http://www.musl-libc.org/
L:MIT
o:musl
m:Timo Teräs <timo.teras@iki.fi>
t:1565162130
c:0c777cf840e82cdc528651e3f3f8f9dda6b1b028
p:so:libc.musl-x86_64.so.1=1
F:lib
R:libc.musl-x86_64.so.1
a:0:0:777
Z:Q17yJ3JFNypA4mxhJJr0ou6CzsJVI=
R:ld-musl-x86_64.so.1
a:0:0:755
Z:Q1TTLtUopPeiF9JrA0cgKQZYggG+c=
F:usr
F:usr/lib
`),
},
},
detect: detect{
input: detectInput{
osFamily: "alpine",
osName: "3.10.2",
pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001", PkgName: "musl"},
},
err: nil,
},
},
want: want{
osFamily: "alpine",
osName: "3.10.2",
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001", PkgName: "musl"},
},
},
},
{
name: "sad path",
fields: fields{
files: extractor.FileMap{
"etc/alpine-release": []byte("3.10.2"),
"invalid": []byte(`invalid`),
},
},
want: want{err: analyzer.ErrPkgAnalysis.Error()},
},
{
name: "Detect returns an error",
fields: fields{
files: extractor.FileMap{
"etc/alpine-release": []byte("3.10.2"),
"lib/apk/db/installed": []byte(`C:Q11Ing8/u1VIdY9czSxaDO9wJg72I=
P:musl
V:1.1.22-r3
A:x86_64
`),
},
},
detect: detect{
input: detectInput{
osFamily: "alpine",
osName: "3.10.2",
pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
want: want{
err: "failed to detect vulnerabilities",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(ospkg2.MockDetector)
mockDetector.On("Detect", tt.detect.input.osFamily, tt.detect.input.osName,
tt.detect.input.pkgs).Return(tt.detect.output.vulns, tt.detect.output.eosl, tt.detect.output.err)
s := NewScanner(mockDetector)
got, got1, got2, err := s.Scan(tt.fields.files)
if tt.want.err != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.want.err, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want.osFamily, got)
assert.Equal(t, tt.want.osName, got1)
assert.Equal(t, tt.want.vulns, got2)
mockDetector.AssertExpectations(t)
})
}
}
func Test_mergePkgs(t *testing.T) {
type args struct {
pkgs []analyzer.Package
pkgsFromCommands []analyzer.Package
}
tests := []struct {
name string
args args
want []analyzer.Package
}{
{
name: "happy path",
args: args{
pkgs: []analyzer.Package{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "3.4.5"},
{Name: "baz", Version: "6.7.8"},
},
pkgsFromCommands: []analyzer.Package{
{Name: "bar", Version: "1.1.1"},
{Name: "hoge", Version: "9.0.1"},
},
},
want: []analyzer.Package{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "3.4.5"},
{Name: "baz", Version: "6.7.8"},
{Name: "hoge", Version: "9.0.1"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := mergePkgs(tt.args.pkgs, tt.args.pkgsFromCommands)
assert.Equal(t, tt.want, got, tt.name)
})
}
}

View File

@@ -7,33 +7,76 @@ import (
"os"
"sort"
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/fanal/extractor/docker"
libDetector "github.com/aquasecurity/trivy/pkg/detector/library"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
rpcLibDetector "github.com/aquasecurity/trivy/pkg/rpc/client/library"
rpcOSDetector "github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner/library"
libScanner "github.com/aquasecurity/trivy/pkg/scanner/library"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg"
ospkgScanner "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/xerrors"
)
func ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (report.Results, error) {
var StandaloneSet = wire.NewSet(
ospkgDetector.SuperSet,
ospkgScanner.NewScanner,
libDetector.SuperSet,
libScanner.NewScanner,
NewScanner,
)
var ClientSet = wire.NewSet(
rpcOSDetector.SuperSet,
ospkgScanner.NewScanner,
rpcLibDetector.SuperSet,
libScanner.NewScanner,
NewScanner,
)
type Scanner struct {
ospkgScanner ospkg.Scanner
libScanner library.Scanner
}
func NewScanner(ospkgScanner ospkg.Scanner, libScanner library.Scanner) Scanner {
return Scanner{ospkgScanner: ospkgScanner, libScanner: libScanner}
}
func (s Scanner) ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (report.Results, error) {
results := report.Results{}
ctx := context.Background()
var target string
var files extractor.FileMap
var ac analyzer.Config
dockerOption, err := types.GetDockerOption()
if err != nil {
return nil, xerrors.Errorf("failed to get docker option: %w", err)
}
if imageName != "" {
dockerOption.Timeout = scanOptions.Timeout
}
ext, err := docker.NewDockerExtractor(dockerOption)
if err != nil {
return nil, err
}
ac = analyzer.Config{Extractor: ext}
if imageName != "" {
target = imageName
dockerOption, err := types.GetDockerOption()
if err != nil {
return nil, xerrors.Errorf("failed to get docker option: %w", err)
}
dockerOption.Timeout = scanOptions.Timeout
files, err = analyzer.Analyze(ctx, imageName, dockerOption)
files, err = ac.Analyze(ctx, imageName, dockerOption)
if err != nil {
return nil, xerrors.Errorf("failed to analyze image: %w", err)
}
@@ -44,7 +87,7 @@ func ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (repor
return nil, xerrors.Errorf("failed to open stream: %w", err)
}
files, err = analyzer.AnalyzeFile(ctx, rc)
files, err = ac.AnalyzeFile(ctx, rc)
if err != nil {
return nil, err
}
@@ -53,9 +96,9 @@ func ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (repor
}
if utils.StringInSlice("os", scanOptions.VulnType) {
osFamily, osVersion, osVulns, err := ospkg.Scan(files)
if err != nil {
return nil, xerrors.Errorf("failed to scan image: %w", err)
osFamily, osVersion, osVulns, err := s.ospkgScanner.Scan(files)
if err != nil && err != ospkgDetector.ErrUnsupportedOS {
return nil, xerrors.Errorf("failed to scan the image: %w", err)
}
if osFamily != "" {
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion)
@@ -67,7 +110,7 @@ func ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (repor
}
if utils.StringInSlice("library", scanOptions.VulnType) {
libVulns, err := library.Scan(files, scanOptions)
libVulns, err := s.libScanner.Scan(files)
if err != nil {
return nil, xerrors.Errorf("failed to scan libraries: %w", err)
}
@@ -88,8 +131,8 @@ func ScanImage(imageName, filePath string, scanOptions types.ScanOptions) (repor
return results, nil
}
func ScanFile(f *os.File) (report.Results, error) {
vulns, err := library.ScanFile(f)
func (s Scanner) ScanFile(f *os.File) (report.Results, error) {
vulns, err := s.libScanner.ScanFile(f)
if err != nil {
return nil, xerrors.Errorf("failed to scan libraries in file: %w", err)
}

View File

@@ -5,4 +5,8 @@ import "time"
type ScanOptions struct {
VulnType []string
Timeout time.Duration
// for client/server
RemoteURL string
Token string
}

View File

@@ -2,6 +2,7 @@ package utils
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
@@ -126,3 +127,28 @@ func FilterTargets(prefixPath string, targets map[string]struct{}) (map[string]s
}
return filtered, nil
}
func CopyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
n, err := io.Copy(destination, source)
return n, err
}

View File

@@ -9,6 +9,9 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/kylelemons/godebug/pretty"
)
@@ -142,3 +145,51 @@ func TestFilterTargets(t *testing.T) {
})
}
}
func TestCopyFile(t *testing.T) {
type args struct {
src string
dst string
}
tests := []struct {
name string
args args
content []byte
want string
wantErr string
}{
{
name: "happy path",
content: []byte("this is a content"),
args: args{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
src := tt.args.src
if tt.args.src == "" {
s, err := ioutil.TempFile("", "src")
require.NoError(t, err, tt.name)
_, err = s.Write(tt.content)
require.NoError(t, err, tt.name)
src = s.Name()
}
dst := tt.args.dst
if tt.args.dst == "" {
d, err := ioutil.TempFile("", "dst")
require.NoError(t, err, tt.name)
dst = d.Name()
require.NoError(t, d.Close(), tt.name)
}
_, err := CopyFile(src, dst)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Equal(t, err.Error(), tt.wantErr, tt.name)
} else {
assert.NoError(t, err, tt.name)
}
})
}
}

View File

@@ -0,0 +1,3 @@
# test
CVE-2019-0001
CVE-2019-0002

View File

@@ -6,6 +6,8 @@ import (
"sort"
"strings"
"github.com/google/wire"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
@@ -18,41 +20,52 @@ const (
DefaultIgnoreFile = ".trivyignore"
)
var SuperSet = wire.NewSet(
wire.Struct(new(db.Config)),
NewClient,
wire.Bind(new(Operation), new(Client)),
)
type Operation interface {
FillInfo(vulns []types.DetectedVulnerability, light bool)
Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability
}
type Client struct {
dbc db.Operations
}
func NewClient() Client {
return Client{
dbc: db.Config{},
}
func NewClient(dbc db.Config) Client {
return Client{dbc: dbc}
}
func (c Client) FillAndFilter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
ignoreUnfixed bool, ignoreFile string, light bool) []types.DetectedVulnerability {
func (c Client) FillInfo(vulns []types.DetectedVulnerability, light bool) {
var err error
var severity dbTypes.Severity
for i := range vulns {
if light {
severity, err = c.dbc.GetSeverity(vulns[i].VulnerabilityID)
vulns[i].Vulnerability.Severity = severity.String()
} else {
vulns[i].Vulnerability, err = c.dbc.GetVulnerability(vulns[i].VulnerabilityID)
}
if err != nil {
log.Logger.Warnf("Error while getting vulnerability details: %s\n", err)
continue
}
}
}
func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
ignoredIDs := getIgnoredIDs(ignoreFile)
var vulnerabilities []types.DetectedVulnerability
for _, vuln := range vulns {
var vulnerability dbTypes.Vulnerability
if light {
severity, err = c.dbc.GetSeverity(vuln.VulnerabilityID)
vulnerability.Severity = severity.String()
} else {
vulnerability, err = c.dbc.GetVulnerability(vuln.VulnerabilityID)
}
if err != nil {
log.Logger.Warn(err)
continue
}
// Filter vulnerabilities by severity
for _, s := range severities {
if s.String() == vulnerability.Severity {
vuln.Vulnerability = vulnerability
if s.String() == vuln.Severity {
// Ignore unfixed vulnerabilities
if ignoreUnfixed && vuln.FixedVersion == "" {
continue

View File

@@ -0,0 +1,35 @@
package vulnerability
import (
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/mock"
)
type MockVulnClient struct {
mock.Mock
}
func NewMockVulnClient() *MockVulnClient {
mockVulnClient := new(MockVulnClient)
mockVulnClient.On("FillInfo", mock.Anything, mock.Anything)
return mockVulnClient
}
func (_m *MockVulnClient) FillInfo(a []types.DetectedVulnerability, b bool) {
_m.Called(a, b)
}
func (_m *MockVulnClient) Filter(a []types.DetectedVulnerability, b []dbTypes.Severity,
c bool, d string) []types.DetectedVulnerability {
ret := _m.Called(a, b, c, d)
ret0 := ret.Get(0)
if ret0 == nil {
return nil
}
vulns, ok := ret0.([]types.DetectedVulnerability)
if !ok {
return nil
}
return vulns
}

View File

@@ -1,190 +1,386 @@
package vulnerability
import (
"os"
"testing"
"github.com/aquasecurity/trivy/pkg/log"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestFillAndFilter(t *testing.T) {
detectedVulns := []types.DetectedVulnerability{
{
VulnerabilityID: "foo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityHigh],
},
},
{
VulnerabilityID: "piyo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityCritical],
},
},
{
VulnerabilityID: "bar",
PkgName: "barpkg",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityLow],
},
},
{
VulnerabilityID: "hoge",
},
{
VulnerabilityID: "baz",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityMedium],
},
},
func TestMain(m *testing.M) {
if err := log.InitLogger(false, true); err != nil {
log.Fatal(err)
}
severities := []dbTypes.Severity{dbTypes.SeverityLow, dbTypes.SeverityCritical,
dbTypes.SeverityMedium, dbTypes.SeverityHigh, dbTypes.SeverityUnknown}
mockDBConfig := new(db.MockDBConfig)
getVulnerability := map[string]dbTypes.Vulnerability{
"foo": {
Title: "footitle",
Description: "foodesc",
Severity: dbTypes.SeverityHigh.String(),
References: []string{"fooref"},
},
"bar": {
Title: "bartitle",
Description: "bardesc",
Severity: dbTypes.SeverityLow.String(),
References: []string{"barref"},
},
"baz": {
Title: "baztitle",
Description: "bazdesc",
Severity: dbTypes.SeverityMedium.String(),
References: []string{"bazref"},
},
"piyo": {
Title: "piyotitle",
Description: "piyodesc",
Severity: dbTypes.SeverityCritical.String(),
References: []string{"piyoref"},
},
"hoge": {
Title: "hogetitle",
Description: "hogedesc",
Severity: dbTypes.SeverityUnknown.String(),
References: []string{"hogeref"},
},
}
for pkgName, vulnerability := range getVulnerability {
mockDBConfig.On("GetVulnerability", pkgName).Return(vulnerability, nil)
}
getSeverity := map[string]dbTypes.Severity{
"foo": dbTypes.SeverityHigh,
"bar": dbTypes.SeverityLow,
"baz": dbTypes.SeverityMedium,
"piyo": dbTypes.SeverityCritical,
"hoge": dbTypes.SeverityUnknown,
}
for pkgName, severity := range getSeverity {
mockDBConfig.On("GetSeverity", pkgName).Return(severity, nil)
}
expected := []types.DetectedVulnerability{
{
VulnerabilityID: "piyo",
Vulnerability: dbTypes.Vulnerability{
Title: "piyotitle",
Description: "piyodesc",
Severity: dbTypes.SeverityNames[dbTypes.SeverityCritical],
References: []string{"piyoref"},
},
},
{
VulnerabilityID: "foo",
Vulnerability: dbTypes.Vulnerability{
Title: "footitle",
Description: "foodesc",
Severity: dbTypes.SeverityNames[dbTypes.SeverityHigh],
References: []string{"fooref"},
},
},
{
VulnerabilityID: "baz",
Vulnerability: dbTypes.Vulnerability{
Title: "baztitle",
Description: "bazdesc",
Severity: dbTypes.SeverityNames[dbTypes.SeverityMedium],
References: []string{"bazref"},
},
},
{
VulnerabilityID: "hoge",
Vulnerability: dbTypes.Vulnerability{
Title: "hogetitle",
Description: "hogedesc",
Severity: dbTypes.SeverityNames[dbTypes.SeverityUnknown],
References: []string{"hogeref"},
},
},
{
VulnerabilityID: "bar",
PkgName: "barpkg",
Vulnerability: dbTypes.Vulnerability{
Title: "bartitle",
Description: "bardesc",
Severity: dbTypes.SeverityNames[dbTypes.SeverityLow],
References: []string{"barref"},
},
},
}
client := Client{
dbc: mockDBConfig,
}
actual := client.FillAndFilter(detectedVulns, severities, false, ".trivyignore", false)
assert.Equal(t, expected, actual, "full db")
expected = []types.DetectedVulnerability{
{
VulnerabilityID: "piyo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityCritical],
},
},
{
VulnerabilityID: "foo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityHigh],
},
},
{
VulnerabilityID: "baz",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityMedium],
},
},
{
VulnerabilityID: "hoge",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityUnknown],
},
},
{
VulnerabilityID: "bar",
PkgName: "barpkg",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityNames[dbTypes.SeverityLow],
},
},
}
actual = client.FillAndFilter(detectedVulns, severities, false, ".trivyignore", true)
assert.Equal(t, expected, actual, "light db")
code := m.Run()
os.Exit(code)
}
func TestClient_FillInfo(t *testing.T) {
type getSeverityOutput struct {
severity dbTypes.Severity
err error
}
type getSeverity struct {
input string
output getSeverityOutput
}
type getVulnerabilityOutput struct {
vulnerability dbTypes.Vulnerability
err error
}
type getVulnerability struct {
input string
output getVulnerabilityOutput
}
type args struct {
vulns []types.DetectedVulnerability
light bool
}
tests := []struct {
name string
getSeverity []getSeverity
getVulnerability []getVulnerability
args args
expected []types.DetectedVulnerability
}{
{
name: "happy path",
getVulnerability: []getVulnerability{
{
input: "CVE-2019-0001",
output: getVulnerabilityOutput{
vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityMedium.String(),
References: []string{"http://example.com"},
},
},
},
},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
},
light: false,
},
expected: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityMedium.String(),
References: []string{"http://example.com"},
},
},
},
},
{
name: "happy path with light option",
getSeverity: []getSeverity{
{
input: "CVE-2019-0001",
output: getSeverityOutput{
severity: dbTypes.SeverityCritical,
},
},
{
input: "CVE-2019-0002",
output: getSeverityOutput{
severity: dbTypes.SeverityHigh,
},
},
},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
{VulnerabilityID: "CVE-2019-0002"},
},
light: true,
},
expected: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0002",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
},
},
{
name: "GetVulnerability returns an error",
getVulnerability: []getVulnerability{
{
input: "CVE-2019-0004",
output: getVulnerabilityOutput{
err: xerrors.New("failed"),
},
},
},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0004"},
},
light: false,
},
expected: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0004"},
},
},
{
name: "GetSeverity returns an error",
getSeverity: []getSeverity{
{
input: "CVE-2019-0003",
output: getSeverityOutput{
err: xerrors.New("failed"),
},
},
},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0003"},
},
light: true,
},
expected: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0003",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityUnknown.String(),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDBConfig := new(db.MockDBConfig)
c := Client{
dbc: mockDBConfig,
}
for _, gs := range tt.getSeverity {
mockDBConfig.On("GetSeverity", gs.input).Return(
gs.output.severity, gs.output.err)
}
for _, gv := range tt.getVulnerability {
mockDBConfig.On("GetVulnerability", gv.input).Return(
gv.output.vulnerability, gv.output.err)
}
c.FillInfo(tt.args.vulns, tt.args.light)
assert.Equal(t, tt.expected, tt.args.vulns, tt.name)
mockDBConfig.AssertExpectations(t)
})
}
}
func TestClient_Filter(t *testing.T) {
type args struct {
vulns []types.DetectedVulnerability
severities []dbTypes.Severity
ignoreUnfixed bool
ignoreFile string
}
tests := []struct {
name string
args args
want []types.DetectedVulnerability
}{
{
name: "happy path",
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(),
},
},
{
VulnerabilityID: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "baz",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2018-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
},
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh},
ignoreUnfixed: false,
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2018-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "baz",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
},
},
{
name: "happy path with ignore-unfixed",
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(),
},
},
{
VulnerabilityID: "CVE-2018-0002",
PkgName: "bar",
InstalledVersion: "1.2.3",
FixedVersion: "",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
},
severities: []dbTypes.Severity{dbTypes.SeverityHigh},
ignoreUnfixed: true,
},
},
{
name: "happy path with ignore-file",
args: args{
vulns: []types.DetectedVulnerability{
{
// this vulnerability is ignored
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(),
},
},
{
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,
ignoreFile: "testdata/.trivyignore",
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0003",
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)
assert.Equal(t, tt.want, got, tt.name)
})
}
}

508
rpc/detector/service.pb.go Normal file
View File

@@ -0,0 +1,508 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: rpc/detector/service.proto
package detector
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Severity int32
const (
Severity_UNKNOWN Severity = 0
Severity_LOW Severity = 1
Severity_MEDIUM Severity = 2
Severity_HIGH Severity = 3
Severity_CRITICAL Severity = 4
)
var Severity_name = map[int32]string{
0: "UNKNOWN",
1: "LOW",
2: "MEDIUM",
3: "HIGH",
4: "CRITICAL",
}
var Severity_value = map[string]int32{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
func (x Severity) String() string {
return proto.EnumName(Severity_name, int32(x))
}
func (Severity) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{0}
}
type OSDetectRequest struct {
OsFamily string `protobuf:"bytes,1,opt,name=os_family,json=osFamily,proto3" json:"os_family,omitempty"`
OsName string `protobuf:"bytes,2,opt,name=os_name,json=osName,proto3" json:"os_name,omitempty"`
Packages []*Package `protobuf:"bytes,3,rep,name=packages,proto3" json:"packages,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OSDetectRequest) Reset() { *m = OSDetectRequest{} }
func (m *OSDetectRequest) String() string { return proto.CompactTextString(m) }
func (*OSDetectRequest) ProtoMessage() {}
func (*OSDetectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{0}
}
func (m *OSDetectRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OSDetectRequest.Unmarshal(m, b)
}
func (m *OSDetectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OSDetectRequest.Marshal(b, m, deterministic)
}
func (m *OSDetectRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_OSDetectRequest.Merge(m, src)
}
func (m *OSDetectRequest) XXX_Size() int {
return xxx_messageInfo_OSDetectRequest.Size(m)
}
func (m *OSDetectRequest) XXX_DiscardUnknown() {
xxx_messageInfo_OSDetectRequest.DiscardUnknown(m)
}
var xxx_messageInfo_OSDetectRequest proto.InternalMessageInfo
func (m *OSDetectRequest) GetOsFamily() string {
if m != nil {
return m.OsFamily
}
return ""
}
func (m *OSDetectRequest) GetOsName() string {
if m != nil {
return m.OsName
}
return ""
}
func (m *OSDetectRequest) GetPackages() []*Package {
if m != nil {
return m.Packages
}
return nil
}
type DetectResponse struct {
Vulnerabilities []*Vulnerability `protobuf:"bytes,1,rep,name=vulnerabilities,proto3" json:"vulnerabilities,omitempty"`
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DetectResponse) Reset() { *m = DetectResponse{} }
func (m *DetectResponse) String() string { return proto.CompactTextString(m) }
func (*DetectResponse) ProtoMessage() {}
func (*DetectResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{1}
}
func (m *DetectResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DetectResponse.Unmarshal(m, b)
}
func (m *DetectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DetectResponse.Marshal(b, m, deterministic)
}
func (m *DetectResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DetectResponse.Merge(m, src)
}
func (m *DetectResponse) XXX_Size() int {
return xxx_messageInfo_DetectResponse.Size(m)
}
func (m *DetectResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DetectResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DetectResponse proto.InternalMessageInfo
func (m *DetectResponse) GetVulnerabilities() []*Vulnerability {
if m != nil {
return m.Vulnerabilities
}
return nil
}
func (m *DetectResponse) GetEosl() bool {
if m != nil {
return m.Eosl
}
return false
}
type Package struct {
// binary package
// e.g. bind-utils
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
Release string `protobuf:"bytes,3,opt,name=release,proto3" json:"release,omitempty"`
Epoch int32 `protobuf:"varint,4,opt,name=epoch,proto3" json:"epoch,omitempty"`
Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"`
// src package containing some binary packages
// e.g. bind
SrcName string `protobuf:"bytes,6,opt,name=src_name,json=srcName,proto3" json:"src_name,omitempty"`
SrcVersion string `protobuf:"bytes,7,opt,name=src_version,json=srcVersion,proto3" json:"src_version,omitempty"`
SrcRelease string `protobuf:"bytes,8,opt,name=src_release,json=srcRelease,proto3" json:"src_release,omitempty"`
SrcEpoch int32 `protobuf:"varint,9,opt,name=src_epoch,json=srcEpoch,proto3" json:"src_epoch,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Package) Reset() { *m = Package{} }
func (m *Package) String() string { return proto.CompactTextString(m) }
func (*Package) ProtoMessage() {}
func (*Package) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{2}
}
func (m *Package) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Package.Unmarshal(m, b)
}
func (m *Package) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Package.Marshal(b, m, deterministic)
}
func (m *Package) XXX_Merge(src proto.Message) {
xxx_messageInfo_Package.Merge(m, src)
}
func (m *Package) XXX_Size() int {
return xxx_messageInfo_Package.Size(m)
}
func (m *Package) XXX_DiscardUnknown() {
xxx_messageInfo_Package.DiscardUnknown(m)
}
var xxx_messageInfo_Package proto.InternalMessageInfo
func (m *Package) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Package) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Package) GetRelease() string {
if m != nil {
return m.Release
}
return ""
}
func (m *Package) GetEpoch() int32 {
if m != nil {
return m.Epoch
}
return 0
}
func (m *Package) GetArch() string {
if m != nil {
return m.Arch
}
return ""
}
func (m *Package) GetSrcName() string {
if m != nil {
return m.SrcName
}
return ""
}
func (m *Package) GetSrcVersion() string {
if m != nil {
return m.SrcVersion
}
return ""
}
func (m *Package) GetSrcRelease() string {
if m != nil {
return m.SrcRelease
}
return ""
}
func (m *Package) GetSrcEpoch() int32 {
if m != nil {
return m.SrcEpoch
}
return 0
}
type LibDetectRequest struct {
FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
Libraries []*Library `protobuf:"bytes,2,rep,name=libraries,proto3" json:"libraries,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LibDetectRequest) Reset() { *m = LibDetectRequest{} }
func (m *LibDetectRequest) String() string { return proto.CompactTextString(m) }
func (*LibDetectRequest) ProtoMessage() {}
func (*LibDetectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{3}
}
func (m *LibDetectRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LibDetectRequest.Unmarshal(m, b)
}
func (m *LibDetectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_LibDetectRequest.Marshal(b, m, deterministic)
}
func (m *LibDetectRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_LibDetectRequest.Merge(m, src)
}
func (m *LibDetectRequest) XXX_Size() int {
return xxx_messageInfo_LibDetectRequest.Size(m)
}
func (m *LibDetectRequest) XXX_DiscardUnknown() {
xxx_messageInfo_LibDetectRequest.DiscardUnknown(m)
}
var xxx_messageInfo_LibDetectRequest proto.InternalMessageInfo
func (m *LibDetectRequest) GetFilePath() string {
if m != nil {
return m.FilePath
}
return ""
}
func (m *LibDetectRequest) GetLibraries() []*Library {
if m != nil {
return m.Libraries
}
return nil
}
type Library struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Library) Reset() { *m = Library{} }
func (m *Library) String() string { return proto.CompactTextString(m) }
func (*Library) ProtoMessage() {}
func (*Library) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{4}
}
func (m *Library) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Library.Unmarshal(m, b)
}
func (m *Library) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Library.Marshal(b, m, deterministic)
}
func (m *Library) XXX_Merge(src proto.Message) {
xxx_messageInfo_Library.Merge(m, src)
}
func (m *Library) XXX_Size() int {
return xxx_messageInfo_Library.Size(m)
}
func (m *Library) XXX_DiscardUnknown() {
xxx_messageInfo_Library.DiscardUnknown(m)
}
var xxx_messageInfo_Library proto.InternalMessageInfo
func (m *Library) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Library) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
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.detector.Severity" json:"severity,omitempty"`
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
func (m *Vulnerability) String() string { return proto.CompactTextString(m) }
func (*Vulnerability) ProtoMessage() {}
func (*Vulnerability) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{5}
}
func (m *Vulnerability) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Vulnerability.Unmarshal(m, b)
}
func (m *Vulnerability) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Vulnerability.Marshal(b, m, deterministic)
}
func (m *Vulnerability) XXX_Merge(src proto.Message) {
xxx_messageInfo_Vulnerability.Merge(m, src)
}
func (m *Vulnerability) XXX_Size() int {
return xxx_messageInfo_Vulnerability.Size(m)
}
func (m *Vulnerability) XXX_DiscardUnknown() {
xxx_messageInfo_Vulnerability.DiscardUnknown(m)
}
var xxx_messageInfo_Vulnerability proto.InternalMessageInfo
func (m *Vulnerability) GetVulnerabilityId() string {
if m != nil {
return m.VulnerabilityId
}
return ""
}
func (m *Vulnerability) GetPkgName() string {
if m != nil {
return m.PkgName
}
return ""
}
func (m *Vulnerability) GetInstalledVersion() string {
if m != nil {
return m.InstalledVersion
}
return ""
}
func (m *Vulnerability) GetFixedVersion() string {
if m != nil {
return m.FixedVersion
}
return ""
}
func (m *Vulnerability) GetTitle() string {
if m != nil {
return m.Title
}
return ""
}
func (m *Vulnerability) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *Vulnerability) GetSeverity() Severity {
if m != nil {
return m.Severity
}
return Severity_UNKNOWN
}
func (m *Vulnerability) GetReferences() []string {
if m != nil {
return m.References
}
return nil
}
func init() {
proto.RegisterEnum("trivy.detector.Severity", Severity_name, Severity_value)
proto.RegisterType((*OSDetectRequest)(nil), "trivy.detector.OSDetectRequest")
proto.RegisterType((*DetectResponse)(nil), "trivy.detector.DetectResponse")
proto.RegisterType((*Package)(nil), "trivy.detector.Package")
proto.RegisterType((*LibDetectRequest)(nil), "trivy.detector.LibDetectRequest")
proto.RegisterType((*Library)(nil), "trivy.detector.Library")
proto.RegisterType((*Vulnerability)(nil), "trivy.detector.Vulnerability")
}
func init() { proto.RegisterFile("rpc/detector/service.proto", fileDescriptor_93e16dbd737b8924) }
var fileDescriptor_93e16dbd737b8924 = []byte{
// 618 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x4f, 0xdb, 0x40,
0x10, 0x6d, 0x3e, 0xed, 0x4c, 0xf8, 0x70, 0x57, 0x48, 0xb8, 0xa0, 0x42, 0x94, 0x5e, 0x68, 0x2b,
0x05, 0x29, 0xb4, 0xea, 0xb9, 0x05, 0x0a, 0x69, 0x43, 0x40, 0xa6, 0x80, 0xda, 0x4b, 0xb4, 0x71,
0x26, 0x64, 0x85, 0x93, 0x75, 0x77, 0x97, 0xa8, 0x96, 0xfa, 0xb7, 0xfa, 0xd3, 0x7a, 0xaf, 0x76,
0xd7, 0x36, 0x49, 0xca, 0x85, 0xdb, 0xcc, 0xbc, 0xe7, 0x99, 0xd9, 0xf7, 0xd6, 0x0b, 0x5b, 0x22,
0x0e, 0xf7, 0x87, 0xa8, 0x30, 0x54, 0x5c, 0xec, 0x4b, 0x14, 0x33, 0x16, 0x62, 0x2b, 0x16, 0x5c,
0x71, 0xb2, 0xa6, 0x04, 0x9b, 0x25, 0xad, 0x0c, 0x6d, 0xfe, 0x86, 0xf5, 0xf3, 0xcb, 0x23, 0x93,
0x05, 0xf8, 0xf3, 0x1e, 0xa5, 0x22, 0xdb, 0x50, 0xe3, 0xb2, 0x3f, 0xa2, 0x13, 0x16, 0x25, 0x7e,
0xa1, 0x51, 0xd8, 0xab, 0x05, 0x2e, 0x97, 0x9f, 0x4d, 0x4e, 0x36, 0xc1, 0xe1, 0xb2, 0x3f, 0xa5,
0x13, 0xf4, 0x8b, 0x06, 0xaa, 0x72, 0xd9, 0xa3, 0x13, 0x24, 0x07, 0xe0, 0xc6, 0x34, 0xbc, 0xa3,
0xb7, 0x28, 0xfd, 0x52, 0xa3, 0xb4, 0x57, 0x6f, 0x6f, 0xb6, 0x16, 0x67, 0xb5, 0x2e, 0x2c, 0x1e,
0xe4, 0xc4, 0xe6, 0x04, 0xd6, 0xb2, 0xd9, 0x32, 0xe6, 0x53, 0x89, 0xe4, 0x04, 0xd6, 0x67, 0xf7,
0xd1, 0x14, 0x05, 0x1d, 0xb0, 0x88, 0x29, 0x86, 0xd2, 0x2f, 0x98, 0x6e, 0x2f, 0x97, 0xbb, 0x5d,
0xcf, 0xd1, 0x92, 0x60, 0xf9, 0x2b, 0x42, 0xa0, 0x8c, 0x5c, 0x46, 0x66, 0x4b, 0x37, 0x30, 0x71,
0xf3, 0x6f, 0x01, 0x9c, 0x74, 0x09, 0x8d, 0x9b, 0x53, 0xd8, 0x03, 0x9a, 0x98, 0xf8, 0xe0, 0xcc,
0x50, 0x48, 0xc6, 0xa7, 0xe9, 0xe1, 0xb2, 0x54, 0x23, 0x02, 0x23, 0xa4, 0x12, 0xfd, 0x92, 0x45,
0xd2, 0x94, 0x6c, 0x40, 0x05, 0x63, 0x1e, 0x8e, 0xfd, 0x72, 0xa3, 0xb0, 0x57, 0x09, 0x6c, 0xa2,
0xbb, 0x53, 0x11, 0x8e, 0xfd, 0x8a, 0xed, 0xae, 0x63, 0xf2, 0x02, 0x5c, 0x29, 0x42, 0xab, 0x5d,
0xd5, 0x36, 0x91, 0x22, 0x34, 0xe2, 0xed, 0x42, 0x5d, 0x43, 0xd9, 0x70, 0xc7, 0xa0, 0x20, 0x45,
0x78, 0x9d, 0xce, 0x4f, 0x09, 0xd9, 0x0e, 0x6e, 0x4e, 0x08, 0xd2, 0x35, 0xb6, 0xa1, 0xa6, 0x09,
0x76, 0x95, 0x9a, 0x59, 0x45, 0x4f, 0x3b, 0xd6, 0x79, 0x73, 0x04, 0x5e, 0x97, 0x0d, 0xfe, 0x73,
0x79, 0xc4, 0x22, 0xec, 0xc7, 0x54, 0x8d, 0x33, 0x97, 0x75, 0xe1, 0x82, 0xaa, 0x31, 0x79, 0x0f,
0xb5, 0x88, 0x0d, 0x04, 0x15, 0x5a, 0xff, 0xe2, 0xe3, 0x6e, 0x76, 0x0d, 0x21, 0x09, 0x1e, 0x98,
0xcd, 0x0f, 0xe0, 0xa4, 0xd5, 0xa7, 0xc9, 0xdb, 0xfc, 0x53, 0x84, 0xd5, 0x05, 0x3f, 0xc9, 0x6b,
0xf0, 0xe6, 0x1d, 0x4d, 0xfa, 0x6c, 0x98, 0xf6, 0x5a, 0x70, 0x3a, 0xe9, 0x0c, 0xb5, 0xae, 0xf1,
0xdd, 0xed, 0xfc, 0x9d, 0x74, 0xe2, 0xbb, 0x5b, 0xa3, 0xeb, 0x5b, 0x78, 0xce, 0xa6, 0x52, 0xd1,
0x28, 0xc2, 0x61, 0xae, 0xae, 0x35, 0xd0, 0xcb, 0x81, 0x4c, 0xe3, 0x57, 0xb0, 0x3a, 0x62, 0xbf,
0xe6, 0x88, 0x65, 0x43, 0x5c, 0x31, 0xc5, 0x8c, 0xb4, 0x01, 0x15, 0xc5, 0x54, 0x84, 0xa9, 0xb3,
0x36, 0x21, 0x0d, 0xa8, 0x0f, 0x51, 0x86, 0x82, 0xc5, 0x4a, 0x7f, 0x68, 0xdd, 0x9d, 0x2f, 0x91,
0x77, 0xe0, 0x4a, 0x9c, 0xa1, 0x60, 0x2a, 0x31, 0xf6, 0xae, 0xb5, 0xfd, 0x65, 0x41, 0x2f, 0x53,
0x3c, 0xc8, 0x99, 0x64, 0x07, 0x40, 0xe0, 0x08, 0x05, 0x4e, 0x43, 0x94, 0xbe, 0xdb, 0x28, 0x69,
0xd7, 0x1f, 0x2a, 0x6f, 0x8e, 0xc0, 0xcd, 0xbe, 0x22, 0x75, 0x70, 0xae, 0x7a, 0x5f, 0x7b, 0xe7,
0x37, 0x3d, 0xef, 0x19, 0x71, 0xa0, 0xd4, 0x3d, 0xbf, 0xf1, 0x0a, 0x04, 0xa0, 0x7a, 0x76, 0x7c,
0xd4, 0xb9, 0x3a, 0xf3, 0x8a, 0xc4, 0x85, 0xf2, 0x69, 0xe7, 0xe4, 0xd4, 0x2b, 0x91, 0x15, 0x70,
0x0f, 0x83, 0xce, 0xb7, 0xce, 0xe1, 0xc7, 0xae, 0x57, 0x6e, 0xdf, 0x00, 0x64, 0x6f, 0x00, 0x17,
0xa4, 0x03, 0x55, 0x1b, 0x93, 0xdd, 0xe5, 0x0d, 0x97, 0x5e, 0x8a, 0xad, 0x9d, 0x65, 0xc2, 0xe2,
0xcf, 0xdc, 0xfe, 0x0e, 0xf5, 0xfc, 0xde, 0x71, 0x41, 0xbe, 0xe4, 0x9d, 0x1b, 0x8f, 0x5c, 0xa6,
0x27, 0xb5, 0xfe, 0x04, 0x3f, 0xdc, 0x0c, 0x1a, 0x54, 0xcd, 0xd3, 0x76, 0xf0, 0x2f, 0x00, 0x00,
0xff, 0xff, 0xde, 0x1c, 0x85, 0x42, 0xf8, 0x04, 0x00, 0x00,
}

View File

@@ -0,0 +1,68 @@
syntax = "proto3";
package trivy.detector;
option go_package = "detector";
service OSDetector {
rpc Detect(OSDetectRequest) returns (DetectResponse);
}
message OSDetectRequest {
string os_family = 1;
string os_name = 2;
repeated Package packages = 3;
}
message DetectResponse {
repeated Vulnerability vulnerabilities = 1;
bool eosl = 2;
}
message Package {
// binary package
// e.g. bind-utils
string name = 1;
string version = 2;
string release = 3;
int32 epoch = 4;
string arch = 5;
// src package containing some binary packages
// e.g. bind
string src_name = 6;
string src_version = 7;
string src_release = 8;
int32 src_epoch = 9;
}
service LibDetector {
rpc Detect(LibDetectRequest) returns (DetectResponse);
}
message LibDetectRequest {
string file_path = 1;
repeated Library libraries = 2;
}
message Library {
string name = 1;
string version = 2;
}
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;
}
enum Severity {
UNKNOWN = 0;
LOW = 1;
MEDIUM = 2;
HIGH = 3;
CRITICAL = 4;
}

File diff suppressed because it is too large Load Diff