mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-06 04:41:18 -08:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42043a0888 | ||
|
|
246793e873 | ||
|
|
692b0f1410 | ||
|
|
0629e1d731 | ||
|
|
9707c7bcb1 | ||
|
|
194fbef73c | ||
|
|
f7db00c1eb | ||
|
|
2f4b31ecc6 | ||
|
|
9289624688 | ||
|
|
5a8749cd5b | ||
|
|
4a7fb525d7 | ||
|
|
8888fcafa7 | ||
|
|
63a8c6d26b | ||
|
|
fc222bed7c | ||
|
|
6132ff93a2 | ||
|
|
87556aa741 | ||
|
|
43362b2832 | ||
|
|
db2d0c2e9b | ||
|
|
922d493159 | ||
|
|
c4811c3104 | ||
|
|
0ec840b3b4 | ||
|
|
0b96d08877 | ||
|
|
7abd41609f | ||
|
|
fcc193b7d1 | ||
|
|
44d74a7d8a | ||
|
|
4189855fc1 | ||
|
|
77f1abc17d | ||
|
|
96d58ccd1b | ||
|
|
823374b578 | ||
|
|
b127c1c8a7 | ||
|
|
b1ea09d901 | ||
|
|
8c1c3df232 | ||
|
|
cee08c38f4 | ||
|
|
bc8f613ba6 | ||
|
|
af584a8517 | ||
|
|
1805a956a3 | ||
|
|
74717b888e | ||
|
|
24fc88ced2 | ||
|
|
1e9dcdb7d1 | ||
|
|
7233b5f419 | ||
|
|
b9eddafe71 | ||
|
|
3a53a88139 | ||
|
|
6cbbb22ab4 | ||
|
|
b6a8af5b20 | ||
|
|
30c1a00225 | ||
|
|
b345342369 | ||
|
|
438680f3e4 | ||
|
|
740c2c4069 | ||
|
|
bdd1266087 |
@@ -1,3 +1,5 @@
|
||||
version: 2.1
|
||||
|
||||
defaults: &defaults
|
||||
docker :
|
||||
- image: aquasec/trivy-ci:latest
|
||||
@@ -5,7 +7,7 @@ defaults: &defaults
|
||||
CGO_ENABLED: "0"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
unit-test:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
@@ -22,6 +24,15 @@ jobs:
|
||||
- run:
|
||||
name: Test
|
||||
command: make test
|
||||
integration-test:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
docker_version:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: << parameters.docker_version >>
|
||||
- run:
|
||||
name: Integration Test
|
||||
command: make test-integration
|
||||
@@ -54,12 +65,14 @@ jobs:
|
||||
- run:
|
||||
name: Create deb repository
|
||||
command: ci/deploy-deb.sh
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
release:
|
||||
jobs:
|
||||
- test
|
||||
jobs:
|
||||
- unit-test
|
||||
- integration-test:
|
||||
docker_version: 18.09.3
|
||||
- release:
|
||||
filters:
|
||||
branches:
|
||||
|
||||
5
.clang-format
Normal file
5
.clang-format
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
Language: Proto
|
||||
BasedOnStyle: Google
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
@@ -1,5 +1,5 @@
|
||||
FROM alpine:3.10
|
||||
FROM alpine:3.11
|
||||
RUN apk --no-cache add ca-certificates git rpm
|
||||
COPY trivy /usr/local/bin/trivy
|
||||
|
||||
COPY contrib/gitlab.tpl contrib/gitlab.tpl
|
||||
ENTRYPOINT ["trivy"]
|
||||
|
||||
16
Makefile
16
Makefile
@@ -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
|
||||
|
||||
214
README.md
214
README.md
@@ -22,27 +22,34 @@ 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)
|
||||
- [Save the results using a template](#save-the-results-using-a-template)
|
||||
- [Filter the vulnerabilities by severities](#filter-the-vulnerabilities-by-severities)
|
||||
- [Filter the vulnerabilities by type](#filter-the-vulnerabilities-by-type)
|
||||
- [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)
|
||||
- [Authentication](#authentication)
|
||||
- [Continuous Integration (CI)](#continuous-integration-ci)
|
||||
- [Travis CI](#travis-ci)
|
||||
- [CircleCI](#circleci)
|
||||
- [GitLab CI](#gitlab)
|
||||
- [GitLab CI](#gitlab-ci)
|
||||
- [Authorization for Private Docker Registry](#authorization-for-private-docker-registry)
|
||||
- [Vulnerability Detection](#vulnerability-detection)
|
||||
- [OS Packages](#os-packages)
|
||||
@@ -71,7 +78,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, openSUSE Leap, SUSE Enterprise Linux, Photon OS and Distroless)
|
||||
- **Application dependencies** (Bundler, Composer, Pipenv, Poetry, npm, yarn and Cargo)
|
||||
- Simple
|
||||
- Specify only an image name
|
||||
@@ -89,6 +96,8 @@ See [here](#continuous-integration-ci) for details.
|
||||
- **Suitable for CI** such as Travis CI, CircleCI, Jenkins, etc.
|
||||
- See [CI Example](#continuous-integration-ci)
|
||||
|
||||
Please see [LICENSE](https://github.com/aquasecurity/trivy/blob/master/LICENSE) for Trivy licensing information. Note that Trivy uses vulnerability information from a variety of sources, some of which are licensed for non-commercial use only.
|
||||
|
||||
# Installation
|
||||
|
||||
## RHEL/CentOS
|
||||
@@ -152,7 +161,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 +263,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).
|
||||
@@ -644,6 +655,25 @@ $ trivy -f json -o results.json golang:1.12-alpine
|
||||
|
||||
</details>
|
||||
|
||||
### Save the results using a template
|
||||
|
||||
```
|
||||
$ trivy --format template --template "{{ range . }} {{ .Target }} {{ end }}" golang:1.12-alpine
|
||||
```
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
```
|
||||
2020-01-02T18:02:32.856+0100 INFO Detecting Alpine vulnerabilities...
|
||||
golang:1.12-alpine (alpine 3.10.2)
|
||||
```
|
||||
</details>
|
||||
|
||||
You can load templates from a file prefixing the template path with an @.
|
||||
|
||||
```
|
||||
$ trivy --format template --template "@/path/to/template" golang:1.12-alpine
|
||||
```
|
||||
|
||||
### Filter the vulnerabilities by severities
|
||||
|
||||
```
|
||||
@@ -1078,6 +1108,61 @@ 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...
|
||||
```
|
||||
|
||||
If you want to accept a connection from outside, you have to specify `0.0.0.0` or your ip address, not `localhost`.
|
||||
|
||||
```
|
||||
$ trivy server --listen 0.0.0.0: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>
|
||||
|
||||
### Authentication
|
||||
|
||||
```
|
||||
$ trivy server --listen localhost:8080 --token dummy
|
||||
```
|
||||
|
||||
```
|
||||
$ trivy client --remote http://localhost:8080 --token dummy alpine:3.10
|
||||
```
|
||||
|
||||
### Deprecated options
|
||||
|
||||
`--only-update`, `--refresh` and `--auto-refresh` are deprecated since they are unnecessary now. These options will be removed at the next version
|
||||
@@ -1154,7 +1239,7 @@ workflows:
|
||||
Example: https://circleci.com/gh/aquasecurity/trivy-ci-test
|
||||
Repository: https://github.com/aquasecurity/trivy-ci-test
|
||||
|
||||
## GitLab
|
||||
## GitLab CI
|
||||
|
||||
```
|
||||
$ cat .gitlab-ci.yml
|
||||
@@ -1163,24 +1248,41 @@ stages:
|
||||
|
||||
trivy:
|
||||
stage: test
|
||||
image: docker:stable-git
|
||||
image: docker:stable
|
||||
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: ""
|
||||
IMAGE: trivy-ci-test:$CI_COMMIT_SHA
|
||||
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}
|
||||
# Build image
|
||||
- docker build -t $IMAGE .
|
||||
# Build report
|
||||
- ./trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $IMAGE
|
||||
# Print report
|
||||
- ./trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --severity HIGH $IMAGE
|
||||
# Fail on high and critical vulnerabilities
|
||||
- ./trivy --exit-code 1 --cache-dir .trivycache/ --severity CRITICAL --no-progress $IMAGE
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/trivy
|
||||
```
|
||||
paths:
|
||||
- .trivycache/
|
||||
# Enables https://docs.gitlab.com/ee/user/application_security/container_scanning/ (Container Scanning report is available on GitLab EE Ultimate or GitLab.com Gold)
|
||||
artifacts:
|
||||
reports:
|
||||
container_scanning: gl-container-scanning-report.json
|
||||
```
|
||||
|
||||
## Authorization for Private Docker Registry
|
||||
|
||||
@@ -1240,12 +1342,16 @@ 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 |
|
||||
| openSUSE Leap | 42, 15 | Installed by zypper/rpm | NO |
|
||||
| SUSE Enterprise Linux | 11, 12, 15 | Installed by zypper/rpm | NO |
|
||||
| Photon OS | 1.0, 2.0, 3.0 | Installed by tdnf/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, SUSE, Amazon Linux and Photon OS 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
|
||||
|
||||
@@ -1289,6 +1395,7 @@ Trivy scans a tar image with the following format.
|
||||
- https://github.com/RustSec/advisory-db
|
||||
|
||||
# Usage
|
||||
## Standalone
|
||||
|
||||
```
|
||||
NAME:
|
||||
@@ -1325,6 +1432,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
|
||||
@@ -1429,7 +1583,7 @@ $ brew untap knqyf263/trivy
|
||||
$ brew install aquasecurity/trivy/trivy
|
||||
```
|
||||
|
||||
## Binary (Including Windows)
|
||||
## Binary
|
||||
No need to fix.
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM circleci/golang:1.13-buster
|
||||
RUN sudo apt-get -y update \
|
||||
&& sudo apt-get -y install rpm reprepro createrepo
|
||||
|
||||
ARG GORELEASER_VERSION=0.110.0
|
||||
ARG GORELEASER_VERSION=0.124.1
|
||||
ARG GORELEASER_ARTIFACT=goreleaser_Linux_x86_64.tar.gz
|
||||
RUN wget https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}/${GORELEASER_ARTIFACT} \
|
||||
&& sudo tar -xzf ${GORELEASER_ARTIFACT} -C /usr/bin/ goreleaser \
|
||||
|
||||
@@ -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 {
|
||||
|
||||
29
contrib/Trivy.gitlab-ci.yml
Normal file
29
contrib/Trivy.gitlab-ci.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
Trivy_container_scanning:
|
||||
stage: test
|
||||
image:
|
||||
name: alpine:3.11
|
||||
variables:
|
||||
# Override the GIT_STRATEGY variable in your `.gitlab-ci.yml` file and set it to `fetch` if you want to provide a `clair-whitelist.yml`
|
||||
# file. See https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
|
||||
# for details
|
||||
GIT_STRATEGY: none
|
||||
IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
|
||||
allow_failure: true
|
||||
before_script:
|
||||
- export TRIVY_VERSION=${TRIVY_VERSION:-v0.4.3}
|
||||
- apk add --no-cache curl docker-cli
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
- curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION}
|
||||
- curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl
|
||||
script:
|
||||
- trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE
|
||||
cache:
|
||||
paths:
|
||||
- .trivycache/
|
||||
artifacts:
|
||||
reports:
|
||||
container_scanning: gl-container-scanning-report.json
|
||||
dependencies: []
|
||||
only:
|
||||
refs:
|
||||
- branches
|
||||
80
contrib/gitlab.tpl
Normal file
80
contrib/gitlab.tpl
Normal file
@@ -0,0 +1,80 @@
|
||||
{{- /* Template based on https://docs.gitlab.com/ee/user/application_security/container_scanning/#reports-json-format */ -}}
|
||||
{
|
||||
"version": "2.3",
|
||||
"vulnerabilities": [
|
||||
{{- $first := true }}
|
||||
{{- range . }}
|
||||
{{- $target := .Target }}
|
||||
{{- range .Vulnerabilities -}}
|
||||
{{- if $first -}}
|
||||
{{- $first = false -}}
|
||||
{{ else -}}
|
||||
,
|
||||
{{- end }}
|
||||
{
|
||||
"category": "container_scanning",
|
||||
"message": {{ .Title | printf "%q" }},
|
||||
"description": {{ .Description | printf "%q" }},
|
||||
"cve": "{{ .VulnerabilityID }}",
|
||||
"severity": {{ if eq .Severity "UNKNOWN" -}}
|
||||
"Unknown"
|
||||
{{- else if eq .Severity "LOW" -}}
|
||||
"Low"
|
||||
{{- else if eq .Severity "MEDIUM" -}}
|
||||
"Medium"
|
||||
{{- else if eq .Severity "HIGH" -}}
|
||||
"High"
|
||||
{{- else if eq .Severity "CRITICAL" -}}
|
||||
"Critical"
|
||||
{{- else -}}
|
||||
"{{ .Severity }}"
|
||||
{{- end }},
|
||||
"confidence": "Unknown",
|
||||
"solution": {{ if .FixedVersion -}}
|
||||
"Upgrade {{ .PkgName }} to {{ .FixedVersion }}"
|
||||
{{- else -}}
|
||||
"No solution provided"
|
||||
{{- end }},
|
||||
"scanner": {
|
||||
"id": "trivy",
|
||||
"name": "trivy"
|
||||
},
|
||||
"location": {
|
||||
"dependency": {
|
||||
"package": {
|
||||
"name": "{{ .PkgName }}"
|
||||
},
|
||||
"version": "{{ .InstalledVersion }}"
|
||||
},
|
||||
{{- /* TODO: No mapping available - https://github.com/aquasecurity/trivy/issues/332 */}}
|
||||
"operating_system": "Unknown",
|
||||
"image": "{{ $target }}"
|
||||
},
|
||||
"identifiers": [
|
||||
{
|
||||
{{- /* TODO: Type not extractable - https://github.com/aquasecurity/trivy-db/pull/24 */}}
|
||||
"type": "cve",
|
||||
"name": "{{ .VulnerabilityID }}",
|
||||
"value": "{{ .VulnerabilityID }}",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{{- $first := true -}}
|
||||
{{- range .References -}}
|
||||
{{- if $first -}}
|
||||
{{- $first = false }}
|
||||
{{- else -}}
|
||||
,
|
||||
{{- end -}}
|
||||
{
|
||||
"url": "{{ . }}"
|
||||
}
|
||||
{{- end }}
|
||||
]
|
||||
}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
],
|
||||
"remediations": []
|
||||
}
|
||||
409
contrib/install.sh
Executable file
409
contrib/install.sh
Executable file
@@ -0,0 +1,409 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
# Code generated by godownloader on 2020-01-14T10:03:29Z. DO NOT EDIT.
|
||||
#
|
||||
|
||||
usage() {
|
||||
this=$1
|
||||
cat <<EOF
|
||||
$this: download go binaries for aquasecurity/trivy
|
||||
|
||||
Usage: $this [-b] bindir [-d] [tag]
|
||||
-b sets bindir or installation directory, Defaults to ./bin
|
||||
-d turns on debug logging
|
||||
[tag] is a tag from
|
||||
https://github.com/aquasecurity/trivy/releases
|
||||
If tag is missing, then the latest will be used.
|
||||
|
||||
Generated by godownloader
|
||||
https://github.com/goreleaser/godownloader
|
||||
|
||||
EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
#BINDIR is ./bin unless set be ENV
|
||||
# over-ridden by flag below
|
||||
|
||||
BINDIR=${BINDIR:-./bin}
|
||||
while getopts "b:dh?x" arg; do
|
||||
case "$arg" in
|
||||
b) BINDIR="$OPTARG" ;;
|
||||
d) log_set_priority 10 ;;
|
||||
h | \?) usage "$0" ;;
|
||||
x) set -x ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
TAG=$1
|
||||
}
|
||||
# this function wraps all the destructive operations
|
||||
# if a curl|bash cuts off the end of the script due to
|
||||
# network, either nothing will happen or will syntax error
|
||||
# out preventing half-done work
|
||||
execute() {
|
||||
tmpdir=$(mktemp -d)
|
||||
log_debug "downloading files into ${tmpdir}"
|
||||
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
|
||||
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
|
||||
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
|
||||
srcdir="${tmpdir}"
|
||||
(cd "${tmpdir}" && untar "${TARBALL}")
|
||||
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||
for binexe in $BINARIES; do
|
||||
if [ "$OS" = "windows" ]; then
|
||||
binexe="${binexe}.exe"
|
||||
fi
|
||||
install "${srcdir}/${binexe}" "${BINDIR}/"
|
||||
log_info "installed ${BINDIR}/${binexe}"
|
||||
done
|
||||
rm -rf "${tmpdir}"
|
||||
}
|
||||
get_binaries() {
|
||||
case "$PLATFORM" in
|
||||
darwin/386) BINARIES="trivy" ;;
|
||||
darwin/amd64) BINARIES="trivy" ;;
|
||||
darwin/arm64) BINARIES="trivy" ;;
|
||||
darwin/armv7) BINARIES="trivy" ;;
|
||||
freebsd/386) BINARIES="trivy" ;;
|
||||
freebsd/amd64) BINARIES="trivy" ;;
|
||||
freebsd/arm64) BINARIES="trivy" ;;
|
||||
freebsd/armv7) BINARIES="trivy" ;;
|
||||
linux/386) BINARIES="trivy" ;;
|
||||
linux/amd64) BINARIES="trivy" ;;
|
||||
linux/arm64) BINARIES="trivy" ;;
|
||||
linux/armv7) BINARIES="trivy" ;;
|
||||
openbsd/386) BINARIES="trivy" ;;
|
||||
openbsd/amd64) BINARIES="trivy" ;;
|
||||
openbsd/arm64) BINARIES="trivy" ;;
|
||||
openbsd/armv7) BINARIES="trivy" ;;
|
||||
*)
|
||||
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
tag_to_version() {
|
||||
if [ -z "${TAG}" ]; then
|
||||
log_info "checking GitHub for latest tag"
|
||||
else
|
||||
log_info "checking GitHub for tag '${TAG}'"
|
||||
fi
|
||||
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
|
||||
if test -z "$REALTAG"; then
|
||||
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
|
||||
exit 1
|
||||
fi
|
||||
# if version starts with 'v', remove it
|
||||
TAG="$REALTAG"
|
||||
VERSION=${TAG#v}
|
||||
}
|
||||
adjust_format() {
|
||||
# change format (tar.gz or zip) based on OS
|
||||
true
|
||||
}
|
||||
adjust_os() {
|
||||
# adjust archive name based on OS
|
||||
case ${OS} in
|
||||
386) OS=32bit ;;
|
||||
amd64) OS=64bit ;;
|
||||
arm) OS=ARM ;;
|
||||
arm64) OS=ARM64 ;;
|
||||
darwin) OS=macOS ;;
|
||||
dragonfly) OS=DragonFlyBSD ;;
|
||||
freebsd) OS=FreeBSD ;;
|
||||
linux) OS=Linux ;;
|
||||
netbsd) OS=NetBSD ;;
|
||||
openbsd) OS=OpenBSD ;;
|
||||
esac
|
||||
true
|
||||
}
|
||||
adjust_arch() {
|
||||
# adjust archive name based on ARCH
|
||||
case ${ARCH} in
|
||||
386) ARCH=32bit ;;
|
||||
amd64) ARCH=64bit ;;
|
||||
arm) ARCH=ARM ;;
|
||||
arm64) ARCH=ARM64 ;;
|
||||
darwin) ARCH=macOS ;;
|
||||
dragonfly) ARCH=DragonFlyBSD ;;
|
||||
freebsd) ARCH=FreeBSD ;;
|
||||
linux) ARCH=Linux ;;
|
||||
netbsd) ARCH=NetBSD ;;
|
||||
openbsd) ARCH=OpenBSD ;;
|
||||
esac
|
||||
true
|
||||
}
|
||||
|
||||
cat /dev/null <<EOF
|
||||
------------------------------------------------------------------------
|
||||
https://github.com/client9/shlib - portable posix shell functions
|
||||
Public domain - http://unlicense.org
|
||||
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||
but credit (and pull requests) appreciated.
|
||||
------------------------------------------------------------------------
|
||||
EOF
|
||||
is_command() {
|
||||
command -v "$1" >/dev/null
|
||||
}
|
||||
echoerr() {
|
||||
echo "$@" 1>&2
|
||||
}
|
||||
log_prefix() {
|
||||
echo "$0"
|
||||
}
|
||||
_logp=6
|
||||
log_set_priority() {
|
||||
_logp="$1"
|
||||
}
|
||||
log_priority() {
|
||||
if test -z "$1"; then
|
||||
echo "$_logp"
|
||||
return
|
||||
fi
|
||||
[ "$1" -le "$_logp" ]
|
||||
}
|
||||
log_tag() {
|
||||
case $1 in
|
||||
0) echo "emerg" ;;
|
||||
1) echo "alert" ;;
|
||||
2) echo "crit" ;;
|
||||
3) echo "err" ;;
|
||||
4) echo "warning" ;;
|
||||
5) echo "notice" ;;
|
||||
6) echo "info" ;;
|
||||
7) echo "debug" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
log_debug() {
|
||||
log_priority 7 || return 0
|
||||
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||
}
|
||||
log_info() {
|
||||
log_priority 6 || return 0
|
||||
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||
}
|
||||
log_err() {
|
||||
log_priority 3 || return 0
|
||||
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||
}
|
||||
log_crit() {
|
||||
log_priority 2 || return 0
|
||||
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||
}
|
||||
uname_os() {
|
||||
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
case "$os" in
|
||||
cygwin_nt*) os="windows" ;;
|
||||
mingw*) os="windows" ;;
|
||||
msys_nt*) os="windows" ;;
|
||||
esac
|
||||
echo "$os"
|
||||
}
|
||||
uname_arch() {
|
||||
arch=$(uname -m)
|
||||
case $arch in
|
||||
x86_64) arch="amd64" ;;
|
||||
x86) arch="386" ;;
|
||||
i686) arch="386" ;;
|
||||
i386) arch="386" ;;
|
||||
aarch64) arch="arm64" ;;
|
||||
armv5*) arch="armv5" ;;
|
||||
armv6*) arch="armv6" ;;
|
||||
armv7*) arch="armv7" ;;
|
||||
esac
|
||||
echo ${arch}
|
||||
}
|
||||
uname_os_check() {
|
||||
os=$(uname_os)
|
||||
case "$os" in
|
||||
darwin) return 0 ;;
|
||||
dragonfly) return 0 ;;
|
||||
freebsd) return 0 ;;
|
||||
linux) return 0 ;;
|
||||
android) return 0 ;;
|
||||
nacl) return 0 ;;
|
||||
netbsd) return 0 ;;
|
||||
openbsd) return 0 ;;
|
||||
plan9) return 0 ;;
|
||||
solaris) return 0 ;;
|
||||
windows) return 0 ;;
|
||||
esac
|
||||
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||
return 1
|
||||
}
|
||||
uname_arch_check() {
|
||||
arch=$(uname_arch)
|
||||
case "$arch" in
|
||||
386) return 0 ;;
|
||||
amd64) return 0 ;;
|
||||
arm64) return 0 ;;
|
||||
armv5) return 0 ;;
|
||||
armv6) return 0 ;;
|
||||
armv7) return 0 ;;
|
||||
ppc64) return 0 ;;
|
||||
ppc64le) return 0 ;;
|
||||
mips) return 0 ;;
|
||||
mipsle) return 0 ;;
|
||||
mips64) return 0 ;;
|
||||
mips64le) return 0 ;;
|
||||
s390x) return 0 ;;
|
||||
amd64p32) return 0 ;;
|
||||
esac
|
||||
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||
return 1
|
||||
}
|
||||
untar() {
|
||||
tarball=$1
|
||||
case "${tarball}" in
|
||||
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
|
||||
*.tar) tar --no-same-owner -xf "${tarball}" ;;
|
||||
*.zip) unzip "${tarball}" ;;
|
||||
*)
|
||||
log_err "untar unknown archive format for ${tarball}"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
http_download_curl() {
|
||||
local_file=$1
|
||||
source_url=$2
|
||||
header=$3
|
||||
if [ -z "$header" ]; then
|
||||
code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
|
||||
else
|
||||
code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
|
||||
fi
|
||||
if [ "$code" != "200" ]; then
|
||||
log_debug "http_download_curl received HTTP status $code"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
http_download_wget() {
|
||||
local_file=$1
|
||||
source_url=$2
|
||||
header=$3
|
||||
if [ -z "$header" ]; then
|
||||
wget -q -O "$local_file" "$source_url"
|
||||
else
|
||||
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||
fi
|
||||
}
|
||||
http_download() {
|
||||
log_debug "http_download $2"
|
||||
if is_command curl; then
|
||||
http_download_curl "$@"
|
||||
return
|
||||
elif is_command wget; then
|
||||
http_download_wget "$@"
|
||||
return
|
||||
fi
|
||||
log_crit "http_download unable to find wget or curl"
|
||||
return 1
|
||||
}
|
||||
http_copy() {
|
||||
tmp=$(mktemp)
|
||||
http_download "${tmp}" "$1" "$2" || return 1
|
||||
body=$(cat "$tmp")
|
||||
rm -f "${tmp}"
|
||||
echo "$body"
|
||||
}
|
||||
github_release() {
|
||||
owner_repo=$1
|
||||
version=$2
|
||||
test -z "$version" && version="latest"
|
||||
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||
json=$(http_copy "$giturl" "Accept:application/json")
|
||||
test -z "$json" && return 1
|
||||
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||
test -z "$version" && return 1
|
||||
echo "$version"
|
||||
}
|
||||
hash_sha256() {
|
||||
TARGET=${1:-/dev/stdin}
|
||||
if is_command gsha256sum; then
|
||||
hash=$(gsha256sum "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command sha256sum; then
|
||||
hash=$(sha256sum "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command shasum; then
|
||||
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||
echo "$hash" | cut -d ' ' -f 1
|
||||
elif is_command openssl; then
|
||||
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
|
||||
echo "$hash" | cut -d ' ' -f a
|
||||
else
|
||||
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
hash_sha256_verify() {
|
||||
TARGET=$1
|
||||
checksums=$2
|
||||
if [ -z "$checksums" ]; then
|
||||
log_err "hash_sha256_verify checksum file not specified in arg2"
|
||||
return 1
|
||||
fi
|
||||
BASENAME=${TARGET##*/}
|
||||
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
|
||||
if [ -z "$want" ]; then
|
||||
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
|
||||
return 1
|
||||
fi
|
||||
got=$(hash_sha256 "$TARGET")
|
||||
if [ "$want" != "$got" ]; then
|
||||
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
cat /dev/null <<EOF
|
||||
------------------------------------------------------------------------
|
||||
End of functions from https://github.com/client9/shlib
|
||||
------------------------------------------------------------------------
|
||||
EOF
|
||||
|
||||
PROJECT_NAME="trivy"
|
||||
OWNER=aquasecurity
|
||||
REPO="trivy"
|
||||
BINARY=trivy
|
||||
FORMAT=tar.gz
|
||||
OS=$(uname_os)
|
||||
ARCH=$(uname_arch)
|
||||
PREFIX="$OWNER/$REPO"
|
||||
|
||||
# use in logging routines
|
||||
log_prefix() {
|
||||
echo "$PREFIX"
|
||||
}
|
||||
PLATFORM="${OS}/${ARCH}"
|
||||
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
|
||||
|
||||
uname_os_check "$OS"
|
||||
uname_arch_check "$ARCH"
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
get_binaries
|
||||
|
||||
tag_to_version
|
||||
|
||||
adjust_format
|
||||
|
||||
adjust_os
|
||||
|
||||
adjust_arch
|
||||
|
||||
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
|
||||
|
||||
NAME=${PROJECT_NAME}_${VERSION}_${OS}-${ARCH}
|
||||
TARBALL=${NAME}.${FORMAT}
|
||||
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
|
||||
CHECKSUM=${PROJECT_NAME}_${VERSION}_checksums.txt
|
||||
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
|
||||
|
||||
|
||||
execute
|
||||
24
go.mod
24
go.mod
@@ -3,27 +3,35 @@ module github.com/aquasecurity/trivy
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/aquasecurity/fanal v0.0.0-20191104115841-1a8ced6845b7
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect
|
||||
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20191101193735-bb56553762c0
|
||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1
|
||||
github.com/caarlos0/env/v6 v6.0.0
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cheggaaa/pb/v3 v3.0.3
|
||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f
|
||||
github.com/genuinetools/reg v0.16.0
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
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/prometheus/procfs v0.0.5 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/twitchtv/twirp v5.10.1+incompatible
|
||||
github.com/urfave/cli v1.20.0
|
||||
go.uber.org/zap v1.9.1
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // 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/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
|
||||
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4
|
||||
)
|
||||
|
||||
84
go.sum
84
go.sum
@@ -10,32 +10,40 @@ github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+x
|
||||
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
|
||||
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
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-20191104115841-1a8ced6845b7 h1:CbCusMPZOgrbqiCWEFbWcmBBonllFpMJhtXp9WkyNVQ=
|
||||
github.com/aquasecurity/fanal v0.0.0-20191104115841-1a8ced6845b7/go.mod h1:dD1Ny21eY5FSDyERfUIMwdgYhg6Lnw611VOwDHmTSoQ=
|
||||
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793 h1:wPGD5cu66RvwRkNULAqOAMbNWFjmYSCJ/mqHJ5rYBN0=
|
||||
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793/go.mod h1:S2D937GMywJzh6KiLQEyt/0yqmfAngUFvuQ9UmkIZSw=
|
||||
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-20191226181755-d6cabf5bc5d1 h1:IVXoVH8ejJuBdxgH/+er2WjBxc0tqIGuBCqI5aWW3A0=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1/go.mod h1:Uf9bXd50zTHtWTP7+7u5+OFCPtUVrmsS4v0RXd7E5lw=
|
||||
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=
|
||||
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
||||
github.com/aws/aws-sdk-go v1.19.11 h1:tqaTGER6Byw3QvsjGW0p018U2UOqaJPeJuzoaF7jjoQ=
|
||||
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.25.31 h1:14mdh3HsTgRekePPkYcCbAaEXJknc3mN7f4XfsiMMDA=
|
||||
github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
@@ -44,7 +52,11 @@ github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 h1:GMmnK0dvr0Sf
|
||||
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91/go.mod h1:hw/JEQBIE+c/BLI4aKM8UU8v+ZqrD3h7HC27kKt8JQU=
|
||||
github.com/caarlos0/env/v6 v6.0.0 h1:NZt6FAoB8ieKO5lEwRdwCzYxWFx7ZYF2R7UcoyaWtyc=
|
||||
github.com/caarlos0/env/v6 v6.0.0/go.mod h1:+wdyOmtjoZIW2GJOc2OYa5NoOFuWD/bIpWqm30NgtRk=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cheggaaa/pb/v3 v3.0.3 h1:8WApbyUmgMOz7WIxJVNK0IRDcRfAmTxcEdi0TuxjdP4=
|
||||
github.com/cheggaaa/pb/v3 v3.0.3/go.mod h1:Pp35CDuiEpHa/ZLGCtBbM6CBwMstv1bJlG884V+73Yc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
@@ -105,6 +117,8 @@ github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -113,6 +127,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
@@ -125,6 +141,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=
|
||||
@@ -150,6 +170,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
|
||||
@@ -177,12 +198,20 @@ 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-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed 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=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@@ -236,9 +265,12 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
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 +297,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.10.1+incompatible h1:35js8ID9rYPKkZ0qWnuZw+q+OuCWM1GIibu1F1YImjA=
|
||||
github.com/twitchtv/twirp v5.10.1+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=
|
||||
@@ -275,20 +309,36 @@ go.etcd.io/bbolt v1.3.2/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,10 +347,12 @@ 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-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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c h1:SRpq/kuj/xNci/RdvEs+RSvpfxqvLAzTKuKGlzoGdZQ=
|
||||
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -308,6 +360,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -323,8 +376,11 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190506115046-ca7f33d4116e h1:bq5BY1tGuaK8HxuwN6pT6kWgTVLeJ5KwuyBpsl1CZL4=
|
||||
golang.org/x/sys v0.0.0-20190506115046-ca7f33d4116e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562 h1:wOweSabW7qssfcg63CEDHHA4zyoqRlGU6eYV7IUMCq0=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -337,12 +393,20 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
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 +434,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 +458,9 @@ 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 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
|
||||
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=
|
||||
|
||||
@@ -12,7 +12,6 @@ builds:
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
- freebsd
|
||||
- openbsd
|
||||
goarch:
|
||||
@@ -34,8 +33,8 @@ nfpms:
|
||||
homepage: "https://github.com/aquasecurity"
|
||||
maintainer: "Teppei Fukuda <knqyf263@gmail.com>"
|
||||
description: "A Fast Vulnerability Scanner for Containers"
|
||||
license: "MIT"
|
||||
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
license: "AGPL-3.0"
|
||||
file_name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
@@ -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
|
||||
@@ -71,6 +65,7 @@ archives:
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
- contrib/gitlab.tpl
|
||||
|
||||
brews:
|
||||
-
|
||||
@@ -99,3 +94,5 @@ dockers:
|
||||
- "--label=org.label-schema.build-date={{ .Date }}"
|
||||
- "--label=org.label-schema.vcs=https://github.com/aquasecurity/trivy"
|
||||
- "--label=org.label-schema.vcs-ref={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- contrib/gitlab.tpl
|
||||
|
||||
439
integration/client_server_test.go
Normal file
439
integration/client_server_test.go
Normal file
@@ -0,0 +1,439 @@
|
||||
// +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
|
||||
ClientTokenHeader string
|
||||
ServerToken string
|
||||
ServerTokenHeader 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",
|
||||
ClientTokenHeader: "Trivy-Token",
|
||||
ServerToken: "token",
|
||||
ServerTokenHeader: "Trivy-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: "opensuse leap 15.1 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/opensuse-leap-151.tar.gz",
|
||||
},
|
||||
golden: "testdata/opensuse-leap-151.json.golden",
|
||||
},
|
||||
{
|
||||
name: "opensuse leap 42.3 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/opensuse-leap-423.tar.gz",
|
||||
},
|
||||
golden: "testdata/opensuse-leap-423.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 1.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/photon-10.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-10.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 2.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/photon-20.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-20.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 3.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/photon-30.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-30.json.golden",
|
||||
},
|
||||
{
|
||||
name: "invalid token",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/distroless-base.tar.gz",
|
||||
ClientToken: "invalidtoken",
|
||||
ClientTokenHeader: "Trivy-Token",
|
||||
ServerToken: "token",
|
||||
ServerTokenHeader: "Trivy-Token",
|
||||
},
|
||||
wantErr: "twirp error unauthenticated: invalid token",
|
||||
},
|
||||
{
|
||||
name: "invalid token header",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
Input: "testdata/fixtures/distroless-base.tar.gz",
|
||||
ClientToken: "valid-token",
|
||||
ClientTokenHeader: "Trivy-Token",
|
||||
ServerToken: "valid-token",
|
||||
ServerTokenHeader: "Invalid",
|
||||
},
|
||||
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, c.testArgs.ServerTokenHeader, 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.ClientTokenHeader, 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, tokenHeader, cacheDir string) []string {
|
||||
osArgs := []string{"trivy", "server", "--skip-update", "--cache-dir", cacheDir, "--listen", addr}
|
||||
if token != "" {
|
||||
osArgs = append(osArgs, []string{"--token", token, "--token-header", tokenHeader}...)
|
||||
}
|
||||
return osArgs
|
||||
}
|
||||
|
||||
func setupClient(t *testing.T, ignoreUnfixed bool, severity, ignoreIDs []string,
|
||||
addr, token, tokenHeader, 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, "--token-header", tokenHeader}...)
|
||||
}
|
||||
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
|
||||
}
|
||||
318
integration/docker_engine_test.go
Normal file
318
integration/docker_engine_test.go
Normal file
@@ -0,0 +1,318 @@
|
||||
// +build integration
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/internal"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRun_WithDockerEngine(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
imageTag string
|
||||
invalidImage bool
|
||||
ignoreUnfixed bool
|
||||
severity []string
|
||||
ignoreIDs []string
|
||||
testfile string
|
||||
expectedOutputFile string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "happy path, valid image path, alpine:3.10",
|
||||
imageTag: "alpine:3.10",
|
||||
expectedOutputFile: "testdata/alpine-310.json.golden",
|
||||
testfile: "testdata/fixtures/alpine-310.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, alpine:3.10, ignore unfixed",
|
||||
ignoreUnfixed: true,
|
||||
imageTag: "alpine:3.10",
|
||||
expectedOutputFile: "testdata/alpine-310-ignore-unfixed.json.golden",
|
||||
testfile: "testdata/fixtures/alpine-310.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, alpine:3.10, ignore unfixed, with medium and high severity",
|
||||
ignoreUnfixed: true,
|
||||
severity: []string{"MEDIUM", "HIGH"},
|
||||
imageTag: "alpine:3.10",
|
||||
expectedOutputFile: "testdata/alpine-310-medium-high.json.golden",
|
||||
testfile: "testdata/fixtures/alpine-310.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, alpine:3.10, with .trivyignore",
|
||||
imageTag: "alpine:3.10",
|
||||
ignoreIDs: []string{"CVE-2019-1549", "CVE-2019-1563"},
|
||||
expectedOutputFile: "testdata/alpine-310-ignore-cveids.json.golden",
|
||||
testfile: "testdata/fixtures/alpine-310.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, alpine:3.9",
|
||||
imageTag: "alpine:3.9",
|
||||
expectedOutputFile: "testdata/alpine-39.json.golden",
|
||||
testfile: "testdata/fixtures/alpine-39.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, amazonlinux:1",
|
||||
imageTag: "amazonlinux:1",
|
||||
expectedOutputFile: "testdata/amazon-1.json.golden",
|
||||
testfile: "testdata/fixtures/amazon-1.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, amazonlinux:2",
|
||||
imageTag: "amazonlinux:2",
|
||||
expectedOutputFile: "testdata/amazon-2.json.golden",
|
||||
testfile: "testdata/fixtures/amazon-2.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, centos:6",
|
||||
imageTag: "centos:6",
|
||||
expectedOutputFile: "testdata/centos-6.json.golden",
|
||||
testfile: "testdata/fixtures/centos-6.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, centos:6",
|
||||
imageTag: "centos:6",
|
||||
expectedOutputFile: "testdata/centos-6.json.golden",
|
||||
testfile: "testdata/fixtures/centos-6.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, centos:7",
|
||||
imageTag: "centos:7",
|
||||
expectedOutputFile: "testdata/centos-7.json.golden",
|
||||
testfile: "testdata/fixtures/centos-7.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, centos:7, with --ignore-unfixed option",
|
||||
imageTag: "centos:7",
|
||||
ignoreUnfixed: true,
|
||||
expectedOutputFile: "testdata/centos-7-ignore-unfixed.json.golden",
|
||||
testfile: "testdata/fixtures/centos-7.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, centos:7, with --ignore-unfixed option, with low and high severity",
|
||||
imageTag: "centos:7",
|
||||
ignoreUnfixed: true,
|
||||
severity: []string{"LOW", "HIGH"},
|
||||
expectedOutputFile: "testdata/centos-7-low-high.json.golden",
|
||||
testfile: "testdata/fixtures/centos-7.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, debian:buster",
|
||||
imageTag: "debian:buster",
|
||||
expectedOutputFile: "testdata/debian-buster.json.golden",
|
||||
testfile: "testdata/fixtures/debian-buster.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, debian:buster, with --ignore-unfixed option",
|
||||
ignoreUnfixed: true,
|
||||
imageTag: "debian:buster",
|
||||
expectedOutputFile: "testdata/debian-buster-ignore-unfixed.json.golden",
|
||||
testfile: "testdata/fixtures/debian-buster.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, debian:stretch",
|
||||
imageTag: "debian:stretch",
|
||||
expectedOutputFile: "testdata/debian-stretch.json.golden",
|
||||
testfile: "testdata/fixtures/debian-stretch.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, distroless:base",
|
||||
imageTag: "gcr.io/distroless/base:latest",
|
||||
expectedOutputFile: "testdata/distroless-base.json.golden",
|
||||
testfile: "testdata/fixtures/distroless-base.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, distroless:base",
|
||||
imageTag: "gcr.io/distroless/base:latest",
|
||||
expectedOutputFile: "testdata/distroless-base.json.golden",
|
||||
testfile: "testdata/fixtures/distroless-base.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, distroless:base, with --ignore-unfixed option",
|
||||
imageTag: "gcr.io/distroless/base:latest",
|
||||
ignoreUnfixed: true,
|
||||
expectedOutputFile: "testdata/distroless-base-ignore-unfixed.json.golden",
|
||||
testfile: "testdata/fixtures/distroless-base.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, distroless:python2.7",
|
||||
imageTag: "gcr.io/distroless/python2.7:latest",
|
||||
expectedOutputFile: "testdata/distroless-python27.json.golden",
|
||||
testfile: "testdata/fixtures/distroless-python27.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, oraclelinux:6-slim",
|
||||
imageTag: "oraclelinux:6-slim",
|
||||
expectedOutputFile: "testdata/oraclelinux-6-slim.json.golden",
|
||||
testfile: "testdata/fixtures/oraclelinux-6-slim.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, oraclelinux:7-slim",
|
||||
imageTag: "oraclelinux:7-slim",
|
||||
expectedOutputFile: "testdata/oraclelinux-7-slim.json.golden",
|
||||
testfile: "testdata/fixtures/oraclelinux-7-slim.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, oraclelinux:8-slim",
|
||||
imageTag: "oraclelinux:8-slim",
|
||||
expectedOutputFile: "testdata/oraclelinux-8-slim.json.golden",
|
||||
testfile: "testdata/fixtures/oraclelinux-8-slim.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, ubuntu:16.04",
|
||||
imageTag: "ubuntu:16.04",
|
||||
expectedOutputFile: "testdata/ubuntu-1604.json.golden",
|
||||
testfile: "testdata/fixtures/ubuntu-1604.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, ubuntu:18.04",
|
||||
imageTag: "ubuntu:18.04",
|
||||
expectedOutputFile: "testdata/ubuntu-1804.json.golden",
|
||||
testfile: "testdata/fixtures/ubuntu-1804.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, ubuntu:18.04, with --ignore-unfixed option",
|
||||
imageTag: "ubuntu:18.04",
|
||||
ignoreUnfixed: true,
|
||||
expectedOutputFile: "testdata/ubuntu-1804-ignore-unfixed.json.golden",
|
||||
testfile: "testdata/fixtures/ubuntu-1804.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, registry.redhat.io/ubi7",
|
||||
imageTag: "registry.redhat.io/ubi7",
|
||||
expectedOutputFile: "testdata/ubi-7.json.golden",
|
||||
testfile: "testdata/fixtures/ubi-7.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, opensuse leap 15.1",
|
||||
imageTag: "opensuse/leap:latest",
|
||||
expectedOutputFile: "testdata/opensuse-leap-151.json.golden",
|
||||
testfile: "testdata/fixtures/opensuse-leap-151.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, opensuse leap 42.3",
|
||||
imageTag: "opensuse/leap:42.3",
|
||||
expectedOutputFile: "testdata/opensuse-leap-423.json.golden",
|
||||
testfile: "testdata/fixtures/opensuse-leap-423.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, photon 1.0",
|
||||
imageTag: "photon:1.0-20190823",
|
||||
expectedOutputFile: "testdata/photon-10.json.golden",
|
||||
testfile: "testdata/fixtures/photon-10.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, photon 2.0",
|
||||
imageTag: "photon:2.0-20190726",
|
||||
expectedOutputFile: "testdata/photon-20.json.golden",
|
||||
testfile: "testdata/fixtures/photon-20.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "happy path, valid image path, photon 3.0",
|
||||
imageTag: "photon:3.0-20190823",
|
||||
expectedOutputFile: "testdata/photon-30.json.golden",
|
||||
testfile: "testdata/fixtures/photon-30.tar.gz",
|
||||
},
|
||||
{
|
||||
name: "sad path, invalid image",
|
||||
invalidImage: true,
|
||||
testfile: "badimage:latest",
|
||||
expectedError: "error in image scan: failed to analyze image: failed to extract files: failed to get the v2 manifest: Get https://registry-1.docker.io/v2/library/badimage/manifests/latest: http: non-successful response (status=401 body=\"{\\\"errors\\\":[{\\\"code\\\":\\\"UNAUTHORIZED\\\",\\\"message\\\":\\\"authentication required\\\",\\\"detail\\\":[{\\\"Type\\\":\\\"repository\\\",\\\"Class\\\":\\\"\\\",\\\"Name\\\":\\\"library/badimage\\\",\\\"Action\\\":\\\"pull\\\"}]}]}\\n\")",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Copy DB file
|
||||
cacheDir := gunzipDB()
|
||||
defer os.RemoveAll(cacheDir)
|
||||
|
||||
ctx := context.Background()
|
||||
defer ctx.Done()
|
||||
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
if !tc.invalidImage {
|
||||
testfile, err := os.Open(tc.testfile)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
// ensure image doesnt already exists
|
||||
_, _ = cli.ImageRemove(ctx, tc.testfile, types.ImageRemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
|
||||
// load image into docker engine
|
||||
_, err = cli.ImageLoad(ctx, testfile, true)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
// tag our image to something unique
|
||||
err = cli.ImageTag(ctx, tc.imageTag, tc.testfile)
|
||||
require.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
of, err := ioutil.TempFile("", "integration-docker-engine-output-file-*")
|
||||
require.NoError(t, err, tc.name)
|
||||
defer os.Remove(of.Name())
|
||||
|
||||
// run trivy
|
||||
app := internal.NewApp("dev")
|
||||
trivyArgs := []string{"trivy", "--skip-update", "--cache-dir", cacheDir, "--format=json"}
|
||||
if tc.ignoreUnfixed {
|
||||
trivyArgs = append(trivyArgs, "--ignore-unfixed")
|
||||
}
|
||||
if len(tc.severity) != 0 {
|
||||
trivyArgs = append(trivyArgs,
|
||||
[]string{"--severity", strings.Join(tc.severity, ",")}...,
|
||||
)
|
||||
}
|
||||
if len(tc.ignoreIDs) != 0 {
|
||||
trivyIgnore := ".trivyignore"
|
||||
err := ioutil.WriteFile(trivyIgnore, []byte(strings.Join(tc.ignoreIDs, "\n")), 0444)
|
||||
assert.NoError(t, err, "failed to write .trivyignore")
|
||||
defer os.Remove(trivyIgnore)
|
||||
}
|
||||
if !tc.invalidImage {
|
||||
trivyArgs = append(trivyArgs, "--output", of.Name())
|
||||
}
|
||||
trivyArgs = append(trivyArgs, tc.testfile)
|
||||
|
||||
err = app.Run(trivyArgs)
|
||||
switch {
|
||||
case tc.expectedError != "":
|
||||
assert.Equal(t, tc.expectedError, err.Error(), tc.name)
|
||||
default:
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
if !tc.invalidImage {
|
||||
// check for vulnerability output info
|
||||
got, err := ioutil.ReadAll(of)
|
||||
assert.NoError(t, err, tc.name)
|
||||
want, err := ioutil.ReadFile(tc.expectedOutputFile)
|
||||
assert.NoError(t, err, tc.name)
|
||||
assert.JSONEq(t, string(want), string(got), tc.name)
|
||||
|
||||
// cleanup
|
||||
_, err = cli.ImageRemove(ctx, tc.testfile, types.ImageRemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
80
integration/integration_test.go
Normal file
80
integration/integration_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,86 @@ 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",
|
||||
},
|
||||
{
|
||||
name: "opensuse leap 15.1 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
SkipUpdate: true,
|
||||
Format: "json",
|
||||
Input: "testdata/fixtures/opensuse-leap-151.tar.gz",
|
||||
},
|
||||
golden: "testdata/opensuse-leap-151.json.golden",
|
||||
},
|
||||
{
|
||||
name: "opensuse leap 42.3 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
SkipUpdate: true,
|
||||
Format: "json",
|
||||
Input: "testdata/fixtures/opensuse-leap-423.tar.gz",
|
||||
},
|
||||
golden: "testdata/opensuse-leap-423.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 1.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
SkipUpdate: true,
|
||||
Format: "json",
|
||||
Input: "testdata/fixtures/photon-10.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-10.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 2.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
SkipUpdate: true,
|
||||
Format: "json",
|
||||
Input: "testdata/fixtures/photon-20.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-20.json.golden",
|
||||
},
|
||||
{
|
||||
name: "photon 3.0 integration",
|
||||
testArgs: args{
|
||||
Version: "dev",
|
||||
SkipUpdate: true,
|
||||
Format: "json",
|
||||
Input: "testdata/fixtures/photon-30.tar.gz",
|
||||
},
|
||||
golden: "testdata/photon-30.json.golden",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -310,7 +350,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}
|
||||
26
integration/testdata/centos-6.json.golden
vendored
26
integration/testdata/centos-6.json.golden
vendored
@@ -12628,6 +12628,32 @@
|
||||
"https://utcc.utoronto.ca/~cks/space/blog/sysadmin/TarFindingTruncateBug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-12735",
|
||||
"PkgName": "vim-minimal",
|
||||
"InstalledVersion": "2:7.4.629-5.el6_8.1",
|
||||
"FixedVersion": "2:7.4.629-5.el6_10.2",
|
||||
"Title": "vim/neovim: ':source!' command allows arbitrary command execution via modelines",
|
||||
"Description": "getchar.c in Vim before 8.1.1365 and Neovim before 0.3.6 allows remote attackers to execute arbitrary OS commands via the :source! command in a modeline, as demonstrated by execute in Vim, and assert_fails or nvim_input in Neovim.",
|
||||
"Severity": "CRITICAL",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00031.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00036.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00037.html",
|
||||
"http://www.securityfocus.com/bid/108724",
|
||||
"https://bugs.debian.org/930020",
|
||||
"https://bugs.debian.org/930024",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735",
|
||||
"https://github.com/neovim/neovim/pull/10082",
|
||||
"https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md",
|
||||
"https://github.com/vim/vim/commit/53575521406739cf20bbe4e384d88e7dca11f040",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/2BMDSHTF754TITC6AQJPCS5IRIDMMIM7/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/TRIRBC2YRGKPAWVRMZS4SZTGGCVRVZPR/",
|
||||
"https://usn.ubuntu.com/4016-1/",
|
||||
"https://usn.ubuntu.com/4016-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4467"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2017-5953",
|
||||
"PkgName": "vim-minimal",
|
||||
|
||||
@@ -112,6 +112,32 @@
|
||||
"https://usn.ubuntu.com/3816-1/",
|
||||
"https://www.exploit-db.com/exploits/45714/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-12735",
|
||||
"PkgName": "vim-minimal",
|
||||
"InstalledVersion": "2:7.4.160-5.el7",
|
||||
"FixedVersion": "2:7.4.160-6.el7_6",
|
||||
"Title": "vim/neovim: ':source!' command allows arbitrary command execution via modelines",
|
||||
"Description": "getchar.c in Vim before 8.1.1365 and Neovim before 0.3.6 allows remote attackers to execute arbitrary OS commands via the :source! command in a modeline, as demonstrated by execute in Vim, and assert_fails or nvim_input in Neovim.",
|
||||
"Severity": "CRITICAL",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00031.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00036.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00037.html",
|
||||
"http://www.securityfocus.com/bid/108724",
|
||||
"https://bugs.debian.org/930020",
|
||||
"https://bugs.debian.org/930024",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735",
|
||||
"https://github.com/neovim/neovim/pull/10082",
|
||||
"https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md",
|
||||
"https://github.com/vim/vim/commit/53575521406739cf20bbe4e384d88e7dca11f040",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/2BMDSHTF754TITC6AQJPCS5IRIDMMIM7/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/TRIRBC2YRGKPAWVRMZS4SZTGGCVRVZPR/",
|
||||
"https://usn.ubuntu.com/4016-1/",
|
||||
"https://usn.ubuntu.com/4016-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4467"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1708,6 +1708,32 @@
|
||||
"https://lists.apache.org/thread.html/5960a34a524848cd722fd7ab7e2227eac10107b0f90d9d1e9c3caa74@%3Cuser.cassandra.apache.org%3E",
|
||||
"https://security.netapp.com/advisory/ntap-20190307-0007/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-12735",
|
||||
"PkgName": "vim-minimal",
|
||||
"InstalledVersion": "2:7.4.160-5.el7",
|
||||
"FixedVersion": "2:7.4.160-6.el7_6",
|
||||
"Title": "vim/neovim: ':source!' command allows arbitrary command execution via modelines",
|
||||
"Description": "getchar.c in Vim before 8.1.1365 and Neovim before 0.3.6 allows remote attackers to execute arbitrary OS commands via the :source! command in a modeline, as demonstrated by execute in Vim, and assert_fails or nvim_input in Neovim.",
|
||||
"Severity": "CRITICAL",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00031.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00036.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00037.html",
|
||||
"http://www.securityfocus.com/bid/108724",
|
||||
"https://bugs.debian.org/930020",
|
||||
"https://bugs.debian.org/930024",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735",
|
||||
"https://github.com/neovim/neovim/pull/10082",
|
||||
"https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md",
|
||||
"https://github.com/vim/vim/commit/53575521406739cf20bbe4e384d88e7dca11f040",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/2BMDSHTF754TITC6AQJPCS5IRIDMMIM7/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/TRIRBC2YRGKPAWVRMZS4SZTGGCVRVZPR/",
|
||||
"https://usn.ubuntu.com/4016-1/",
|
||||
"https://usn.ubuntu.com/4016-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4467"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
26
integration/testdata/centos-7.json.golden
vendored
26
integration/testdata/centos-7.json.golden
vendored
@@ -11849,6 +11849,32 @@
|
||||
"https://www.kernel.org/pub/linux/utils/util-linux/v2.27/v2.27-ReleaseNotes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-12735",
|
||||
"PkgName": "vim-minimal",
|
||||
"InstalledVersion": "2:7.4.160-5.el7",
|
||||
"FixedVersion": "2:7.4.160-6.el7_6",
|
||||
"Title": "vim/neovim: ':source!' command allows arbitrary command execution via modelines",
|
||||
"Description": "getchar.c in Vim before 8.1.1365 and Neovim before 0.3.6 allows remote attackers to execute arbitrary OS commands via the :source! command in a modeline, as demonstrated by execute in Vim, and assert_fails or nvim_input in Neovim.",
|
||||
"Severity": "CRITICAL",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00031.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00036.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-06/msg00037.html",
|
||||
"http://www.securityfocus.com/bid/108724",
|
||||
"https://bugs.debian.org/930020",
|
||||
"https://bugs.debian.org/930024",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735",
|
||||
"https://github.com/neovim/neovim/pull/10082",
|
||||
"https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md",
|
||||
"https://github.com/vim/vim/commit/53575521406739cf20bbe4e384d88e7dca11f040",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/2BMDSHTF754TITC6AQJPCS5IRIDMMIM7/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/TRIRBC2YRGKPAWVRMZS4SZTGGCVRVZPR/",
|
||||
"https://usn.ubuntu.com/4016-1/",
|
||||
"https://usn.ubuntu.com/4016-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4467"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2017-5953",
|
||||
"PkgName": "vim-minimal",
|
||||
|
||||
103
integration/testdata/opensuse-leap-151.json.golden
vendored
Normal file
103
integration/testdata/opensuse-leap-151.json.golden
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/opensuse-leap-151.tar.gz (opensuse.leap 15.1)",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2596-1",
|
||||
"PkgName": "cpio",
|
||||
"InstalledVersion": "2.12-lp151.2.68",
|
||||
"FixedVersion": "2.12-lp151.3.3.1",
|
||||
"Title": "Security update for cpio",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-11/msg00076.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2611-1",
|
||||
"PkgName": "libidn2-0",
|
||||
"InstalledVersion": "2.0.4-lp151.2.3",
|
||||
"FixedVersion": "2.2.0-lp151.3.3.1",
|
||||
"Title": "Security update for libidn2",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-12/msg00009.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2551-1",
|
||||
"PkgName": "libncurses6",
|
||||
"InstalledVersion": "6.1-lp151.5.41",
|
||||
"FixedVersion": "6.1-lp151.6.3.1",
|
||||
"Title": "Security update for ncurses",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-11/msg00059.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2689-1",
|
||||
"PkgName": "libssh4",
|
||||
"InstalledVersion": "0.8.7-lp151.2.3.1",
|
||||
"FixedVersion": "0.8.7-lp151.2.6.1",
|
||||
"Title": "Security update for libssh",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-12/msg00033.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2612-1",
|
||||
"PkgName": "libxml2-2",
|
||||
"InstalledVersion": "2.9.7-lp151.5.3.1",
|
||||
"FixedVersion": "2.9.7-lp151.5.6.1",
|
||||
"Title": "Security update for libxml2",
|
||||
"Severity": "UNKNOWN",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-12/msg00010.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2551-1",
|
||||
"PkgName": "ncurses-utils",
|
||||
"InstalledVersion": "6.1-lp151.5.41",
|
||||
"FixedVersion": "6.1-lp151.6.3.1",
|
||||
"Title": "Security update for ncurses",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-11/msg00059.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2672-1",
|
||||
"PkgName": "permissions",
|
||||
"InstalledVersion": "20181116-lp151.4.6.1",
|
||||
"FixedVersion": "20181116-lp151.4.9.1",
|
||||
"Title": "Security update for permissions",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-12/msg00024.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "openSUSE-SU-2019:2551-1",
|
||||
"PkgName": "terminfo-base",
|
||||
"InstalledVersion": "6.1-lp151.5.41",
|
||||
"FixedVersion": "6.1-lp151.6.3.1",
|
||||
"Title": "Security update for ncurses",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-11/msg00059.html",
|
||||
"https://www.suse.com/support/security/rating/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
6
integration/testdata/opensuse-leap-423.json.golden
vendored
Normal file
6
integration/testdata/opensuse-leap-423.json.golden
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/opensuse-leap-423.tar.gz (opensuse.leap 42.3)",
|
||||
"Vulnerabilities": null
|
||||
}
|
||||
]
|
||||
165
integration/testdata/oraclelinux-6-slim.json.golden
vendored
Normal file
165
integration/testdata/oraclelinux-6-slim.json.golden
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
858
integration/testdata/oraclelinux-7-slim.json.golden
vendored
Normal file
858
integration/testdata/oraclelinux-7-slim.json.golden
vendored
Normal file
@@ -0,0 +1,858 @@
|
||||
[
|
||||
{
|
||||
"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/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
6
integration/testdata/oraclelinux-8-slim.json.golden
vendored
Normal file
6
integration/testdata/oraclelinux-8-slim.json.golden
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/oraclelinux-8-slim.tar.gz (oracle 8.0)",
|
||||
"Vulnerabilities": null
|
||||
}
|
||||
]
|
||||
61
integration/testdata/photon-10.json.golden
vendored
Normal file
61
integration/testdata/photon-10.json.golden
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/photon-10.tar.gz (photon 1.0)",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2012-6711",
|
||||
"PkgName": "bash",
|
||||
"InstalledVersion": "4.3.48-3.ph1",
|
||||
"FixedVersion": "4.3.48-4.ph1",
|
||||
"Title": "bash: heap-based buffer overflow during echo of unsupported characters",
|
||||
"Description": "A heap-based buffer overflow exists in GNU Bash before 4.3 when wide characters, not supported by the current locale set in the LC_CTYPE environment variable, are printed through the echo built-in function. A local attacker, who can provide data to print through the \"echo -e\" built-in function, may use this flaw to crash a script or execute code with the privileges of the bash process. This occurs because ansicstr() in lib/sh/strtrans.c mishandles u32cconv().",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://git.savannah.gnu.org/cgit/bash.git/commit/?h=devel\u0026id=863d31ae775d56b785dc5b0105b6d251515d81d5",
|
||||
"http://www.securityfocus.com/bid/108824",
|
||||
"https://bugzilla.redhat.com/show_bug.cgi?id=1721071",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6711"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1563",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph1",
|
||||
"FixedVersion": "1.0.2t-1.ph1",
|
||||
"Title": "openssl: information disclosure in PKCS7_dataDecode and CMS_decrypt_set1_pkey",
|
||||
"Description": "In situations where an attacker receives automated notification of the success or failure of a decryption attempt an attacker, after sending a very large number of messages to be decrypted, can recover a CMS/PKCS7 transported encryption key or decrypt any RSA encrypted message that was encrypted with the public RSA key, using a Bleichenbacher padding oracle attack. Applications are not affected if they use a certificate together with the private RSA key to the CMS_decrypt or PKCS7_decrypt functions to select the correct recipient info to decrypt. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1563",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=08229ad838c50f644d7e928e2eef147b4308ad64",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=631f94db0065c78181ca9ba5546ebc8bb3884b97",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=e21f8cf78a125cd3c8c0d1a1a6c8bb0b901f893f",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1547",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph1",
|
||||
"FixedVersion": "1.0.2t-1.ph1",
|
||||
"Title": "openssl: side-channel weak encryption vulnerability",
|
||||
"Description": "Normally in OpenSSL EC groups always have a co-factor present and this is used in side channel resistant code paths. However, in some cases, it is possible to construct a group using explicit parameters (instead of using a named curve). In those cases it is possible that such a group does not have the cofactor present. This can occur even where all the parameters match a known named curve. If such a curve is used then OpenSSL falls back to non-side channel resistant code paths which may result in full key recovery during an ECDSA signature operation. In order to be vulnerable an attacker would have to have the ability to time the creation of a large number of signatures where explicit parameters with no co-factor present are in use by an application using libcrypto. For the avoidance of doubt libssl is not vulnerable because explicit parameters are never used. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "LOW",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://arxiv.org/abs/1909.01785",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=21c856b75d81eff61aa63b4f036bb64a85bf6d46",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=30c22fa8b1d840036b8e203585738df62a03cec8",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=7c1709c2da5414f5b6133d00a03fc8c5bf996c7a",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
227
integration/testdata/photon-20.json.golden
vendored
Normal file
227
integration/testdata/photon-20.json.golden
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/photon-20.tar.gz (photon 2.0)",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5481",
|
||||
"PkgName": "curl",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-9.ph2",
|
||||
"Title": "curl: double free due to subsequent call of realloc()",
|
||||
"Description": "Double-free vulnerability in the FTP-kerberos code in cURL 7.52.0 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5481.html",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5482",
|
||||
"PkgName": "curl",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-9.ph2",
|
||||
"Title": "curl: heap buffer overflow in function tftp_receive_packet()",
|
||||
"Description": "Heap buffer overflow in the TFTP protocol handler in cURL 7.19.4 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5482.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5482",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2018-16890",
|
||||
"PkgName": "curl",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-8.ph2",
|
||||
"Title": "curl: NTLM type-2 heap out-of-bounds buffer read",
|
||||
"Description": "libcurl versions from 7.36.0 to before 7.64.0 is vulnerable to a heap buffer out-of-bounds read. The function handling incoming NTLM type-2 messages (`lib/vauth/ntlm.c:ntlm_decode_type2_target`) does not validate incoming data correctly and is subject to an integer overflow vulnerability. Using that overflow, a malicious or broken NTLM server could trick libcurl to accept a bad length + offset combination that would lead to a buffer read out-of-bounds.",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://www.securityfocus.com/bid/106947",
|
||||
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-16890",
|
||||
"https://cert-portal.siemens.com/productcert/pdf/ssa-436177.pdf",
|
||||
"https://curl.haxx.se/docs/CVE-2018-16890.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16890",
|
||||
"https://lists.apache.org/thread.html/8338a0f605bdbb3a6098bb76f666a95fc2b2f53f37fa1ecc89f1146f@%3Cdevnull.infra.apache.org%3E",
|
||||
"https://security.netapp.com/advisory/ntap-20190315-0001/",
|
||||
"https://usn.ubuntu.com/3882-1/",
|
||||
"https://www.debian.org/security/2019/dsa-4386",
|
||||
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html",
|
||||
"https://www.oracle.com/technetwork/security-advisory/cpujul2019-5072835.html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5481",
|
||||
"PkgName": "curl-libs",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-9.ph2",
|
||||
"Title": "curl: double free due to subsequent call of realloc()",
|
||||
"Description": "Double-free vulnerability in the FTP-kerberos code in cURL 7.52.0 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5481.html",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5482",
|
||||
"PkgName": "curl-libs",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-9.ph2",
|
||||
"Title": "curl: heap buffer overflow in function tftp_receive_packet()",
|
||||
"Description": "Heap buffer overflow in the TFTP protocol handler in cURL 7.19.4 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5482.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5482",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2018-16890",
|
||||
"PkgName": "curl-libs",
|
||||
"InstalledVersion": "7.59.0-7.ph2",
|
||||
"FixedVersion": "7.59.0-8.ph2",
|
||||
"Title": "curl: NTLM type-2 heap out-of-bounds buffer read",
|
||||
"Description": "libcurl versions from 7.36.0 to before 7.64.0 is vulnerable to a heap buffer out-of-bounds read. The function handling incoming NTLM type-2 messages (`lib/vauth/ntlm.c:ntlm_decode_type2_target`) does not validate incoming data correctly and is subject to an integer overflow vulnerability. Using that overflow, a malicious or broken NTLM server could trick libcurl to accept a bad length + offset combination that would lead to a buffer read out-of-bounds.",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://www.securityfocus.com/bid/106947",
|
||||
"https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-16890",
|
||||
"https://cert-portal.siemens.com/productcert/pdf/ssa-436177.pdf",
|
||||
"https://curl.haxx.se/docs/CVE-2018-16890.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16890",
|
||||
"https://lists.apache.org/thread.html/8338a0f605bdbb3a6098bb76f666a95fc2b2f53f37fa1ecc89f1146f@%3Cdevnull.infra.apache.org%3E",
|
||||
"https://security.netapp.com/advisory/ntap-20190315-0001/",
|
||||
"https://usn.ubuntu.com/3882-1/",
|
||||
"https://www.debian.org/security/2019/dsa-4386",
|
||||
"https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html",
|
||||
"https://www.oracle.com/technetwork/security-advisory/cpujul2019-5072835.html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5094",
|
||||
"PkgName": "e2fsprogs-libs",
|
||||
"InstalledVersion": "1.43.4-2.ph2",
|
||||
"FixedVersion": "1.43.4-3.ph2",
|
||||
"Description": "An exploitable code execution vulnerability exists in the quota file functionality of E2fsprogs 1.45.3. A specially crafted ext4 partition can cause an out-of-bounds write on the heap, resulting in code execution. An attacker can corrupt a partition to trigger this vulnerability.",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5094",
|
||||
"https://lists.debian.org/debian-lts-announce/2019/09/msg00029.html",
|
||||
"https://seclists.org/bugtraq/2019/Sep/58",
|
||||
"https://talosintelligence.com/vulnerability_reports/TALOS-2019-0887",
|
||||
"https://usn.ubuntu.com/4142-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4535"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2018-20843",
|
||||
"PkgName": "expat-libs",
|
||||
"InstalledVersion": "2.2.4-1.ph2",
|
||||
"FixedVersion": "2.2.4-2.ph2",
|
||||
"Title": "expat: large number of colons in input makes parser consume high amount of resources, leading to DoS",
|
||||
"Description": "In libexpat in Expat before 2.2.7, XML input including XML names that contain a large number of colons could make the XML parser consume a high amount of RAM and CPU resources while processing (enough to be usable for denial-of-service attacks).",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=5226",
|
||||
"https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=931031",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-20843",
|
||||
"https://github.com/libexpat/libexpat/blob/R_2_2_7/expat/Changes",
|
||||
"https://github.com/libexpat/libexpat/issues/186",
|
||||
"https://github.com/libexpat/libexpat/pull/262",
|
||||
"https://github.com/libexpat/libexpat/pull/262/commits/11f8838bf99ea0a6f0b76f9760c43704d00c4ff6",
|
||||
"https://lists.debian.org/debian-lts-announce/2019/06/msg00028.html",
|
||||
"https://seclists.org/bugtraq/2019/Jun/39",
|
||||
"https://security.netapp.com/advisory/ntap-20190703-0001/",
|
||||
"https://usn.ubuntu.com/4040-1/",
|
||||
"https://usn.ubuntu.com/4040-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4472"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-13115",
|
||||
"PkgName": "libssh2",
|
||||
"InstalledVersion": "1.8.2-1.ph2",
|
||||
"FixedVersion": "1.9.0-1.ph2",
|
||||
"Title": "libssh2: integer overflow in kex_method_diffie_hellman_group_exchange_sha256_key_exchange in kex.c leads to out-of-bounds write",
|
||||
"Description": "In libssh2 before 1.9.0, kex_method_diffie_hellman_group_exchange_sha256_key_exchange in kex.c has an integer overflow that could lead to an out-of-bounds read in the way packets are read from the server. A remote attacker who compromises a SSH server may be able to disclose sensitive information or cause a denial of service condition on the client system when a user connects to the server. This is related to an _libssh2_check_length mistake, and is different from the various issues fixed in 1.8.1, such as CVE-2019-3855.",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"https://blog.semmle.com/libssh2-integer-overflow/",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13115",
|
||||
"https://github.com/libssh2/libssh2/compare/02ecf17...42d37aa",
|
||||
"https://github.com/libssh2/libssh2/pull/350",
|
||||
"https://libssh2.org/changes.html",
|
||||
"https://lists.debian.org/debian-lts-announce/2019/07/msg00024.html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1563",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph2",
|
||||
"FixedVersion": "1.0.2t-1.ph2",
|
||||
"Title": "openssl: information disclosure in PKCS7_dataDecode and CMS_decrypt_set1_pkey",
|
||||
"Description": "In situations where an attacker receives automated notification of the success or failure of a decryption attempt an attacker, after sending a very large number of messages to be decrypted, can recover a CMS/PKCS7 transported encryption key or decrypt any RSA encrypted message that was encrypted with the public RSA key, using a Bleichenbacher padding oracle attack. Applications are not affected if they use a certificate together with the private RSA key to the CMS_decrypt or PKCS7_decrypt functions to select the correct recipient info to decrypt. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1563",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=08229ad838c50f644d7e928e2eef147b4308ad64",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=631f94db0065c78181ca9ba5546ebc8bb3884b97",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=e21f8cf78a125cd3c8c0d1a1a6c8bb0b901f893f",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1547",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph2",
|
||||
"FixedVersion": "1.0.2t-1.ph2",
|
||||
"Title": "openssl: side-channel weak encryption vulnerability",
|
||||
"Description": "Normally in OpenSSL EC groups always have a co-factor present and this is used in side channel resistant code paths. However, in some cases, it is possible to construct a group using explicit parameters (instead of using a named curve). In those cases it is possible that such a group does not have the cofactor present. This can occur even where all the parameters match a known named curve. If such a curve is used then OpenSSL falls back to non-side channel resistant code paths which may result in full key recovery during an ECDSA signature operation. In order to be vulnerable an attacker would have to have the ability to time the creation of a large number of signatures where explicit parameters with no co-factor present are in use by an application using libcrypto. For the avoidance of doubt libssl is not vulnerable because explicit parameters are never used. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "LOW",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://arxiv.org/abs/1909.01785",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=21c856b75d81eff61aa63b4f036bb64a85bf6d46",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=30c22fa8b1d840036b8e203585738df62a03cec8",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=7c1709c2da5414f5b6133d00a03fc8c5bf996c7a",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-16168",
|
||||
"PkgName": "sqlite-libs",
|
||||
"InstalledVersion": "3.27.2-3.ph2",
|
||||
"FixedVersion": "3.27.2-5.ph2",
|
||||
"Description": "In SQLite through 3.29.0, whereLoopAddBtreeIndex in sqlite3.c can crash a browser or other application because of missing validation of a sqlite_stat1 sz field, aka a \"severe division by zero in the query planner.\"",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"https://security.netapp.com/advisory/ntap-20190926-0003/",
|
||||
"https://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg116312.html",
|
||||
"https://www.sqlite.org/src/info/e4598ecbdd18bd82945f6029013296690e719a62",
|
||||
"https://www.sqlite.org/src/timeline?c=98357d8c1263920b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
142
integration/testdata/photon-30.json.golden
vendored
Normal file
142
integration/testdata/photon-30.json.golden
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/photon-30.tar.gz (photon 3.0)",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5481",
|
||||
"PkgName": "curl",
|
||||
"InstalledVersion": "7.61.1-4.ph3",
|
||||
"FixedVersion": "7.61.1-5.ph3",
|
||||
"Title": "curl: double free due to subsequent call of realloc()",
|
||||
"Description": "Double-free vulnerability in the FTP-kerberos code in cURL 7.52.0 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5481.html",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5482",
|
||||
"PkgName": "curl",
|
||||
"InstalledVersion": "7.61.1-4.ph3",
|
||||
"FixedVersion": "7.61.1-5.ph3",
|
||||
"Title": "curl: heap buffer overflow in function tftp_receive_packet()",
|
||||
"Description": "Heap buffer overflow in the TFTP protocol handler in cURL 7.19.4 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5482.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5482",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5481",
|
||||
"PkgName": "curl-libs",
|
||||
"InstalledVersion": "7.61.1-4.ph3",
|
||||
"FixedVersion": "7.61.1-5.ph3",
|
||||
"Title": "curl: double free due to subsequent call of realloc()",
|
||||
"Description": "Double-free vulnerability in the FTP-kerberos code in cURL 7.52.0 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5481.html",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5482",
|
||||
"PkgName": "curl-libs",
|
||||
"InstalledVersion": "7.61.1-4.ph3",
|
||||
"FixedVersion": "7.61.1-5.ph3",
|
||||
"Title": "curl: heap buffer overflow in function tftp_receive_packet()",
|
||||
"Description": "Heap buffer overflow in the TFTP protocol handler in cURL 7.19.4 to 7.65.3.",
|
||||
"Severity": "HIGH",
|
||||
"References": [
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00048.html",
|
||||
"http://lists.opensuse.org/opensuse-security-announce/2019-09/msg00055.html",
|
||||
"https://curl.haxx.se/docs/CVE-2019-5482.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5482",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RGDVKSLY5JUNJRLYRUA6CXGQ2LM63XC3/",
|
||||
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UA7KDM2WPM5CJDDGOEGFV6SSGD2J7RNT/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-5094",
|
||||
"PkgName": "e2fsprogs-libs",
|
||||
"InstalledVersion": "1.44.3-2.ph3",
|
||||
"FixedVersion": "1.44.3-3.ph3",
|
||||
"Description": "An exploitable code execution vulnerability exists in the quota file functionality of E2fsprogs 1.45.3. A specially crafted ext4 partition can cause an out-of-bounds write on the heap, resulting in code execution. An attacker can corrupt a partition to trigger this vulnerability.",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5094",
|
||||
"https://lists.debian.org/debian-lts-announce/2019/09/msg00029.html",
|
||||
"https://seclists.org/bugtraq/2019/Sep/58",
|
||||
"https://talosintelligence.com/vulnerability_reports/TALOS-2019-0887",
|
||||
"https://usn.ubuntu.com/4142-2/",
|
||||
"https://www.debian.org/security/2019/dsa-4535"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1563",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph3",
|
||||
"FixedVersion": "1.0.2t-1.ph3",
|
||||
"Title": "openssl: information disclosure in PKCS7_dataDecode and CMS_decrypt_set1_pkey",
|
||||
"Description": "In situations where an attacker receives automated notification of the success or failure of a decryption attempt an attacker, after sending a very large number of messages to be decrypted, can recover a CMS/PKCS7 transported encryption key or decrypt any RSA encrypted message that was encrypted with the public RSA key, using a Bleichenbacher padding oracle attack. Applications are not affected if they use a certificate together with the private RSA key to the CMS_decrypt or PKCS7_decrypt functions to select the correct recipient info to decrypt. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1563",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=08229ad838c50f644d7e928e2eef147b4308ad64",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=631f94db0065c78181ca9ba5546ebc8bb3884b97",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=e21f8cf78a125cd3c8c0d1a1a6c8bb0b901f893f",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-1547",
|
||||
"PkgName": "openssl",
|
||||
"InstalledVersion": "1.0.2s-1.ph3",
|
||||
"FixedVersion": "1.0.2t-1.ph3",
|
||||
"Title": "openssl: side-channel weak encryption vulnerability",
|
||||
"Description": "Normally in OpenSSL EC groups always have a co-factor present and this is used in side channel resistant code paths. However, in some cases, it is possible to construct a group using explicit parameters (instead of using a named curve). In those cases it is possible that such a group does not have the cofactor present. This can occur even where all the parameters match a known named curve. If such a curve is used then OpenSSL falls back to non-side channel resistant code paths which may result in full key recovery during an ECDSA signature operation. In order to be vulnerable an attacker would have to have the ability to time the creation of a large number of signatures where explicit parameters with no co-factor present are in use by an application using libcrypto. For the avoidance of doubt libssl is not vulnerable because explicit parameters are never used. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c). Fixed in OpenSSL 1.1.0l (Affected 1.1.0-1.1.0k). Fixed in OpenSSL 1.0.2t (Affected 1.0.2-1.0.2s).",
|
||||
"Severity": "LOW",
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/154467/Slackware-Security-Advisory-openssl-Updates.html",
|
||||
"https://arxiv.org/abs/1909.01785",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=21c856b75d81eff61aa63b4f036bb64a85bf6d46",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=30c22fa8b1d840036b8e203585738df62a03cec8",
|
||||
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=7c1709c2da5414f5b6133d00a03fc8c5bf996c7a",
|
||||
"https://seclists.org/bugtraq/2019/Sep/25",
|
||||
"https://security.netapp.com/advisory/ntap-20190919-0002/",
|
||||
"https://www.openssl.org/news/secadv/20190910.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-16168",
|
||||
"PkgName": "sqlite-libs",
|
||||
"InstalledVersion": "3.27.2-3.ph3",
|
||||
"FixedVersion": "3.27.2-5.ph3",
|
||||
"Description": "In SQLite through 3.29.0, whereLoopAddBtreeIndex in sqlite3.c can crash a browser or other application because of missing validation of a sqlite_stat1 sz field, aka a \"severe division by zero in the query planner.\"",
|
||||
"Severity": "MEDIUM",
|
||||
"References": [
|
||||
"https://security.netapp.com/advisory/ntap-20190926-0003/",
|
||||
"https://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg116312.html",
|
||||
"https://www.sqlite.org/src/info/e4598ecbdd18bd82945f6029013296690e719a62",
|
||||
"https://www.sqlite.org/src/timeline?c=98357d8c1263920b"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
BIN
integration/testdata/trivy.db.gz
vendored
BIN
integration/testdata/trivy.db.gz
vendored
Binary file not shown.
293
internal/app.go
Normal file
293
internal/app.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/internal/client"
|
||||
"github.com/aquasecurity/trivy/internal/server"
|
||||
"github.com/aquasecurity/trivy/internal/standalone"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/vulnerability"
|
||||
)
|
||||
|
||||
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: "cache directory",
|
||||
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",
|
||||
}
|
||||
|
||||
tokenHeader = cli.StringFlag{
|
||||
Name: "token-header",
|
||||
Value: "Trivy-Token",
|
||||
Usage: "specify a header name for token",
|
||||
EnvVar: "TRIVY_TOKEN_HEADER",
|
||||
}
|
||||
)
|
||||
|
||||
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,
|
||||
tokenHeader,
|
||||
cli.StringFlag{
|
||||
Name: "remote",
|
||||
Value: "http://localhost:4954",
|
||||
Usage: "server address",
|
||||
EnvVar: "TRIVY_REMOTE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "custom-headers",
|
||||
Usage: "custom headers",
|
||||
EnvVar: "TRIVY_CUSTOM_HEADERS",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
tokenHeader,
|
||||
cli.StringFlag{
|
||||
Name: "listen",
|
||||
Value: "localhost:4954",
|
||||
Usage: "listen address",
|
||||
EnvVar: "TRIVY_LISTEN",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
165
internal/client/config/config.go
Normal file
165
internal/client/config/config.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"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"
|
||||
)
|
||||
|
||||
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
|
||||
tokenHeader string
|
||||
customHeaders []string
|
||||
CustomHeaders http.Header
|
||||
|
||||
// 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"),
|
||||
tokenHeader: c.String("token-header"),
|
||||
customHeaders: c.StringSlice("custom-headers"),
|
||||
}, 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
|
||||
c.CustomHeaders = splitCustomHeaders(c.customHeaders)
|
||||
|
||||
// add token to custom headers
|
||||
if c.token != "" {
|
||||
c.CustomHeaders.Set(c.tokenHeader, c.token)
|
||||
}
|
||||
|
||||
// --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")
|
||||
} else if len(args) > 1 {
|
||||
c.logger.Error(`multiple images cannot be specified`)
|
||||
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
|
||||
}
|
||||
|
||||
func splitCustomHeaders(headers []string) http.Header {
|
||||
result := make(http.Header)
|
||||
for _, header := range headers {
|
||||
// e.g. x-api-token:XXX
|
||||
s := strings.SplitN(header, ":", 2)
|
||||
if len(s) != 2 {
|
||||
continue
|
||||
}
|
||||
result.Set(s[0], s[1])
|
||||
}
|
||||
return result
|
||||
}
|
||||
284
internal/client/config/config_test.go
Normal file
284
internal/client/config/config_test.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"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
|
||||
token string
|
||||
tokenHeader string
|
||||
}
|
||||
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,
|
||||
token: "foobar",
|
||||
tokenHeader: "Trivy-Token",
|
||||
},
|
||||
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,
|
||||
token: "foobar",
|
||||
tokenHeader: "Trivy-Token",
|
||||
CustomHeaders: http.Header{
|
||||
"Trivy-Token": []string{"foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
CustomHeaders: make(http.Header),
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
CustomHeaders: make(http.Header),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sad: multiple image names",
|
||||
fields: fields{
|
||||
severities: "MEDIUM",
|
||||
},
|
||||
args: []string{"centos:7", "alpine:3.10"},
|
||||
logs: []string{
|
||||
"multiple images cannot be specified",
|
||||
},
|
||||
wantErr: "arguments error",
|
||||
},
|
||||
{
|
||||
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,
|
||||
token: tt.fields.token,
|
||||
tokenHeader: tt.fields.tokenHeader,
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_splitCustomHeaders(t *testing.T) {
|
||||
type args struct {
|
||||
headers []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want http.Header
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: args{
|
||||
headers: []string{"x-api-token:foo bar", "Authorization:user:password"},
|
||||
},
|
||||
want: http.Header{
|
||||
"X-Api-Token": []string{"foo bar"},
|
||||
"Authorization": []string{"user:password"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := splitCustomHeaders(tt.args.headers); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("splitCustomHeaders() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
23
internal/client/inject.go
Normal file
23
internal/client/inject.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// +build wireinject
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"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(cacheClient cache.Cache, ospkgCustomHeaders ospkg.CustomHeaders, libraryCustomHeaders library.CustomHeaders,
|
||||
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{}
|
||||
}
|
||||
81
internal/client/run.go
Normal file
81
internal/client/run.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/internal/client/config"
|
||||
"github.com/aquasecurity/trivy/internal/operation"
|
||||
"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)
|
||||
cacheClient := cache.Initialize(c.CacheDir)
|
||||
cacheOperation := operation.NewCache(cacheClient)
|
||||
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
|
||||
|
||||
if c.ClearCache {
|
||||
return cacheOperation.ClearImages()
|
||||
}
|
||||
|
||||
scanOptions := types.ScanOptions{
|
||||
VulnType: c.VulnType,
|
||||
Timeout: c.Timeout,
|
||||
RemoteURL: c.RemoteAddr,
|
||||
}
|
||||
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
|
||||
|
||||
scanner := initializeScanner(cacheClient,
|
||||
ospkg.CustomHeaders(c.CustomHeaders), library.CustomHeaders(c.CustomHeaders),
|
||||
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
|
||||
}
|
||||
36
internal/client/wire_gen.go
Normal file
36
internal/client/wire_gen.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate wire
|
||||
//+build !wireinject
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"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(cacheClient cache.Cache, ospkgCustomHeaders ospkg.CustomHeaders, libraryCustomHeaders library.CustomHeaders, ospkgURL ospkg.RemoteURL, libURL library.RemoteURL) scanner.Scanner {
|
||||
osDetector := ospkg.NewProtobufClient(ospkgURL)
|
||||
detector := ospkg.NewDetector(ospkgCustomHeaders, osDetector)
|
||||
ospkgScanner := ospkg2.NewScanner(detector)
|
||||
libDetector := library.NewProtobufClient(libURL)
|
||||
libraryDetector := library.NewDetector(libraryCustomHeaders, libDetector)
|
||||
libraryScanner := library2.NewScanner(libraryDetector)
|
||||
scannerScanner := scanner.NewScanner(cacheClient, ospkgScanner, libraryScanner)
|
||||
return scannerScanner
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
config := db.Config{}
|
||||
client := vulnerability.NewClient(config)
|
||||
return client
|
||||
}
|
||||
13
internal/operation/inject.go
Normal file
13
internal/operation/inject.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// +build wireinject
|
||||
|
||||
package operation
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
func initializeDBClient(quiet bool) db.Client {
|
||||
wire.Build(db.SuperSet)
|
||||
return db.Client{}
|
||||
}
|
||||
94
internal/operation/operation.go
Normal file
94
internal/operation/operation.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/google/wire"
|
||||
"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"
|
||||
)
|
||||
|
||||
var SuperSet = wire.NewSet(
|
||||
cache.Initialize,
|
||||
NewCache,
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
client cache.Cache
|
||||
}
|
||||
|
||||
func NewCache(client cache.Cache) Cache {
|
||||
return Cache{client: client}
|
||||
}
|
||||
|
||||
func (c Cache) Reset() (err error) {
|
||||
if err := c.ClearDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ClearImages(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Cache) ClearDB() (err error) {
|
||||
log.Logger.Info("Removing DB file...")
|
||||
if err = os.RemoveAll(utils.CacheDir()); err != nil {
|
||||
return xerrors.New("failed to remove cache")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Cache) ClearImages() error {
|
||||
log.Logger.Info("Removing image caches...")
|
||||
if err := c.client.Clear(); err != nil {
|
||||
return xerrors.New("failed to remove image layer cache")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
|
||||
client := initializeDBClient(quiet)
|
||||
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)
|
||||
}
|
||||
log.Logger.Info("Downloading DB...")
|
||||
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
|
||||
}
|
||||
25
internal/operation/wire_gen.go
Normal file
25
internal/operation/wire_gen.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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"
|
||||
"github.com/aquasecurity/trivy/pkg/indicator"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
// Injectors from inject.go:
|
||||
|
||||
func initializeDBClient(quiet bool) db.Client {
|
||||
config := db2.Config{}
|
||||
client := github.NewClient()
|
||||
progressBar := indicator.NewProgressBar(quiet)
|
||||
realClock := clock.RealClock{}
|
||||
dbClient := db.NewClient(config, client, progressBar, realClock)
|
||||
return dbClient
|
||||
}
|
||||
52
internal/server/config/config.go
Normal file
52
internal/server/config/config.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
context *cli.Context
|
||||
|
||||
Quiet bool
|
||||
Debug bool
|
||||
CacheDir string
|
||||
Reset bool
|
||||
DownloadDBOnly bool
|
||||
SkipUpdate bool
|
||||
|
||||
Listen string
|
||||
Token string
|
||||
TokenHeader 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"),
|
||||
TokenHeader: c.String("token-header"),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
return nil
|
||||
}
|
||||
159
internal/server/config/config_test.go
Normal file
159
internal/server/config/config_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
53
internal/server/run.go
Normal file
53
internal/server/run.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"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/rpc/server"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
)
|
||||
|
||||
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())
|
||||
|
||||
// server doesn't have image cache
|
||||
cacheOperation := operation.NewCache(nil)
|
||||
if c.Reset {
|
||||
return cacheOperation.ClearDB()
|
||||
}
|
||||
|
||||
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, true, false, c.SkipUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.DownloadDBOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
return server.ListenAndServe(c.Listen, c)
|
||||
}
|
||||
172
internal/standalone/config/config.go
Normal file
172
internal/standalone/config/config.go
Normal file
@@ -0,0 +1,172 @@
|
||||
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"
|
||||
)
|
||||
|
||||
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.Template != "" {
|
||||
if c.Format == "" {
|
||||
c.logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
|
||||
} else if c.Format != "template" {
|
||||
c.logger.Warnf("--template is ignored because --format %s is specified. Use --template option with --format template option.", c.Format)
|
||||
}
|
||||
}
|
||||
if c.Format == "template" && c.Template == "" {
|
||||
c.logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
|
||||
}
|
||||
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
|
||||
|
||||
// --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")
|
||||
} else if len(args) > 1 {
|
||||
c.logger.Error(`multiple images cannot be specified`)
|
||||
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
|
||||
}
|
||||
361
internal/standalone/config/config_test.go
Normal file
361
internal/standalone/config/config_test.go
Normal file
@@ -0,0 +1,361 @@
|
||||
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: "invalid option combination: --template enabled without --format",
|
||||
fields: fields{
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
AppVersion: "0.0.0",
|
||||
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
severities: "LOW",
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --template and --format json",
|
||||
fields: fields{
|
||||
Format: "json",
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format json is specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
AppVersion: "0.0.0",
|
||||
Format: "json",
|
||||
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
severities: "LOW",
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --format template without --template",
|
||||
fields: fields{
|
||||
Format: "template",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: Config{
|
||||
AppVersion: "0.0.0",
|
||||
Format: "template",
|
||||
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
severities: "LOW",
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
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: multiple image names",
|
||||
fields: fields{
|
||||
severities: "MEDIUM",
|
||||
},
|
||||
args: []string{"centos:7", "alpine:3.10"},
|
||||
logs: []string{
|
||||
"multiple images cannot be specified",
|
||||
},
|
||||
wantErr: "arguments error",
|
||||
},
|
||||
{
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
26
internal/standalone/inject.go
Normal file
26
internal/standalone/inject.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// +build wireinject
|
||||
|
||||
package standalone
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/internal/operation"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
"github.com/aquasecurity/trivy/pkg/vulnerability"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
func initializeCacheClient(cacheDir string) (operation.Cache, error) {
|
||||
wire.Build(operation.SuperSet)
|
||||
return operation.Cache{}, nil
|
||||
}
|
||||
|
||||
func initializeScanner(c cache.Cache) scanner.Scanner {
|
||||
wire.Build(scanner.StandaloneSet)
|
||||
return scanner.Scanner{}
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
wire.Build(vulnerability.SuperSet)
|
||||
return vulnerability.Client{}
|
||||
}
|
||||
107
internal/standalone/run.go
Normal file
107
internal/standalone/run.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package standalone
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
l "log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"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)
|
||||
cacheClient := cache.Initialize(c.CacheDir)
|
||||
cacheOperation := operation.NewCache(cacheClient)
|
||||
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
|
||||
|
||||
if c.Reset {
|
||||
return cacheOperation.Reset()
|
||||
}
|
||||
if c.ClearCache {
|
||||
return cacheOperation.ClearImages()
|
||||
}
|
||||
|
||||
if err = db.Init(c.CacheDir); err != nil {
|
||||
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
|
||||
}
|
||||
|
||||
// download the database file
|
||||
noProgress := c.Quiet || c.NoProgress
|
||||
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, 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(cacheClient)
|
||||
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)
|
||||
}
|
||||
|
||||
template := c.Template
|
||||
|
||||
if strings.HasPrefix(c.Template, "@") {
|
||||
buf, err := ioutil.ReadFile(strings.TrimPrefix(c.Template, "@"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Error retrieving template from path: %w", err)
|
||||
}
|
||||
template = string(buf)
|
||||
}
|
||||
|
||||
if err = report.WriteResults(c.Format, c.Output, results, 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
|
||||
}
|
||||
42
internal/standalone/wire_gen.go
Normal file
42
internal/standalone/wire_gen.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate wire
|
||||
//+build !wireinject
|
||||
|
||||
package standalone
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy/internal/operation"
|
||||
"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 initializeCacheClient(cacheDir string) (operation.Cache, error) {
|
||||
cacheCache := cache.Initialize(cacheDir)
|
||||
operationCache := operation.NewCache(cacheCache)
|
||||
return operationCache, nil
|
||||
}
|
||||
|
||||
func initializeScanner(c cache.Cache) scanner.Scanner {
|
||||
detector := ospkg.Detector{}
|
||||
ospkgScanner := ospkg2.NewScanner(detector)
|
||||
driverFactory := library.DriverFactory{}
|
||||
libraryDetector := library.NewDetector(driverFactory)
|
||||
libraryScanner := library2.NewScanner(libraryDetector)
|
||||
scannerScanner := scanner.NewScanner(c, ospkgScanner, libraryScanner)
|
||||
return scannerScanner
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
config := db.Config{}
|
||||
client := vulnerability.NewClient(config)
|
||||
return client
|
||||
}
|
||||
164
pkg/app.go
164
pkg/app.go
@@ -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 without scanning",
|
||||
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
|
||||
}
|
||||
105
pkg/db/db.go
105
pkg/db/db.go
@@ -5,15 +5,16 @@ 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"
|
||||
"github.com/aquasecurity/trivy/pkg/indicator"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,35 +22,54 @@ const (
|
||||
lightDB = "trivy-light.db.gz"
|
||||
)
|
||||
|
||||
var SuperSet = wire.NewSet(
|
||||
// indicator.ProgressBar
|
||||
indicator.NewProgressBar,
|
||||
|
||||
// clock.Clock
|
||||
wire.Struct(new(clock.RealClock)),
|
||||
wire.Bind(new(clock.Clock), new(clock.RealClock)),
|
||||
|
||||
// db.Config
|
||||
wire.Struct(new(db.Config)),
|
||||
|
||||
// github.Client
|
||||
github.NewClient,
|
||||
wire.Bind(new(github.Operation), new(github.Client)),
|
||||
|
||||
// db.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
|
||||
pb indicator.ProgressBar
|
||||
clock clock.Clock
|
||||
githubClient GitHubOperation
|
||||
}
|
||||
|
||||
func NewClient() Client {
|
||||
func NewClient(dbc db.Config, githubClient github.Operation, pb indicator.ProgressBar, clock clock.Clock) Client {
|
||||
return Client{
|
||||
dbc: db.Config{},
|
||||
clock: clock.RealClock{},
|
||||
githubClient: github.NewClient(),
|
||||
dbc: dbc,
|
||||
githubClient: githubClient,
|
||||
pb: pb,
|
||||
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,70 +78,67 @@ 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 {
|
||||
spinner := utils.NewSpinner(message)
|
||||
spinner.Start()
|
||||
defer spinner.Stop()
|
||||
func (c Client) Download(ctx context.Context, cacheDir string, light bool) error {
|
||||
dbFile := fullDB
|
||||
if light {
|
||||
dbFile = lightDB
|
||||
}
|
||||
|
||||
rc, err := c.githubClient.DownloadDB(ctx, dbFile)
|
||||
rc, size, err := c.githubClient.DownloadDB(ctx, dbFile)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
gr, err := gzip.NewReader(rc)
|
||||
bar := c.pb.Start(int64(size))
|
||||
barReader := bar.NewProxyReader(rc)
|
||||
defer bar.Finish()
|
||||
|
||||
gr, err := gzip.NewReader(barReader)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("invalid gzip file: %w", err)
|
||||
}
|
||||
|
||||
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
22
pkg/db/db_mock.go
Normal 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)
|
||||
|
||||
}
|
||||
@@ -3,12 +3,13 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/indicator"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
@@ -17,6 +18,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 +41,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 +67,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 +77,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 +90,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 +103,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 +116,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 +130,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 +145,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,20 +181,6 @@ func TestClient_Download(t *testing.T) {
|
||||
mockConfig.On("GetMetadata").Return(
|
||||
tc.getMetadata.metadata, tc.getMetadata.err)
|
||||
|
||||
mockGitHubConfig := new(MockGitHubClient)
|
||||
for _, dd := range tc.downloadDB {
|
||||
var rc io.ReadCloser
|
||||
if dd.output.fileName != "" {
|
||||
f, err := os.Open(dd.output.fileName)
|
||||
assert.NoError(t, err, tc.name)
|
||||
rc = f
|
||||
}
|
||||
|
||||
mockGitHubConfig.On("DownloadDB", mock.Anything, dd.input).Return(
|
||||
rc, dd.output.err,
|
||||
)
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "db")
|
||||
require.NoError(t, err, tc.name)
|
||||
defer os.RemoveAll(dir)
|
||||
@@ -296,13 +189,11 @@ func TestClient_Download(t *testing.T) {
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
client := Client{
|
||||
dbc: mockConfig,
|
||||
clock: tc.clock,
|
||||
githubClient: mockGitHubConfig,
|
||||
dbc: mockConfig,
|
||||
clock: tc.clock,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err = client.Download(ctx, "test", dir, tc.light, tc.skip)
|
||||
needsUpdate, err := client.NeedsUpdate(context.Background(), "test", tc.light, tc.skip)
|
||||
|
||||
switch {
|
||||
case tc.expectedError != nil:
|
||||
@@ -311,8 +202,88 @@ func TestClient_Download(t *testing.T) {
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expected, needsUpdate)
|
||||
mockConfig.AssertExpectations(t)
|
||||
mockGitHubConfig.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Download(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
light bool
|
||||
downloadDB []github.DownloadDBExpectation
|
||||
expectedContent []byte
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
light: false,
|
||||
downloadDB: []github.DownloadDBExpectation{
|
||||
{
|
||||
Args: github.DownloadDBInput{FileName: fullDB},
|
||||
ReturnArgs: github.DownloadDBOutput{
|
||||
FileName: "testdata/test.db.gz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DownloadDB returns an error",
|
||||
light: false,
|
||||
downloadDB: []github.DownloadDBExpectation{
|
||||
{
|
||||
Args: github.DownloadDBInput{FileName: fullDB},
|
||||
ReturnArgs: github.DownloadDBOutput{
|
||||
Err: xerrors.New("download failed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("failed to download vulnerability DB: download failed"),
|
||||
},
|
||||
{
|
||||
name: "invalid gzip",
|
||||
light: false,
|
||||
downloadDB: []github.DownloadDBExpectation{
|
||||
{
|
||||
Args: github.DownloadDBInput{FileName: fullDB},
|
||||
ReturnArgs: github.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, err := github.NewMockClient(tc.downloadDB)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
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)
|
||||
|
||||
pb := indicator.NewProgressBar(true)
|
||||
client := NewClient(db.Config{}, mockGitHubClient, pb, nil)
|
||||
ctx := context.Background()
|
||||
err = client.Download(ctx, dir, tc.light)
|
||||
|
||||
switch {
|
||||
case tc.expectedError != nil:
|
||||
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
|
||||
default:
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
mockGitHubClient.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
BIN
pkg/db/testdata/invalid.db.gz
vendored
BIN
pkg/db/testdata/invalid.db.gz
vendored
Binary file not shown.
72
pkg/detector/library/detect.go
Normal file
72
pkg/detector/library/detect.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"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, string, time.Time, []ptypes.Library) ([]types.DetectedVulnerability, error)
|
||||
}
|
||||
|
||||
type Detector struct {
|
||||
driverFactory Factory
|
||||
}
|
||||
|
||||
func NewDetector(factory Factory) Detector {
|
||||
return Detector{driverFactory: factory}
|
||||
}
|
||||
|
||||
func (d Detector) Detect(_ string, filePath string, _ time.Time, 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
|
||||
}
|
||||
50
pkg/detector/library/detector_mock.go
Normal file
50
pkg/detector/library/detector_mock.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
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 {
|
||||
ImageName string
|
||||
FilePath string
|
||||
Created time.Time
|
||||
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.ImageName, e.Args.FilePath, e.Args.Created, e.Args.Libs).Return(
|
||||
e.ReturnArgs.Vulns, e.ReturnArgs.Err)
|
||||
}
|
||||
return mockDetector
|
||||
}
|
||||
|
||||
func (_m *MockDetector) Detect(a, b string, c time.Time, d []ptypes.Library) ([]types.DetectedVulnerability, error) {
|
||||
ret := _m.Called(a, b, c, d)
|
||||
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)
|
||||
}
|
||||
50
pkg/detector/library/driver.go
Normal file
50
pkg/detector/library/driver.go
Normal 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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -36,6 +36,7 @@ var (
|
||||
"3.8": time.Date(2020, 5, 1, 23, 59, 59, 0, time.UTC),
|
||||
"3.9": time.Date(2020, 11, 1, 23, 59, 59, 0, time.UTC),
|
||||
"3.10": time.Date(2021, 5, 1, 23, 59, 59, 0, time.UTC),
|
||||
"3.11": time.Date(2021, 11, 1, 23, 59, 59, 0, time.UTC),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -65,10 +66,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,
|
||||
247
pkg/detector/ospkg/alpine/alpine_test.go
Normal file
247
pkg/detector/ospkg/alpine/alpine_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
86
pkg/detector/ospkg/detect.go
Normal file
86
pkg/detector/ospkg/detect.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package ospkg
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"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/photon"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/ospkg/redhat"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/ospkg/suse"
|
||||
"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, string, time.Time, []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, _ time.Time, 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()
|
||||
case fos.OpenSUSELeap:
|
||||
d = suse.NewScanner(suse.OpenSUSE)
|
||||
case fos.SLES:
|
||||
d = suse.NewScanner(suse.SUSEEnterpriseLinux)
|
||||
case fos.Photon:
|
||||
d = photon.NewScanner()
|
||||
default:
|
||||
log.Logger.Warnf("unsupported os : %s", osFamily)
|
||||
return nil
|
||||
}
|
||||
return d
|
||||
}
|
||||
52
pkg/detector/ospkg/detector_mock.go
Normal file
52
pkg/detector/ospkg/detector_mock.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package ospkg
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockDetector struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type DetectInput struct {
|
||||
ImageName string
|
||||
OSFamily string
|
||||
OSName string
|
||||
Created time.Time
|
||||
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.ImageName, e.Args.OSFamily, e.Args.OSName, e.Args.Created, e.Args.Pkgs).Return(
|
||||
e.ReturnArgs.Vulns, e.ReturnArgs.Eosl, e.ReturnArgs.Err)
|
||||
}
|
||||
return mockDetector
|
||||
}
|
||||
|
||||
func (_m *MockDetector) Detect(a string, b string, c string, d time.Time, e []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
|
||||
ret := _m.Called(a, b, c, d, e)
|
||||
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)
|
||||
}
|
||||
94
pkg/detector/ospkg/oracle/oracle.go
Normal file
94
pkg/detector/ospkg/oracle/oracle.go
Normal 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)
|
||||
}
|
||||
96
pkg/detector/ospkg/oracle/oracle_test.go
Normal file
96
pkg/detector/ospkg/oracle/oracle_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
68
pkg/detector/ospkg/photon/photon.go
Normal file
68
pkg/detector/ospkg/photon/photon.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package photon
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/photon"
|
||||
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{}
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
vs dbTypes.VulnSrc
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
func NewScanner() *Scanner {
|
||||
return &Scanner{
|
||||
vs: photon.NewVulnSrc(),
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
|
||||
log.Logger.Info("Detecting Photon Linux vulnerabilities...")
|
||||
log.Logger.Debugf("Photon Linux: os version: %s", osVer)
|
||||
log.Logger.Debugf("Photon 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 Photon 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 {
|
||||
return true
|
||||
}
|
||||
@@ -57,25 +57,42 @@ func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.Detecte
|
||||
|
||||
var vulns []types.DetectedVulnerability
|
||||
for _, pkg := range pkgs {
|
||||
// For Red Hat Security Data API containing only source package names
|
||||
advisories, err := s.vs.Get(osVer, pkg.SrcName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err)
|
||||
}
|
||||
|
||||
installed := utils.FormatSrcVersion(pkg)
|
||||
installed := utils.FormatVersion(pkg)
|
||||
installedVersion := version.NewVersion(installed)
|
||||
for _, adv := range advisories {
|
||||
fixedVersion := version.NewVersion(adv.FixedVersion)
|
||||
|
||||
for _, adv := range advisories {
|
||||
if adv.FixedVersion != "" {
|
||||
continue
|
||||
}
|
||||
vuln := types.DetectedVulnerability{
|
||||
VulnerabilityID: adv.VulnerabilityID,
|
||||
PkgName: pkg.Name,
|
||||
InstalledVersion: installed,
|
||||
}
|
||||
vulns = append(vulns, vuln)
|
||||
}
|
||||
|
||||
// For Red Hat OVAL containing only binary package names
|
||||
advisories, err = s.vs.Get(osVer, pkg.Name)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err)
|
||||
}
|
||||
|
||||
for _, adv := range advisories {
|
||||
fixedVersion := version.NewVersion(adv.FixedVersion)
|
||||
if installedVersion.LessThan(fixedVersion) {
|
||||
vuln.FixedVersion = fixedVersion.String()
|
||||
vulns = append(vulns, vuln)
|
||||
} else if adv.FixedVersion == "" {
|
||||
vuln := types.DetectedVulnerability{
|
||||
VulnerabilityID: adv.VulnerabilityID,
|
||||
PkgName: pkg.Name,
|
||||
InstalledVersion: installed,
|
||||
FixedVersion: fixedVersion.String(),
|
||||
}
|
||||
vulns = append(vulns, vuln)
|
||||
}
|
||||
}
|
||||
310
pkg/detector/ospkg/redhat/redhat_test.go
Normal file
310
pkg/detector/ospkg/redhat/redhat_test.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package redhat
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
get []dbTypes.GetExpectation
|
||||
want []types.DetectedVulnerability
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy path: src pkg name is different from bin pkg name",
|
||||
args: args{
|
||||
osVer: "7.6",
|
||||
pkgs: []analyzer.Package{
|
||||
{
|
||||
Name: "vim-minimal",
|
||||
Version: "7.4.160",
|
||||
Release: "5.el7",
|
||||
Epoch: 2,
|
||||
Arch: "x86_64",
|
||||
SrcName: "vim",
|
||||
SrcVersion: "7.4.160",
|
||||
SrcRelease: "5.el7",
|
||||
SrcEpoch: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
get: []dbTypes.GetExpectation{
|
||||
{
|
||||
Args: dbTypes.GetArgs{
|
||||
Release: "7",
|
||||
PkgName: "vim",
|
||||
},
|
||||
Returns: dbTypes.GetReturns{
|
||||
Advisories: []dbTypes.Advisory{
|
||||
{
|
||||
VulnerabilityID: "CVE-2017-5953",
|
||||
FixedVersion: "",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2017-6350",
|
||||
FixedVersion: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: dbTypes.GetArgs{
|
||||
Release: "7",
|
||||
PkgName: "vim-minimal",
|
||||
},
|
||||
Returns: dbTypes.GetReturns{
|
||||
Advisories: []dbTypes.Advisory{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-12735",
|
||||
FixedVersion: "2:7.4.160-6.el7_6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2017-5953",
|
||||
PkgName: "vim-minimal",
|
||||
InstalledVersion: "2:7.4.160-5.el7",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2017-6350",
|
||||
PkgName: "vim-minimal",
|
||||
InstalledVersion: "2:7.4.160-5.el7",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-12735",
|
||||
PkgName: "vim-minimal",
|
||||
InstalledVersion: "2:7.4.160-5.el7",
|
||||
FixedVersion: "2:7.4.160-6.el7_6",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path: src pkg name is the same as bin pkg name",
|
||||
args: args{
|
||||
osVer: "6.5",
|
||||
pkgs: []analyzer.Package{
|
||||
{
|
||||
Name: "nss",
|
||||
Version: "3.36.0",
|
||||
Release: "7.1.el7_6",
|
||||
Epoch: 0,
|
||||
Arch: "x86_64",
|
||||
SrcName: "nss",
|
||||
SrcVersion: "3.36.0",
|
||||
SrcRelease: "7.4.160",
|
||||
SrcEpoch: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
get: []dbTypes.GetExpectation{
|
||||
{
|
||||
Args: dbTypes.GetArgs{
|
||||
Release: "6",
|
||||
PkgName: "nss",
|
||||
},
|
||||
Returns: dbTypes.GetReturns{
|
||||
Advisories: []dbTypes.Advisory{
|
||||
{
|
||||
VulnerabilityID: "CVE-2015-2808",
|
||||
FixedVersion: "",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2016-2183",
|
||||
FixedVersion: "",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-12404",
|
||||
FixedVersion: "3.44.0-4.el7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2015-2808",
|
||||
PkgName: "nss",
|
||||
InstalledVersion: "3.36.0-7.1.el7_6",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2016-2183",
|
||||
PkgName: "nss",
|
||||
InstalledVersion: "3.36.0-7.1.el7_6",
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-12404",
|
||||
PkgName: "nss",
|
||||
InstalledVersion: "3.36.0-7.1.el7_6",
|
||||
FixedVersion: "3.44.0-4.el7",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sad path: Get returns an error",
|
||||
args: args{
|
||||
osVer: "5",
|
||||
pkgs: []analyzer.Package{
|
||||
{
|
||||
Name: "nss",
|
||||
Version: "3.36.0",
|
||||
Release: "7.1.el7_6",
|
||||
Epoch: 0,
|
||||
Arch: "x86_64",
|
||||
SrcName: "nss",
|
||||
SrcVersion: "3.36.0",
|
||||
SrcRelease: "7.4.160",
|
||||
SrcEpoch: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
get: []dbTypes.GetExpectation{
|
||||
{
|
||||
Args: dbTypes.GetArgs{
|
||||
Release: "5",
|
||||
PkgName: "nss",
|
||||
},
|
||||
Returns: dbTypes.GetReturns{
|
||||
Err: xerrors.New("error"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockVs := new(dbTypes.MockVulnSrc)
|
||||
mockVs.ApplyGetExpectations(tt.get)
|
||||
s := &Scanner{
|
||||
vs: mockVs,
|
||||
}
|
||||
got, err := s.Detect(tt.args.osVer, tt.args.pkgs)
|
||||
require.Equal(t, tt.wantErr, err != nil)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanner_IsSupportedVersion(t *testing.T) {
|
||||
vectors := map[string]struct {
|
||||
now time.Time
|
||||
osFamily string
|
||||
osVersion string
|
||||
expected bool
|
||||
}{
|
||||
"centos5": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "5.0",
|
||||
expected: false,
|
||||
},
|
||||
"centos6": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "6.7",
|
||||
expected: true,
|
||||
},
|
||||
"centos6 (eol ends)": {
|
||||
now: time.Date(2020, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "6.7",
|
||||
expected: false,
|
||||
},
|
||||
"centos7": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "7.5",
|
||||
expected: true,
|
||||
},
|
||||
"centos8": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "8.0",
|
||||
expected: true,
|
||||
},
|
||||
"two dots": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "centos",
|
||||
osVersion: "8.0.1",
|
||||
expected: true,
|
||||
},
|
||||
"redhat5": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "5.0",
|
||||
expected: true,
|
||||
},
|
||||
"redhat6": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "6.7",
|
||||
expected: true,
|
||||
},
|
||||
"redhat6 (eol ends)": {
|
||||
now: time.Date(2024, 7, 1, 0, 0, 0, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "6.7",
|
||||
expected: false,
|
||||
},
|
||||
"redhat7": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "7.5",
|
||||
expected: true,
|
||||
},
|
||||
"redhat8": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "8.0",
|
||||
expected: true,
|
||||
},
|
||||
"no dot": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "redhat",
|
||||
osVersion: "8",
|
||||
expected: true,
|
||||
},
|
||||
"debian": {
|
||||
now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
osFamily: "debian",
|
||||
osVersion: "8",
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
128
pkg/detector/ospkg/suse/suse.go
Normal file
128
pkg/detector/ospkg/suse/suse.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package suse
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
fos "github.com/aquasecurity/fanal/analyzer/os"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
susecvrf "github.com/aquasecurity/trivy-db/pkg/vulnsrc/suse-cvrf"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
version "github.com/knqyf263/go-rpm-version"
|
||||
"golang.org/x/xerrors"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
var (
|
||||
slesEolDates = map[string]time.Time{
|
||||
// Source: https://www.suse.com/lifecycle/
|
||||
"10": time.Date(2007, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
"10.1": time.Date(2008, 11, 30, 23, 59, 59, 0, time.UTC),
|
||||
"10.2": time.Date(2010, 4, 11, 23, 59, 59, 0, time.UTC),
|
||||
"10.3": time.Date(2011, 10, 11, 23, 59, 59, 0, time.UTC),
|
||||
"10.4": time.Date(2013, 7, 31, 23, 59, 59, 0, time.UTC),
|
||||
"11": time.Date(2010, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
"11.1": time.Date(2012, 8, 31, 23, 59, 59, 0, time.UTC),
|
||||
"11.2": time.Date(2014, 1, 31, 23, 59, 59, 0, time.UTC),
|
||||
"11.3": time.Date(2016, 1, 31, 23, 59, 59, 0, time.UTC),
|
||||
"11.4": time.Date(2019, 3, 31, 23, 59, 59, 0, time.UTC),
|
||||
"12": time.Date(2016, 6, 30, 23, 59, 59, 0, time.UTC),
|
||||
"12.1": time.Date(2017, 5, 31, 23, 59, 59, 0, time.UTC),
|
||||
"12.2": time.Date(2018, 3, 31, 23, 59, 59, 0, time.UTC),
|
||||
"12.3": time.Date(2019, 1, 30, 23, 59, 59, 0, time.UTC),
|
||||
// 6 months after SLES12 SP5 release
|
||||
// "12.4": time.Date(2024, 10, 31, 23, 59, 59, 0, time.UTC),
|
||||
"15": time.Date(2019, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
// 6 months after SLES 15 SP2 release
|
||||
// "15.1": time.Date(2028, 7, 31, 23, 59, 59, 0, time.UTC),
|
||||
}
|
||||
|
||||
opensuseEolDates = map[string]time.Time{
|
||||
// Source: https://en.opensuse.org/Lifetime
|
||||
"42.1": time.Date(2017, 5, 17, 23, 59, 59, 0, time.UTC),
|
||||
"42.2": time.Date(2018, 1, 26, 23, 59, 59, 0, time.UTC),
|
||||
"42.3": time.Date(2019, 6, 30, 23, 59, 59, 0, time.UTC),
|
||||
"15.0": time.Date(2019, 12, 3, 23, 59, 59, 0, time.UTC),
|
||||
"15.1": time.Date(2020, 11, 30, 23, 59, 59, 0, time.UTC),
|
||||
"15.2": time.Date(2021, 11, 30, 23, 59, 59, 0, time.UTC),
|
||||
}
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
vs dbTypes.VulnSrc
|
||||
clock clock.Clock
|
||||
family string
|
||||
}
|
||||
|
||||
type SUSEType int
|
||||
|
||||
const (
|
||||
SUSEEnterpriseLinux SUSEType = iota
|
||||
OpenSUSE
|
||||
)
|
||||
|
||||
func NewScanner(t SUSEType) *Scanner {
|
||||
switch t {
|
||||
case SUSEEnterpriseLinux:
|
||||
return &Scanner{
|
||||
vs: susecvrf.NewVulnSrc(susecvrf.SUSEEnterpriseLinux),
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
case OpenSUSE:
|
||||
return &Scanner{
|
||||
vs: susecvrf.NewVulnSrc(susecvrf.OpenSUSE),
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
|
||||
log.Logger.Info("Detecting SUSE vulnerabilities...")
|
||||
log.Logger.Debugf("SUSE: os version: %s", osVer)
|
||||
log.Logger.Debugf("SUSE: 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 SUSE 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 {
|
||||
var eolDate time.Time
|
||||
var ok bool
|
||||
|
||||
if osFamily == fos.SLES {
|
||||
eolDate, ok = slesEolDates[osVer]
|
||||
} else if osFamily == fos.OpenSUSELeap {
|
||||
eolDate, ok = opensuseEolDates[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(eolDate)
|
||||
}
|
||||
92
pkg/detector/ospkg/suse/suse_test.go
Normal file
92
pkg/detector/ospkg/suse/suse_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package suse
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
susecvrf "github.com/aquasecurity/trivy-db/pkg/vulnsrc/suse-cvrf"
|
||||
"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
|
||||
distribution susecvrf.Distribution
|
||||
expected bool
|
||||
}{
|
||||
"opensuse.leap42.3": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "opensuse.leap",
|
||||
osVersion: "42.3",
|
||||
distribution: susecvrf.OpenSUSE,
|
||||
expected: true,
|
||||
},
|
||||
"opensuse.leap15": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "opensuse.leap",
|
||||
osVersion: "15.0",
|
||||
distribution: susecvrf.OpenSUSE,
|
||||
expected: true,
|
||||
},
|
||||
"opensuse.leap15.1": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "opensuse.leap",
|
||||
osVersion: "15.1",
|
||||
distribution: susecvrf.OpenSUSE,
|
||||
expected: true,
|
||||
},
|
||||
"opensuse.leap15.1-sametime": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2020, 11, 30, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "opensuse.leap",
|
||||
osVersion: "15.1",
|
||||
distribution: susecvrf.OpenSUSE,
|
||||
expected: false,
|
||||
},
|
||||
"sles12.3": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "suse linux enterprise server",
|
||||
osVersion: "12.3",
|
||||
distribution: susecvrf.SUSEEnterpriseLinux,
|
||||
expected: false,
|
||||
},
|
||||
"sles15": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "suse linux enterprise server",
|
||||
osVersion: "15",
|
||||
distribution: susecvrf.SUSEEnterpriseLinux,
|
||||
expected: true,
|
||||
},
|
||||
"unknown": {
|
||||
clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)),
|
||||
osFamily: "oracle",
|
||||
osVersion: "unknown",
|
||||
distribution: susecvrf.SUSEEnterpriseLinux,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for testName, v := range vectors {
|
||||
s := &Scanner{
|
||||
vs: susecvrf.NewVulnSrc(v.distribution),
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
@@ -41,6 +42,10 @@ 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, int, error)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Repository RepositoryInterface
|
||||
}
|
||||
@@ -68,11 +73,11 @@ func NewClient() Client {
|
||||
}
|
||||
}
|
||||
|
||||
func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, error) {
|
||||
func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error) {
|
||||
options := github.ListOptions{}
|
||||
releases, _, err := c.Repository.ListReleases(ctx, &options)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to list releases: %w", err)
|
||||
return nil, 0, xerrors.Errorf("failed to list releases: %w", err)
|
||||
}
|
||||
|
||||
sort.Slice(releases, func(i, j int) bool {
|
||||
@@ -87,37 +92,42 @@ func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser,
|
||||
}
|
||||
|
||||
for _, asset := range release.Assets {
|
||||
rc, err := c.downloadAsset(ctx, asset, fileName)
|
||||
rc, size, err := c.downloadAsset(ctx, asset, fileName)
|
||||
if err != nil {
|
||||
log.Logger.Debug(err)
|
||||
continue
|
||||
}
|
||||
return rc, nil
|
||||
return rc, size, nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil, xerrors.New("DB file not found")
|
||||
return nil, 0, xerrors.New("DB file not found")
|
||||
}
|
||||
|
||||
func (c Client) downloadAsset(ctx context.Context, asset github.ReleaseAsset, fileName string) (io.ReadCloser, error) {
|
||||
func (c Client) downloadAsset(ctx context.Context, asset github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
|
||||
log.Logger.Debugf("asset name: %s", asset.GetName())
|
||||
if asset.GetName() != fileName {
|
||||
return nil, xerrors.New("file name doesn't match")
|
||||
return nil, 0, xerrors.New("file name doesn't match")
|
||||
}
|
||||
|
||||
rc, url, err := c.Repository.DownloadAsset(ctx, asset.GetID())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to download the asset: %w", err)
|
||||
return nil, 0, xerrors.Errorf("unable to download the asset: %w", err)
|
||||
}
|
||||
|
||||
if rc != nil {
|
||||
return rc, nil
|
||||
return rc, asset.GetSize(), nil
|
||||
}
|
||||
|
||||
log.Logger.Debugf("asset URL: %s", url)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
return nil, xerrors.Errorf("unable to download the asset via URL: %w", err)
|
||||
return nil, 0, xerrors.Errorf("unable to download the asset via URL: %w", err)
|
||||
}
|
||||
return resp.Body, nil
|
||||
|
||||
size, err := strconv.Atoi(resp.Header.Get("Content-Length"))
|
||||
if err != nil {
|
||||
return nil, 0, xerrors.Errorf("invalid size: %w", err)
|
||||
}
|
||||
return resp.Body, size, nil
|
||||
}
|
||||
|
||||
57
pkg/github/github_mock.go
Normal file
57
pkg/github/github_mock.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type DownloadDBInput struct {
|
||||
FileName string
|
||||
}
|
||||
type DownloadDBOutput struct {
|
||||
FileName string
|
||||
Size int
|
||||
Err error
|
||||
}
|
||||
type DownloadDBExpectation struct {
|
||||
Args DownloadDBInput
|
||||
ReturnArgs DownloadDBOutput
|
||||
}
|
||||
|
||||
func NewMockClient(downloadDBExpectations []DownloadDBExpectation) (*MockClient, error) {
|
||||
mockDetector := new(MockClient)
|
||||
for _, e := range downloadDBExpectations {
|
||||
var rc io.ReadCloser
|
||||
if e.ReturnArgs.FileName != "" {
|
||||
f, err := os.Open(e.ReturnArgs.FileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc = f
|
||||
}
|
||||
|
||||
mockDetector.On("DownloadDB", mock.Anything, e.Args.FileName).Return(
|
||||
rc, e.ReturnArgs.Size, e.ReturnArgs.Err)
|
||||
}
|
||||
return mockDetector, nil
|
||||
}
|
||||
|
||||
func (_m *MockClient) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error) {
|
||||
ret := _m.Called(ctx, fileName)
|
||||
ret0 := ret.Get(0)
|
||||
if ret0 == nil {
|
||||
return nil, ret.Int(1), ret.Error(2)
|
||||
}
|
||||
rc, ok := ret0.(io.ReadCloser)
|
||||
if !ok {
|
||||
return nil, ret.Int(1), ret.Error(2)
|
||||
}
|
||||
return rc, ret.Int(1), ret.Error(2)
|
||||
}
|
||||
@@ -451,7 +451,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
rc, err := client.DownloadDB(ctx, tc.fileName)
|
||||
rc, _, err := client.DownloadDB(ctx, tc.fileName)
|
||||
|
||||
switch {
|
||||
case tc.expectedError != nil:
|
||||
|
||||
40
pkg/indicator/progress.go
Normal file
40
pkg/indicator/progress.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package indicator
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/cheggaaa/pb/v3"
|
||||
)
|
||||
|
||||
type ProgressBar struct {
|
||||
quiet bool
|
||||
}
|
||||
|
||||
func NewProgressBar(quiet bool) ProgressBar {
|
||||
return ProgressBar{quiet: quiet}
|
||||
}
|
||||
|
||||
func (p ProgressBar) Start(total int64) Bar {
|
||||
if p.quiet {
|
||||
return Bar{}
|
||||
}
|
||||
bar := pb.Full.Start64(total)
|
||||
return Bar{bar: bar}
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
bar *pb.ProgressBar
|
||||
}
|
||||
|
||||
func (b Bar) NewProxyReader(r io.Reader) io.Reader {
|
||||
if b.bar == nil {
|
||||
return r
|
||||
}
|
||||
return b.bar.NewProxyReader(r)
|
||||
}
|
||||
func (b Bar) Finish() {
|
||||
if b.bar == nil {
|
||||
return
|
||||
}
|
||||
b.bar.Finish()
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
type Results []Result
|
||||
|
||||
type Result struct {
|
||||
FileName string `json:"Target"`
|
||||
Target string `json:"Target"`
|
||||
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities"`
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ func (tw TableWriter) write(result Result) {
|
||||
results = append(results, r)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n", result.FileName)
|
||||
fmt.Println(strings.Repeat("=", len(result.FileName)))
|
||||
fmt.Printf("\n%s\n", result.Target)
|
||||
fmt.Println(strings.Repeat("=", len(result.Target)))
|
||||
fmt.Printf("Total: %d (%s)\n\n", len(result.Vulnerabilities), strings.Join(results, ", "))
|
||||
|
||||
if len(result.Vulnerabilities) == 0 {
|
||||
|
||||
@@ -117,7 +117,7 @@ func TestReportWriter_Table(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
inputResults := report.Results{
|
||||
{
|
||||
FileName: "foo",
|
||||
Target: "foo",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func TestReportWriter_JSON(t *testing.T) {
|
||||
},
|
||||
expectedJSON: report.Results{
|
||||
report.Result{
|
||||
FileName: "foojson",
|
||||
Target: "foojson",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
@@ -179,7 +179,7 @@ func TestReportWriter_JSON(t *testing.T) {
|
||||
|
||||
inputResults := report.Results{
|
||||
{
|
||||
FileName: "foojson",
|
||||
Target: "foojson",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
@@ -236,7 +236,7 @@ func TestReportWriter_Template(t *testing.T) {
|
||||
tmplWritten := bytes.Buffer{}
|
||||
inputResults := report.Results{
|
||||
{
|
||||
FileName: "foojson",
|
||||
Target: "foojson",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
20
pkg/rpc/client/headers.go
Normal file
20
pkg/rpc/client/headers.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/twitchtv/twirp"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
func WithCustomHeaders(ctx context.Context, customHeaders http.Header) context.Context {
|
||||
// Attach the headers to a context
|
||||
ctxWithToken, err := twirp.WithHTTPRequestHeaders(ctx, customHeaders)
|
||||
if err != nil {
|
||||
log.Logger.Warnf("twirp error setting headers: %s", err)
|
||||
return ctx
|
||||
}
|
||||
return ctxWithToken
|
||||
}
|
||||
59
pkg/rpc/client/headers_test.go
Normal file
59
pkg/rpc/client/headers_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
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 TestWithCustomHeaders(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
customHeaders http.Header
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want http.Header
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
customHeaders: http.Header{
|
||||
"Trivy-Token": []string{"token"},
|
||||
},
|
||||
},
|
||||
want: http.Header{
|
||||
"Trivy-Token": []string{"token"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sad path, invalid headers passed in",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
customHeaders: http.Header{
|
||||
"Content-Type": []string{"token"},
|
||||
},
|
||||
},
|
||||
want: http.Header(nil),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gotCtx := WithCustomHeaders(tt.args.ctx, tt.args.customHeaders)
|
||||
header, _ := twirp.HTTPRequestHeaders(gotCtx)
|
||||
assert.Equal(t, tt.want, header, tt.name)
|
||||
}
|
||||
}
|
||||
70
pkg/rpc/client/library/client.go
Normal file
70
pkg/rpc/client/library/client.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
"github.com/google/wire"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
depptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
detector "github.com/aquasecurity/trivy/pkg/detector/library"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
r "github.com/aquasecurity/trivy/pkg/rpc"
|
||||
"github.com/aquasecurity/trivy/pkg/rpc/client"
|
||||
"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 CustomHeaders http.Header
|
||||
|
||||
type Detector struct {
|
||||
customHeaders CustomHeaders
|
||||
client rpc.LibDetector
|
||||
}
|
||||
|
||||
func NewDetector(customHeaders CustomHeaders, detector rpc.LibDetector) Detector {
|
||||
return Detector{customHeaders: customHeaders, client: detector}
|
||||
}
|
||||
|
||||
func (d Detector) Detect(imageName, filePath string, created time.Time, libs []depptypes.Library) ([]types.DetectedVulnerability, error) {
|
||||
ctx := client.WithCustomHeaders(context.Background(), http.Header(d.customHeaders))
|
||||
|
||||
var res *rpc.DetectResponse
|
||||
err := r.Retry(func() error {
|
||||
var err error
|
||||
res, err = d.client.Detect(ctx, &rpc.LibDetectRequest{
|
||||
ImageName: imageName,
|
||||
FilePath: filePath,
|
||||
Libraries: r.ConvertToRpcLibraries(libs),
|
||||
Created: func() *timestamp.Timestamp {
|
||||
t, err := ptypes.TimestampProto(created)
|
||||
if err != nil {
|
||||
log.Logger.Warnf("invalid timestamp: %s", err)
|
||||
}
|
||||
return t
|
||||
}(),
|
||||
})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
|
||||
}
|
||||
|
||||
return r.ConvertFromRpcVulns(res.Vulnerabilities), nil
|
||||
}
|
||||
182
pkg/rpc/client/library/client_test.go
Normal file
182
pkg/rpc/client/library/client_test.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
deptypes "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 {
|
||||
customHeaders CustomHeaders
|
||||
}
|
||||
|
||||
type args struct {
|
||||
imageName string
|
||||
filePath string
|
||||
created time.Time
|
||||
libs []deptypes.Library
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
detect detect
|
||||
want []types.DetectedVulnerability
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
fields: fields{
|
||||
customHeaders: CustomHeaders{
|
||||
"Trivy-Token": []string{"token"},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
imageName: "/tmp/alpine.tar",
|
||||
filePath: "app/Pipfile.lock",
|
||||
created: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
libs: []deptypes.Library{
|
||||
{Name: "django", Version: "3.0.0"},
|
||||
},
|
||||
},
|
||||
detect: detect{
|
||||
input: detectInput{req: &detector.LibDetectRequest{
|
||||
ImageName: "/tmp/alpine.tar",
|
||||
FilePath: "app/Pipfile.lock",
|
||||
Created: func() *timestamp.Timestamp {
|
||||
d := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
t, _ := ptypes.TimestampProto(d)
|
||||
return t
|
||||
}(),
|
||||
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{
|
||||
imageName: "/tmp/alpine.tar",
|
||||
filePath: "app/Pipfile.lock",
|
||||
created: time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC),
|
||||
libs: []deptypes.Library{
|
||||
{Name: "django", Version: "3.0.0"},
|
||||
},
|
||||
},
|
||||
detect: detect{
|
||||
input: detectInput{req: &detector.LibDetectRequest{
|
||||
ImageName: "/tmp/alpine.tar",
|
||||
FilePath: "app/Pipfile.lock",
|
||||
Libraries: []*detector.Library{
|
||||
{Name: "django", Version: "3.0.0"},
|
||||
},
|
||||
Created: func() *timestamp.Timestamp {
|
||||
d := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC)
|
||||
t, _ := ptypes.TimestampProto(d)
|
||||
return t
|
||||
}(),
|
||||
},
|
||||
},
|
||||
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.customHeaders, mockDetector)
|
||||
got, err := d.Detect(tt.args.imageName, tt.args.filePath, tt.args.created, 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
73
pkg/rpc/client/ospkg/client.go
Normal file
73
pkg/rpc/client/ospkg/client.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package ospkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
"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/rpc/client"
|
||||
"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 CustomHeaders http.Header
|
||||
|
||||
type Detector struct {
|
||||
customHeaders CustomHeaders
|
||||
client rpc.OSDetector
|
||||
}
|
||||
|
||||
func NewDetector(customHeaders CustomHeaders, detector rpc.OSDetector) Detector {
|
||||
return Detector{customHeaders: customHeaders, client: detector}
|
||||
}
|
||||
|
||||
func (d Detector) Detect(imageName, osFamily, osName string, created time.Time, pkgs []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
|
||||
ctx := client.WithCustomHeaders(context.Background(), http.Header(d.customHeaders))
|
||||
|
||||
var res *rpc.DetectResponse
|
||||
err := r.Retry(func() error {
|
||||
var err error
|
||||
res, err = d.client.Detect(ctx, &rpc.OSDetectRequest{
|
||||
ImageName: imageName,
|
||||
OsFamily: osFamily,
|
||||
OsName: osName,
|
||||
Created: func() *timestamp.Timestamp {
|
||||
t, err := ptypes.TimestampProto(created)
|
||||
if err != nil {
|
||||
log.Logger.Warnf("invalid timestamp: %s", err)
|
||||
}
|
||||
return t
|
||||
}(),
|
||||
Packages: r.ConvertToRpcPkgs(pkgs),
|
||||
})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
|
||||
}
|
||||
|
||||
return r.ConvertFromRpcVulns(res.Vulnerabilities), res.Eosl, nil
|
||||
}
|
||||
202
pkg/rpc/client/ospkg/client_test.go
Normal file
202
pkg/rpc/client/ospkg/client_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package ospkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
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 {
|
||||
customHeaders CustomHeaders
|
||||
}
|
||||
type args struct {
|
||||
imageName string
|
||||
osFamily string
|
||||
osName string
|
||||
created time.Time
|
||||
pkgs []analyzer.Package
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
detect detect
|
||||
want []types.DetectedVulnerability
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
fields: fields{
|
||||
customHeaders: CustomHeaders{
|
||||
"Trivy-Token": []string{"token"},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
imageName: "alpine:3.10.2",
|
||||
osFamily: "alpine",
|
||||
osName: "3.10.2",
|
||||
created: time.Unix(1581498560, 0),
|
||||
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",
|
||||
ImageName: "alpine:3.10.2",
|
||||
Created: func() *timestamp.Timestamp {
|
||||
t, _ := ptypes.TimestampProto(time.Unix(1581498560, 0))
|
||||
return t
|
||||
}(),
|
||||
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{
|
||||
imageName: "alpine:3.10.2",
|
||||
osFamily: "alpine",
|
||||
osName: "3.10.2",
|
||||
created: time.Unix(1581498560, 0),
|
||||
pkgs: []analyzer.Package{
|
||||
{
|
||||
Name: "openssl",
|
||||
Version: "1.0.1e",
|
||||
Release: "1",
|
||||
Epoch: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
detect: detect{
|
||||
input: detectInput{
|
||||
req: &detector.OSDetectRequest{
|
||||
ImageName: "alpine:3.10.2",
|
||||
OsFamily: "alpine",
|
||||
OsName: "3.10.2",
|
||||
Created: func() *timestamp.Timestamp {
|
||||
t, _ := ptypes.TimestampProto(time.Unix(1581498560, 0))
|
||||
return t
|
||||
}(),
|
||||
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.customHeaders, mockDetector)
|
||||
got, _, err := d.Detect(tt.args.imageName, tt.args.osFamily, tt.args.osName, tt.args.created, 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
110
pkg/rpc/convert.go
Normal file
110
pkg/rpc/convert.go
Normal 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
309
pkg/rpc/convert_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
40
pkg/rpc/retry.go
Normal file
40
pkg/rpc/retry.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/twitchtv/twirp"
|
||||
)
|
||||
|
||||
const (
|
||||
maxRetries = 10
|
||||
)
|
||||
|
||||
func Retry(f func() error) error {
|
||||
operation := func() error {
|
||||
err := f()
|
||||
if err != nil {
|
||||
twerr, ok := err.(twirp.Error)
|
||||
if !ok {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
if twerr.Code() == twirp.Unavailable {
|
||||
return err
|
||||
}
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
b := backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries)
|
||||
err := backoff.RetryNotify(operation, b, func(err error, _ time.Duration) {
|
||||
log.Logger.Warn(err)
|
||||
log.Logger.Info("Retrying HTTP request...")
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
25
pkg/rpc/server/inject.go
Normal file
25
pkg/rpc/server/inject.go
Normal 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(quiet bool) dbWorker {
|
||||
wire.Build(SuperSet)
|
||||
return dbWorker{}
|
||||
}
|
||||
43
pkg/rpc/server/library/server.go
Normal file
43
pkg/rpc/server/library/server.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"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(_ context.Context, req *proto.LibDetectRequest) (res *proto.DetectResponse, err error) {
|
||||
vulns, err := s.detector.Detect("", req.FilePath, time.Time{}, 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
|
||||
}
|
||||
137
pkg/rpc/server/library/server_test.go
Normal file
137
pkg/rpc/server/library/server_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
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{
|
||||
ImageName: "alpine:3.10",
|
||||
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{
|
||||
ImageName: "alpine:3.10",
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
43
pkg/rpc/server/ospkg/server.go
Normal file
43
pkg/rpc/server/ospkg/server.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package ospkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"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, time.Time{}, 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
|
||||
}
|
||||
125
pkg/rpc/server/ospkg/server_test.go
Normal file
125
pkg/rpc/server/ospkg/server_test.go
Normal 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
137
pkg/rpc/server/server.go
Normal 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(true)
|
||||
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, c.TokenHeader))
|
||||
|
||||
libHandler := rpc.NewLibDetectorServer(initializeLibServer(), nil)
|
||||
mux.Handle(rpc.LibDetectorPathPrefix, withToken(withWaitGroup(libHandler), c.Token, c.TokenHeader))
|
||||
|
||||
log.Logger.Infof("Listening %s...", addr)
|
||||
|
||||
return http.ListenAndServe(addr, mux)
|
||||
}
|
||||
|
||||
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if token != "" && token != r.Header.Get(tokenHeader) {
|
||||
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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user