Compare commits

..

10 Commits

Author SHA1 Message Date
Teppei Fukuda
020c4a3b14 fix(app): add ArgsUsage (#508) 2020-06-02 21:28:14 +03:00
Teppei Fukuda
2f2d1a908b feat: support repository and filesystem scan (#503)
* refactor: embed config

* refactor: replace image and layer with artifact and blob

* feat(config): add ArtifactConfig

* fix(scanner): use Artifact

* test(scanner): update mocks

* feat: add repo and fs subcommands

* chore(mod): update

* refactor: fix warn message

* feat(cli): add --no-progress to repo and fs

* mod: Update fanal dependency

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

Co-authored-by: Simarpreet Singh <simar@linux.com>
2020-05-30 19:46:12 +03:00
Masahiro331
03ad8a3cd0 Add GHSA support (#467)
* Change library advisory use github security advisory

* Add java scanner

* Add multi vulnsrc support

* Fix null pointer exception

* Add ghsa mock test

* Delete nuget & java

* Update README

* Fix bug

* refactor: add ghsa

* refactor: Add multi scanner in driver.go

* fix go.mod

* Add scanner.go

* Add parse lockfile

* unexport Driver & delete parse lockfile

* Fix scanner struct

* refactor: scanner -> advisory

* Add Driver

* delete Driver interface

* Add new drivers

* delete types.go

* Fix review

* Merge driver.go ← advisory.go

* Change NewDriver interface

Co-authored-by: Teppei Fukuda <knqyf263@gmail.com>
2020-05-30 09:34:00 +03:00
Teppei Fukuda
1218e111ef refactor: define common options and embed them into the option for subcommand (#502)
* refactor: embed config

* config_test: Add some missing cases for custom headers

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

Co-authored-by: Simarpreet Singh <simar@linux.com>
2020-05-29 22:09:30 +03:00
Simarpreet Singh
78b7529172 Add image subcommand (#493)
* config_test: Add missing assertions for TestNew

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

* integration: Add integration tests for image subcommand.

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

* refactor: bump up urfave/cli to v2.0

* refactor: apply DIY to image flags

* refactor: reorder sub commands

* feat: set hidden to global image options

* test(integration): insert --cache-dir before sub command

* README: update readme to reflect new usage

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

* chore(README): add image subcommand

* fix(flags): define aliases according to urfave/cli v2.0 style

Co-authored-by: knqyf263 <knqyf263@gmail.com>
2020-05-25 12:06:15 +03:00
Teppei Fukuda
e2bcb44687 fix: remove help template (#500) 2020-05-25 11:28:29 +03:00
Simarpreet Singh
a57c27eeec vulnerability: Add CVSS Vectors to JSON output. (#484)
* vulnerability: Add CVSS Vectors to JSON output.

Now Trivy will display the CVSS Vectors presented by various
vendors as part of the JSON output. This can be seen as follows:

```
      {
        "VulnerabilityID": "CVE-2019-9923",
        "PkgName": "tar",
        "InstalledVersion": "1.30+dfsg-6",
        "Layer": {
          "Digest": "sha256:90fe46dd819953eb995f9cc9c326130abe9dd0b3993a998e12c01d0218a0b831",
          "DiffID": "sha256:e40d297cf5f89a9822af4c2f63caa2f2085d5aa188137506918e603774b083cb"
        },
        "SeveritySource": "debian",
        "Title": "tar: null-pointer dereference in pax_decode_header in sparse.c",
        "Description": "pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.",
        "Severity": "LOW",
        "VendorVectors": {
          "nvd": {
            "v2": "AV:N/AC:L/Au:N/C:N/I:N/A:P",
            "v3": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
          },
          "redhat": {
            "v3": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L"
          }
        },
        "References": [
          "http://git.savannah.gnu.org/cgit/tar.git/commit/?id=cb07844454d8cc9fb21f53ace75975f91185a120",
          "http://lists.opensuse.org/opensuse-security-announce/2019-04/msg00077.html",
          "http://savannah.gnu.org/bugs/?55369",
          "https://bugs.launchpad.net/ubuntu/+source/tar/+bug/1810241"
        ]
      },
```

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

* mod: Update to latest master of trivy-db

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

* vulnerability_test: Fix tests for new struct type

Signed-off-by: Simarpreet Singh <simar@linux.com>
2020-05-21 14:22:14 -07:00
Teppei Fukuda
926f323a72 feat: support registry token (#482)
* feat: support registry token

* chore(mod): update

* test(integration): add registry tests

* chore(mod): update

* test(integration): comment in terminate

Co-authored-by: Simarpreet Singh <simar@linux.com>
2020-05-19 20:49:27 +03:00
Teppei Fukuda
aa20adb22b chore: bump up urfave/cli to v2 (#499) 2020-05-18 14:43:06 +03:00
Teppei Fukuda
3e0779a78d chore(doc): update README (#490) 2020-05-12 15:17:06 +03:00
79 changed files with 3815 additions and 2375 deletions

136
README.md
View File

@@ -134,7 +134,7 @@ $ rpm -ivh https://github.com/aquasecurity/trivy/releases/download/{TRIVY_VERSIO
Add repository to `/etc/apt/sources.list.d`.
```
$ sudo apt-get install wget apt-transport-https gnupg lsb-release
$ sudo apt-get install wget apt-transport-https gnupg lsb-release
$ wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
$ echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
$ sudo apt-get update
@@ -195,13 +195,13 @@ Simply specify an image name (and a tag). **The `latest` tag should be avoided a
## Basic
```
$ trivy [YOUR_IMAGE_NAME]
$ trivy image [YOUR_IMAGE_NAME]
```
For example:
```
$ trivy python:3.4-alpine
$ trivy image python:3.4-alpine
```
<details>
@@ -278,7 +278,7 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
Simply specify an image name (and a tag).
```
$ trivy knqyf263/vuln-image:1.2.3
$ trivy image knqyf263/vuln-image:1.2.3
```
<details>
@@ -492,7 +492,7 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
```
$ docker save ruby:2.3.0-alpine3.9 -o ruby-2.3.0.tar
$ trivy --input ruby-2.3.0.tar
$ trivy image --input ruby-2.3.0.tar
```
<details>
@@ -536,6 +536,8 @@ Total: 7447 (UNKNOWN: 5, LOW: 326, MEDIUM: 5695, HIGH: 1316, CRITICAL: 105)
...
```
</details>
### Scan an OCI image
An image directory compliant with "Open Container Image Layout Specification".
@@ -543,22 +545,21 @@ Buildah:
```
$ buildah push docker.io/library/alpine:3.11 oci:/path/to/alpine
$ trivy --input /path/to/alpine
$ trivy image --input /path/to/alpine
```
Skopeo:
```
$ skopeo copy docker-daemon:alpine:3.11 oci:/path/to/alpine
$ trivy --input /path/to/alpine
$ trivy image --input /path/to/alpine
```
</details>
### Save the results as JSON
```
$ trivy -f json -o results.json golang:1.12-alpine
$ trivy image -f json -o results.json golang:1.12-alpine
```
<details>
@@ -683,7 +684,7 @@ $ trivy -f json -o results.json golang:1.12-alpine
### Save the results using a template
```
$ trivy --format template --template "{{ range . }} {{ .Target }} {{ end }}" golang:1.12-alpine
$ trivy image --format template --template "{{ range . }} {{ .Target }} {{ end }}" golang:1.12-alpine
```
<details>
<summary>Result</summary>
@@ -696,13 +697,13 @@ $ trivy --format template --template "{{ range . }} {{ .Target }} {{ end }}" gol
You can load templates from a file prefixing the template path with an @.
```
$ trivy --format template --template "@/path/to/template" golang:1.12-alpine
$ trivy image --format template --template "@/path/to/template" golang:1.12-alpine
```
### Filter the vulnerabilities by severities
```
$ trivy --severity HIGH,CRITICAL ruby:2.3.0
$ trivy image --severity HIGH,CRITICAL ruby:2.4.0
```
<details>
@@ -712,7 +713,7 @@ $ trivy --severity HIGH,CRITICAL ruby:2.3.0
2019-05-16T01:51:46.255+0900 INFO Updating vulnerability database...
2019-05-16T01:51:49.213+0900 INFO Detecting Debian vulnerabilities...
ruby:2.3.0 (debian 8.4)
ruby:2.4.0 (debian 8.7)
=======================
Total: 1785 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1680, CRITICAL: 105)
@@ -766,7 +767,7 @@ Total: 1785 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1680, CRITICAL: 105)
### Filter the vulnerabilities by type
```
$ trivy --vuln-type os ruby:2.3.0
$ trivy image --vuln-type os ruby:2.4.0
```
Available values:
@@ -784,7 +785,7 @@ Available values:
2019-05-22T19:36:52.390+0200 INFO Updating pipenv Security DB...
2019-05-22T19:36:53.406+0200 INFO Detecting pipenv vulnerabilities...
ruby:2.3.0 (debian 8.4)
ruby:2.4.0 (debian 8.7)
Total: 4751 (UNKNOWN: 1, LOW: 150, MEDIUM: 3504, HIGH: 1013, CRITICAL: 83)
+---------+------------------+----------+-------------------+---------------+----------------------------------+
@@ -904,7 +905,7 @@ Total: 4751 (UNKNOWN: 1, LOW: 150, MEDIUM: 3504, HIGH: 1013, CRITICAL: 83)
`Trivy` always updates its vulnerability database when it starts operating. This is usually fast, as it is a difference update. But if you want to skip even that, use the `--skip-update` option.
```
$ trivy --skip-update python:3.4-alpine3.9
$ trivy image --skip-update python:3.4-alpine3.9
```
<details>
@@ -930,11 +931,10 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
### Only download vulnerability database
You can also ask `Trivy` to simply retrieve the vulnerability database. This is useful to initialize workers in Continuous Integration systems. In the first run, the `--only-update` option is silently ignored.
You can also ask `Trivy` to simply retrieve the vulnerability database. This is useful to initialize workers in Continuous Integration systems.
```
$ trivy --download-db-only
$ trivy --download-db-only --only-update alpine
$ trivy image --download-db-only
```
### Ignore unfixed vulnerabilities
@@ -943,7 +943,7 @@ By default, `Trivy` also detects unpatched/unfixed vulnerabilities. This means y
If you would like to ignore them, use the `--ignore-unfixed` option.
```
$ trivy --ignore-unfixed ruby:2.3.0
$ trivy image --ignore-unfixed ruby:2.4.0
```
<details>
@@ -953,7 +953,7 @@ $ trivy --ignore-unfixed ruby:2.3.0
2019-05-16T12:49:52.656+0900 INFO Updating vulnerability database...
2019-05-16T12:50:14.786+0900 INFO Detecting Debian vulnerabilities...
ruby:2.3.0 (debian 8.4)
ruby:2.4.0 (debian 8.7)
=======================
Total: 4730 (UNKNOWN: 1, LOW: 145, MEDIUM: 3487, HIGH: 1014, CRITICAL: 83)
@@ -991,7 +991,7 @@ By default, `Trivy` exits with code 0 even when vulnerabilities are detected.
Use the `--exit-code` option if you want to exit with a non-zero exit code.
```
$ trivy --exit-code 1 python:3.4-alpine3.9
$ trivy image --exit-code 1 python:3.4-alpine3.9
```
<details>
@@ -1018,8 +1018,8 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)
This option is useful for CI/CD. In the following example, the test will fail only when a critical vulnerability is found.
```
$ trivy --exit-code 0 --severity MEDIUM,HIGH ruby:2.3.0
$ trivy --exit-code 1 --severity CRITICAL ruby:2.3.0
$ trivy image --exit-code 0 --severity MEDIUM,HIGH ruby:2.4.0
$ trivy image --exit-code 1 --severity CRITICAL ruby:2.4.0
```
### Ignore the specified vulnerabilities
@@ -1034,7 +1034,7 @@ CVE-2018-14618
# No impact in our settings
CVE-2019-1543
$ trivy python:3.4-alpine3.9
$ trivy image python:3.4-alpine3.9
```
<details>
@@ -1055,7 +1055,7 @@ Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
### Specify cache directory
```
$ trivy --cache-dir /tmp/trivy/ python:3.4-alpine3.9
$ trivy --cache-dir /tmp/trivy/ image python:3.4-alpine3.9
```
### Clear image caches
@@ -1065,7 +1065,7 @@ The `--clear-cache` option removes image caches. This option is useful if the im
**The scan is not performed.**
```
$ trivy --clear-cache
$ trivy image --clear-cache
```
<details>
@@ -1083,7 +1083,7 @@ $ trivy --clear-cache
The `--reset` option removes all caches and database. After this, it takes a long time as the vulnerability database needs to be rebuilt locally.
```
$ trivy --reset
$ trivy image --reset
```
<details>
@@ -1105,7 +1105,7 @@ To find the additional information, you can search vulnerability details on the
https://nvd.nist.gov/vuln/search
```
$ trivy --light alpine:3.10
$ trivy image --light alpine:3.10
```
`--light` option doesn't display titles like the following example.
@@ -1411,12 +1411,16 @@ Trivy scans a tar image with the following format.
### Data source
- PHP
- https://github.com/FriendsOfPHP/security-advisories
- https://github.com/advisories?query=ecosystem%3Acomposer
- Python
- https://github.com/pyupio/safety-db
- https://github.com/advisories?query=ecosystem%3Apip
- Ruby
- https://github.com/rubysec/ruby-advisory-db
- https://github.com/advisories?query=ecosystem%3Arubygems
- Node.js
- https://github.com/nodejs/security-wg
- https://github.com/advisories?query=ecosystem%3Anpm
- Rust
- https://github.com/RustSec/advisory-db
@@ -1425,41 +1429,49 @@ Trivy scans a tar image with the following format.
```
NAME:
trivy - A simple and comprehensive vulnerability scanner for containers
USAGE:
main [options] image_name
VERSION:
0.2.0
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]
--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]
--clear-cache, -c clear image caches [$TRIVY_CLEAR_CACHE]
--quiet, -q suppress progress bar and log output [$TRIVY_QUIET]
--no-progress suppress progress bar [$TRIVY_NO_PROGRESS]
--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]
--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]
--ignorefile value specify .trivyignore file (default: ".trivyignore") [$TRIVY_IGNOREFILE]
--timeout value docker timeout (default: 1m0s) [$TRIVY_TIMEOUT]
--light light mode: it's faster, but vulnerability descriptions and references are not displayed
--only-update value deprecated [$TRIVY_ONLY_UPDATE]
--refresh deprecated [$TRIVY_REFRESH]
--auto-refresh deprecated [$TRIVY_AUTO_REFRESH]
--help, -h show help
--version, -v print the version
trivy - A simple and comprehensive vulnerability scanner for containers
USAGE:
trivy image [options] image_name
VERSION:
v0.7.0
OPTIONS:
--quiet suppress progress bar and log output (default: false) [$TRIVY_QUIET]
--debug debug mode (default: false) [$TRIVY_DEBUG]
--cache-dir value cache directory (default: "/Users/simar/Library/Caches/trivy") [$TRIVY_CACHE_DIR]
--help, -h show help (default: false)
--version, -v print the version (default: false)
```
## Sub commands
Trivy has two sub commands, client and server.
Trivy has three sub commands, image, client and server.
```
NAME:
trivy image - scan an image
USAGE:
trivy image [command options] [arguments...]
OPTIONS:
--template value output template [$TRIVY_TEMPLATE]
--format value format (table, json, template) (default: "table") [$TRIVY_FORMAT]
--input value input file path instead of image name [$TRIVY_INPUT]
--severity value severities of vulnerabilities to be displayed (comma separated) (default: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") [$TRIVY_SEVERITY]
--output value output file name [$TRIVY_OUTPUT]
--exit-code value Exit code when vulnerabilities were found (default: 0) [$TRIVY_EXIT_CODE]
--skip-update skip db update (default: false) [$TRIVY_SKIP_UPDATE]
--download-db-only download/update vulnerability database but don't run a scan (default: false) [$TRIVY_DOWNLOAD_DB_ONLY]
--reset remove all caches and database (default: false) [$TRIVY_RESET]
--clear-cache clear image caches without scanning (default: false) [$TRIVY_CLEAR_CACHE]
--no-progress suppress progress bar (default: false) [$TRIVY_NO_PROGRESS]
--ignore-unfixed display only fixed vulnerabilities (default: false) [$TRIVY_IGNORE_UNFIXED]
--removed-pkgs detect vulnerabilities of removed packages (only for Alpine) (default: false) [$TRIVY_REMOVED_PKGS]
--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]
--timeout value docker timeout (default: 2m0s) [$TRIVY_TIMEOUT]
--light light mode: it's faster, but vulnerability descriptions and references are not displayed (default: false) [$TRIVY_LIGHT]
--help, -h show help (default: false)
```
```
NAME:
@@ -1667,7 +1679,7 @@ $ GITHUB_TOKEN=XXXXXXXXXX trivy alpine:3.10
Try again with `--reset` option:
```
$ trivy --reset
$ trivy image --reset
```
# Related Projects

10
go.mod
View File

@@ -3,15 +3,16 @@ module github.com/aquasecurity/trivy
go 1.13
require (
github.com/aquasecurity/fanal v0.0.0-20200504143803-30a561989059
github.com/aquasecurity/fanal v0.0.0-20200528202907-79693bf4a058
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy-db v0.0.0-20200430091154-7c0a6e1ad398
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470
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 v1.4.2-0.20190924003213-a8608b5b67c7
github.com/genuinetools/reg v0.16.0
github.com/docker/go-connections v0.4.0
github.com/golang/protobuf v1.3.3
github.com/google/go-containerregistry v0.0.0-20200331213917-3d03ed9b1ca2
github.com/google/go-github/v28 v28.1.1
github.com/google/wire v0.3.0
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
@@ -22,8 +23,9 @@ require (
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
github.com/spf13/afero v1.2.2
github.com/stretchr/testify v1.4.0
github.com/testcontainers/testcontainers-go v0.3.1
github.com/twitchtv/twirp v5.10.1+incompatible
github.com/urfave/cli v1.22.1
github.com/urfave/cli/v2 v2.2.0
go.uber.org/atomic v1.5.1 // indirect
go.uber.org/multierr v1.4.0 // indirect
go.uber.org/zap v1.13.0

126
go.sum
View File

@@ -31,29 +31,35 @@ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
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/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aquasecurity/fanal v0.0.0-20200504143803-30a561989059 h1:FLQkluzBXeQvyNAMNtFpvd0qMbxLeYVdP6B/Pxx/d54=
github.com/aquasecurity/fanal v0.0.0-20200504143803-30a561989059/go.mod h1:3H3F3x2XtcdFH3o1LQJEzfu2sS/rf+XufPIngMZrKO4=
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/aquasecurity/fanal v0.0.0-20200528202907-79693bf4a058 h1:vNAuJrimb3eqXSFMhZJNf0PVHfzHFnCRMDuhVi7z2Ok=
github.com/aquasecurity/fanal v0.0.0-20200528202907-79693bf4a058/go.mod h1:omM/xBVqAPNzdV/MegrjayEkKEZzI+eUpyjCXpbTMG0=
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/testdocker v0.0.0-20200426142840-5f05bce6f12a h1:hsw7PpiymXP64evn/K7gsj3hWzMqLrdoeE6JkqDocVg=
github.com/aquasecurity/testdocker v0.0.0-20200426142840-5f05bce6f12a/go.mod h1:psfu0MVaiTDLpNxCoNsTeILSKY2EICBwv345f3M+Ffs=
github.com/aquasecurity/trivy-db v0.0.0-20200430091154-7c0a6e1ad398 h1:+13ICJ+UlP/1aHZixBv1EdhS+4kTdY0ASJOktnCUOfI=
github.com/aquasecurity/trivy-db v0.0.0-20200430091154-7c0a6e1ad398/go.mod h1:8mrJtzlmPGWO1uVwPurDrybthyA/eZ7voMO9b54rdRw=
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470 h1:6VE+g4AK2uivPqZtVk/QtcCBb2rUjAvKqDNexSgqMC0=
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470/go.mod h1:F77bF2nRbcH4EIhhcNEP585MoAKdLpEP3dihF9V1Hbw=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 h1:xbdUfr2KE4THsFx9CFWtWpU91lF+YhgP46moV94nYTA=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.1 h1:MXnqY6SlWySaZAqNnXThOvjRFdiiOuKtC6i7baFdNdU=
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@@ -76,9 +82,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.3 h1:LoIzb5y9x5l8VKAlyrbusNPXqBY0+kviRloxFUMFwKc=
github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
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=
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
@@ -93,6 +98,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -102,32 +108,22 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 h1:2HQmlpI3yI9deH18Q6xiSOIjXD4sLI55Y/gfpa8/558=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43 h1:gZ4lWixV821UVbYtr+oz1ZPCHkbtE+ivfmHyZRgyl2Y=
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -138,21 +134,32 @@ github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhP
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
github.com/genuinetools/reg v0.16.0 h1:ZhLZPT+aUGHLfy45Ub5FLWik+3Dij1iwaj8A/GyAZBw=
github.com/genuinetools/reg v0.16.0/go.mod h1:12Fe9EIvK3dG/qWhNk5e9O96I8SGmCKLsJ8GsXUbk+Y=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
@@ -169,7 +176,9 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -180,7 +189,6 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -228,7 +236,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -240,9 +247,13 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
@@ -255,6 +266,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/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=
@@ -272,12 +285,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
@@ -305,8 +318,8 @@ github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1N
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -320,6 +333,8 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a h1:0LD5FJGQpEyD78OdhX97W75RjYmMjfLPp1ePrk5URxs=
@@ -327,24 +342,23 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a/go.mod h1
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterhellberg/link v1.0.0 h1:mUWkiegowUXEcmlb+ybF75Q/8D2Y0BjZtR8cxoKhaQo=
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -353,15 +367,12 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
@@ -373,13 +384,16 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e h1:NO86zOn5ScSKW8wRbMaSIcjDZUFpWdCQQnexRqZ9h9A=
github.com/saracen/walker v0.0.0-20191201085201-324a081bae7e/go.mod h1:G0Z6yVPru183i2MuRJx1DcR4dgIZtLcTdaaE/pC1BJU=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -390,6 +404,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sosedoff/gitkit v0.2.0 h1:cVre9QZvsDzS/v42PSOsf+GCaecvb/CWGX+diP232F8=
github.com/sosedoff/gitkit v0.2.0/go.mod h1:A+o6ZazfVJwetlcHz3ah6th66XcBdsyzLo+aBt/AsK4=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -411,6 +427,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/testcontainers/testcontainers-go v0.3.1 h1:KZkEKNfnlsipJblzGCz6fmzd+0DzJ3djulYrislG3Zw=
github.com/testcontainers/testcontainers-go v0.3.1/go.mod h1:br7bkzIukhPSIjy07Ma3OuXjjFvl2jm7CDU0LQNsqLw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twitchtv/twirp v5.10.1+incompatible h1:35js8ID9rYPKkZ0qWnuZw+q+OuCWM1GIibu1F1YImjA=
@@ -418,11 +435,14 @@ github.com/twitchtv/twirp v5.10.1+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3v
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
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/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -444,21 +464,22 @@ go.uber.org/zap v1.10.0/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-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -474,7 +495,6 @@ golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73r
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=
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -488,8 +508,8 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -498,20 +518,22 @@ 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/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -524,6 +546,7 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -556,6 +579,7 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191011211836-4c025a95b26e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200210192313-1ace956b0e17 h1:a/Fd23DJvg1CaeDH0dYHahE+hCI0v9rFgxSNIThoUcM=
@@ -575,13 +599,11 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -591,19 +613,19 @@ google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRn
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
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 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@@ -611,8 +633,11 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -620,7 +645,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -14,7 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/internal"
)

View File

@@ -0,0 +1,23 @@
server:
addr: ":5001"
certificate: "/certs/cert.pem"
key: "/certs/key.pem"
token:
issuer: "Trivy auth server" # Must match issuer in the Registry config.
expiration: 900
users:
# Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate.
"admin":
password: "$2y$05$LO.vzwpWC5LZGqThvEfznu8qhb5SGqvBSWY1J3yZ4AxtMRZ3kN5jC" # badmin
"test":
password: "$2y$05$WuwBasGDAgr.QCbGIjKJaep4dhxeai9gNZdmBnQXqpKly57oNutya" # 123
acl:
- match: {account: "admin"}
actions: ["*"]
comment: "Admin has full access to everything."
- match: {account: "test"}
actions: ["pull"]
comment: "User \"test\" can pull stuff."

View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+jCCAeKgAwIBAgIRAJLJ5vw48YZwoHlC8i6VdHswDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMDA1MDMxMTU2MzhaFw0yMTA1MDMxMTU2
MzhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDhnepAL1Atd1xVh/TOZpTK7yHwtOrtGWNEqNkFbcyD7x9CNgUkxjO8
nc4ynEo4ARpLj+2VDLIwi93weCFj6mcz2tdHi7n0eiPR7+PSNMNpPFwablLOEtaX
XVqHhJNsHcJx6okX6ullksJoRnZGu+n1LvGRMMLWjS3UJZA6+1pujoifyrx9YXLU
qSjkRRv3Ly8HmAPJq0T19uCZiJ8qbrW1Vx3hdUILL4OlJmpjZvGKMRnolinko2Vk
0pHH5MWz0iUbqWQjHZmQWi0rDHRAFbuCqQdmFsEneXmUzExXZbyHwrTH/mrjJTCJ
YmtR7Eq80AxsWnXNI3Z0mVQ9/nZDsT31AgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAG7spAmpZVKOV913WhKZp8o1+
T6v/b6KUOAZ0iWXeGWO7LwnRaulkyauGav9xJUpfEw7Z/57qwTVIGZD6mZF6tt77
FsXXiQJA88LrQnt1BRTeNK8sRp4S3N1hrtY3akTit/dyQcfh3NSDttzkYsoUu0qT
DUkXD0b4eDmaD47+0Z6eIVp3aEcPMzpiy6qWc5fMjMeHjtYF4lBSF0JTWzmxNUGl
fiGhMJStQK/n73t58O7h5Adva5wRV+Km6pa+6SfOxPNUjsxXjG0LzWA9dJg/q2rs
k/ouIE05BfB3z538ncQVBTwfPMClbIiJhAs3b6ej22+j/O+vbFBmdfkpVpFRtg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhnepAL1Atd1xV
h/TOZpTK7yHwtOrtGWNEqNkFbcyD7x9CNgUkxjO8nc4ynEo4ARpLj+2VDLIwi93w
eCFj6mcz2tdHi7n0eiPR7+PSNMNpPFwablLOEtaXXVqHhJNsHcJx6okX6ullksJo
RnZGu+n1LvGRMMLWjS3UJZA6+1pujoifyrx9YXLUqSjkRRv3Ly8HmAPJq0T19uCZ
iJ8qbrW1Vx3hdUILL4OlJmpjZvGKMRnolinko2Vk0pHH5MWz0iUbqWQjHZmQWi0r
DHRAFbuCqQdmFsEneXmUzExXZbyHwrTH/mrjJTCJYmtR7Eq80AxsWnXNI3Z0mVQ9
/nZDsT31AgMBAAECggEBAKwwGhSMR3O7sdNxJIvVzF8orE2JtfXoN1OyTZcQGlLi
z4d3tOtA/UFJapJDp30gklHy8Y6clu3oASVCebFItyTjMwPehrgn82iI3eWS8URC
lcRySG4QAIia7bmZm+2atMi+B40icqhbnlV42VHYnpDKGAEIJtsZ+kz7shzhsj3G
yTQMFyuqk0DUmsbSVKPjryv15DXsT9Rk2pVZYFhiRw/gQpWD58GMP/HMrSz+sjuX
ZIlhSMGVWA4Yc7le4PpWI2qAZLR+X1EgkzxcMJ0kWvnvzEXFmofaYzkbEcNOlguF
Bv9kP5fh35AbQbTLykGO9h4VrfDajlHequzNBJs1z60CgYEA53cwBh42pg/fSmaO
sowpFV52ZfbfUPcuXRuaidHWougByB8P8XTMeQTse4NLt+2oat/5rdP3keGr5OR5
8q7v8/R/KY0NQOa/93BUeRDW4ntxMECWbC2p/sq2wnRKTl+yepAWrRXzk8z9vFP/
TZM5m65aj3IsZ3Bo1WG+SSf8bvsCgYEA+YgFxmiTauKRO4IVPuOqJ88yC5SQ83mF
T54ILYalG3yq/Jm1TTOzoZAoKvHrJeeZqQvjS4jSY5gc5TCrUVTdsw5nXtrRKZJs
HjtVT78qfzjCSHzImvc3Rw5+SNO2+j9yxuBSAG4tEKD3KKxSodXnKtD4CwzvRdyI
gUyjQi3Os88CgYEAgrzegkYkhe2nKKX+6bijJ+/AHl2vy1KifHKv+jJs8nzrLLbm
0XIwYBa44BbL+Oqi2yMBKv7z8hEuf03R15KZ9Ahgnv6Nwt/TBBcNj4hEZ45j42ZH
0HiGcWTcj78RjW0eKX4jYMZqW0xI8Uvcg1uqCVYUzrsle5ORkxzvVvDf82sCgYEA
4hS9tsA1IJhaoaIAgdRf7GWroBZhJlep0zMJkcX2fer8OJVDUMlRLUahPhelx9gI
vsLIkz1J8XZ2Z6kq7yuHGp4oRibXb2T8lH+JkhFP/ah9TpPQZacq7DRTcsRvelhW
M542bbFlHzXX+X/39i0Jnx9qPQjhGVjwTMYU/Pbn2r0CgYAkwwh2oq6BP3w7/4xe
giaW/5zzMA8R9ZXFfkE4OXk2vig8LzUn1IO0JeGLyHQbdg8exYxTU3zygIlSvCQP
Zbl0+RB+NHwGOqlpEDdPFXsqi1GAdWOC6FzYtBFNk9WyjFICXjB42wnfIIUQPLU1
kQKFaehfx9KR0iW1dnm3vbFlzw==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,112 @@
package docker
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"github.com/docker/docker/client"
"github.com/docker/docker/api/types"
)
type RegistryConfig struct {
URL *url.URL
Username string
Password string
}
func (c RegistryConfig) GetAuthConfig() types.AuthConfig {
return types.AuthConfig{
Username: c.Username,
Password: c.Password,
ServerAddress: c.URL.Host,
}
}
func (c RegistryConfig) GetRegistryAuth() (string, error) {
authConfig := types.AuthConfig{
Username: c.Username,
Password: c.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(encodedJSON), nil
}
type Docker struct {
cli *client.Client
}
func New() (Docker, error) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return Docker{}, err
}
return Docker{
cli: cli,
}, nil
}
// ReplicateImage tags the given imagePath and pushes it to the given dest registry.
func (d Docker) ReplicateImage(ctx context.Context, imageRef, imagePath string, dest RegistryConfig) error {
// remove existing Image if any
_, _ = d.cli.ImageRemove(ctx, imageRef, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
testfile, err := os.Open(imagePath)
if err != nil {
return err
}
// load image into docker engine
resp, err := d.cli.ImageLoad(ctx, testfile, true)
if err != nil {
return err
}
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
return err
}
defer resp.Body.Close()
targetImageRef := fmt.Sprintf("%s/%s", dest.URL.Host, imageRef)
if err = d.cli.ImageTag(ctx, imageRef, targetImageRef); err != nil {
return err
}
defer func() {
_, _ = d.cli.ImageRemove(ctx, imageRef, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
_, _ = d.cli.ImageRemove(ctx, targetImageRef, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
}()
auth, err := dest.GetRegistryAuth()
if err != nil {
return err
}
pushOut, err := d.cli.ImagePush(ctx, targetImageRef, types.ImagePushOptions{RegistryAuth: auth})
if err != nil {
return err
}
defer pushOut.Close()
if _, err = io.Copy(ioutil.Discard, pushOut); err != nil {
return err
}
return nil
}

View File

@@ -20,22 +20,33 @@ import (
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 string
withImageSubcommand bool
imageTag string
invalidImage bool
ignoreUnfixed bool
severity []string
ignoreIDs []string
testfile string
expectedOutputFile string
expectedError string
}{
// All of these cases should pass for either
// $ trivy <args>
// $ trivy image <args>
{
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, with image subcommand, alpine:3.10",
withImageSubcommand: true,
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,
@@ -273,7 +284,14 @@ func TestRun_WithDockerEngine(t *testing.T) {
// run trivy
app := internal.NewApp("dev")
trivyArgs := []string{"trivy", "--skip-update", "--cache-dir", cacheDir, "--format=json", "--output", of.Name()}
trivyArgs := []string{"trivy"}
trivyArgs = append(trivyArgs, "--cache-dir", cacheDir)
if tc.withImageSubcommand {
trivyArgs = append(trivyArgs, "image")
}
trivyArgs = append(trivyArgs, []string{"--skip-update", "--format=json", "--output", of.Name()}...)
if tc.ignoreUnfixed {
trivyArgs = append(trivyArgs, "--ignore-unfixed")
}

View File

@@ -1,4 +1,4 @@
// +rbuild integration
// +build integration
package integration

View File

@@ -0,0 +1,346 @@
// +build integration
package integration
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"github.com/docker/go-connections/nat"
"github.com/google/go-containerregistry/pkg/name"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
testcontainers "github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
_ "github.com/aquasecurity/fanal/analyzer"
testdocker "github.com/aquasecurity/trivy/integration/docker"
"github.com/aquasecurity/trivy/internal"
"github.com/aquasecurity/trivy/pkg/report"
)
const (
registryImage = "registry:2"
registryPort = "5443/tcp"
authImage = "cesanta/docker_auth:1"
authPort = "5001/tcp"
authUsername = "admin"
authPassword = "badmin"
)
func setupRegistry(ctx context.Context, baseDir string, authURL *url.URL) (testcontainers.Container, error) {
req := testcontainers.ContainerRequest{
Name: "registry",
Image: registryImage,
ExposedPorts: []string{registryPort},
Env: map[string]string{
"REGISTRY_HTTP_ADDR": "0.0.0.0:5443",
"REGISTRY_HTTP_TLS_CERTIFICATE": "/certs/cert.pem",
"REGISTRY_HTTP_TLS_KEY": "/certs/key.pem",
"REGISTRY_AUTH": "token",
"REGISTRY_AUTH_TOKEN_REALM": fmt.Sprintf("%s/auth", authURL),
"REGISTRY_AUTH_TOKEN_SERVICE": "registry.docker.io",
"REGISTRY_AUTH_TOKEN_ISSUER": "Trivy auth server",
"REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE": "/certs/cert.pem",
},
BindMounts: map[string]string{
filepath.Join(baseDir, "data", "certs"): "/certs",
},
WaitingFor: wait.ForLog("listening on [::]:5443"),
}
registryC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
return registryC, err
}
func setupAuthServer(ctx context.Context, baseDir string) (testcontainers.Container, error) {
req := testcontainers.ContainerRequest{
Name: "docker_auth",
Image: authImage,
ExposedPorts: []string{authPort},
BindMounts: map[string]string{
filepath.Join(baseDir, "data", "auth_config"): "/config",
filepath.Join(baseDir, "data", "certs"): "/certs",
},
Cmd: []string{"/config/config.yml"},
}
authC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
return authC, err
}
func getURL(ctx context.Context, container testcontainers.Container, exposedPort nat.Port) (*url.URL, error) {
ip, err := container.Host(ctx)
if err != nil {
return nil, err
}
port, err := container.MappedPort(ctx, exposedPort)
if err != nil {
return nil, err
}
urlStr := fmt.Sprintf("https://%s:%s", ip, port.Port())
return url.Parse(urlStr)
}
type registryOption struct {
AuthURL *url.URL
Username string
Password string
RegistryToken bool
}
func TestRegistry(t *testing.T) {
ctx := context.Background()
baseDir, err := filepath.Abs(".")
require.NoError(t, err)
// set up auth server
authC, err := setupAuthServer(ctx, baseDir)
require.NoError(t, err)
defer authC.Terminate(ctx)
authURL, err := getURL(ctx, authC, authPort)
require.NoError(t, err)
// set up registry
registryC, err := setupRegistry(ctx, baseDir, authURL)
require.NoError(t, err)
defer registryC.Terminate(ctx)
registryURL, err := getURL(ctx, registryC, registryPort)
require.NoError(t, err)
config := testdocker.RegistryConfig{
URL: registryURL,
Username: authUsername,
Password: authPassword,
}
testCases := []struct {
name string
imageName string
imageFile string
option registryOption
golden string
wantErr string
}{
{
name: "happy path with username/password",
imageName: "alpine:3.10",
imageFile: "testdata/fixtures/alpine-310.tar.gz",
option: registryOption{
AuthURL: authURL,
Username: authUsername,
Password: authPassword,
},
golden: "testdata/alpine-310-registry.json.golden",
},
{
name: "happy path with registry token",
imageName: "alpine:3.10",
imageFile: "testdata/fixtures/alpine-310.tar.gz",
option: registryOption{
AuthURL: authURL,
Username: authUsername,
Password: authPassword,
RegistryToken: true,
},
golden: "testdata/alpine-310-registry.json.golden",
},
{
name: "sad path",
imageName: "alpine:3.10",
imageFile: "testdata/fixtures/alpine-310.tar.gz",
wantErr: "unsupported status code 401; body: Auth failed",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
d, err := testdocker.New()
require.NoError(t, err)
s := fmt.Sprintf("%s/%s", registryURL.Host, tc.imageName)
imageRef, err := name.ParseReference(s)
require.NoError(t, err)
// 1. Load a test image from the tar file, tag it and push to the test registry.
err = d.ReplicateImage(ctx, tc.imageName, tc.imageFile, config)
require.NoError(t, err)
// 2. Scan it
resultFile, err := scan(imageRef, baseDir, tc.option)
if tc.wantErr != "" {
require.NotNil(t, err)
require.Contains(t, err.Error(), tc.wantErr, err)
return
} else {
require.NoError(t, err)
}
defer os.Remove(resultFile)
// 3. Compare want and got
golden, err := os.Open(tc.golden)
assert.NoError(t, err)
var want report.Results
err = json.NewDecoder(golden).Decode(&want)
require.NoError(t, err)
result, err := os.Open(resultFile)
assert.NoError(t, err)
var got report.Results
err = json.NewDecoder(result).Decode(&got)
require.NoError(t, err)
assert.Equal(t, want[0].Vulnerabilities, got[0].Vulnerabilities)
assert.Equal(t, want[0].Vulnerabilities, got[0].Vulnerabilities)
})
}
}
func scan(imageRef name.Reference, baseDir string, opt registryOption) (string, error) {
// Copy DB file
cacheDir, err := gunzipDB()
if err != nil {
return "", err
}
defer os.RemoveAll(cacheDir)
// Setup the output file
var outputFile string
output, err := ioutil.TempFile("", "integration")
if err != nil {
return "", err
}
if err = output.Close(); err != nil {
return "", err
}
outputFile = output.Name()
// Setup env
if err = setupEnv(imageRef, baseDir, opt); err != nil {
return "", err
}
defer unsetEnv()
// Setup CLI App
app := internal.NewApp("dev")
app.Writer = ioutil.Discard
osArgs := []string{"trivy", "--cache-dir", cacheDir, "--format", "json", "--skip-update", "--output", outputFile, imageRef.Name()}
// Run Trivy
if err = app.Run(osArgs); err != nil {
return "", err
}
return outputFile, nil
}
func setupEnv(imageRef name.Reference, baseDir string, opt registryOption) error {
if err := os.Setenv("TRIVY_INSECURE", "true"); err != nil {
return err
}
if opt.Username != "" && opt.Password != "" {
if opt.RegistryToken {
// Get a registry token in advance
token, err := requestRegistryToken(imageRef, baseDir, opt)
if err != nil {
return err
}
if err := os.Setenv("TRIVY_REGISTRY_TOKEN", token); err != nil {
return err
}
} else {
if err := os.Setenv("TRIVY_USERNAME", opt.Username); err != nil {
return err
}
if err := os.Setenv("TRIVY_PASSWORD", opt.Password); err != nil {
return err
}
}
}
return nil
}
func unsetEnv() error {
envs := []string{"TRIVY_INSECURE", "TRIVY_USERNAME", "TRIVY_PASSWORD", "TRIVY_REGISTRY_TOKEN"}
for _, e := range envs {
if err := os.Unsetenv(e); err != nil {
return err
}
}
return nil
}
func requestRegistryToken(imageRef name.Reference, baseDir string, opt registryOption) (string, error) {
// Create a CA certificate pool and add cert.pem to it
caCert, err := ioutil.ReadFile(filepath.Join(baseDir, "data", "certs", "cert.pem"))
if err != nil {
return "", err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create a HTTPS client and supply the created CA pool
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
// Get a registry token
req, err := http.NewRequest("GET", fmt.Sprintf("%s/auth", opt.AuthURL), nil)
if err != nil {
return "", err
}
// Set query parameters
values := req.URL.Query()
values.Set("service", "registry.docker.io")
values.Set("scope", imageRef.Scope("pull"))
req.URL.RawQuery = values.Encode()
req.SetBasicAuth(opt.Username, opt.Password)
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
type res struct {
AccessToken string `json:"access_token"`
}
var r res
if err = json.NewDecoder(resp.Body).Decode(&r); err != nil {
return "", err
}
return r.AccessToken, nil
}

View File

@@ -16,13 +16,14 @@ import (
func TestRun_WithTar(t *testing.T) {
type args struct {
Version string
SkipUpdate bool
IgnoreUnfixed bool
Severity []string
IgnoreIDs []string
Format string
Input string
Version string
WithImageSubcommand bool
SkipUpdate bool
IgnoreUnfixed bool
Severity []string
IgnoreIDs []string
Format string
Input string
}
cases := []struct {
name string
@@ -39,6 +40,17 @@ func TestRun_WithTar(t *testing.T) {
},
golden: "testdata/alpine-310.json.golden",
},
{
name: "alpine 3.10 integration with image subcommand",
testArgs: args{
Version: "dev",
WithImageSubcommand: true,
SkipUpdate: true,
Format: "json",
Input: "testdata/fixtures/alpine-310.tar.gz",
},
golden: "testdata/alpine-310.json.golden",
},
{
name: "alpine 3.10 integration with --ignore-unfixed option",
testArgs: args{
@@ -354,7 +366,13 @@ func TestRun_WithTar(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
osArgs := []string{"trivy", "--cache-dir", cacheDir, "--format", c.testArgs.Format}
osArgs := []string{"trivy"}
osArgs = append(osArgs, "--cache-dir", cacheDir)
if c.testArgs.WithImageSubcommand {
osArgs = append(osArgs, "image")
}
osArgs = append(osArgs, "--format", c.testArgs.Format)
if c.testArgs.SkipUpdate {
osArgs = append(osArgs, "--skip-update")
}

View File

@@ -0,0 +1,107 @@
[
{
"Target": "localhost:5000/alpine:3.10 (alpine 3.10.2)",
"Type": "alpine",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2019-1549",
"PkgName": "openssl",
"InstalledVersion": "1.1.1c-r0",
"FixedVersion": "1.1.1d-r0",
"Layer": {
"DiffID": "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0",
"Digest": "sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609"
},
"SeveritySource": "nvd",
"Title": "openssl: information disclosure in fork()",
"Description": "OpenSSL 1.1.1 introduced a rewritten random number generator (RNG). This was intended to include protection in the event of a fork() system call in order to ensure that the parent and child processes did not share the same RNG state. However this protection was not being used in the default case. A partial mitigation for this issue is that the output from a high precision timer is mixed into the RNG state so the likelihood of a parent and child process sharing state is significantly reduced. If an application already calls OPENSSL_init_crypto() explicitly using OPENSSL_INIT_ATFORK then this problem does not occur at all. Fixed in OpenSSL 1.1.1d (Affected 1.1.1-1.1.1c).",
"Severity": "MEDIUM",
"References": [
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=1b0fe00e2704b5e20334a16d3c9099d1ba2ef1be",
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/GY6SNRJP2S7Y42GIIDO3HXPNMDYN2U3A/",
"https://security.netapp.com/advisory/ntap-20190919-0002/",
"https://support.f5.com/csp/article/K44070243",
"https://www.openssl.org/news/secadv/20190910.txt"
]
},
{
"VulnerabilityID": "CVE-2019-1551",
"PkgName": "openssl",
"InstalledVersion": "1.1.1c-r0",
"FixedVersion": "1.1.1d-r2",
"Layer": {
"DiffID": "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0",
"Digest": "sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609"
},
"SeveritySource": "nvd",
"Title": "openssl: Integer overflow in RSAZ modular exponentiation on x86_64",
"Description": "There is an overflow bug in the x64_64 Montgomery squaring procedure used in exponentiation with 512-bit moduli. No EC algorithms are affected. Analysis suggests that attacks against 2-prime RSA1024, 3-prime RSA1536, and DSA1024 as a result of this defect would be very difficult to perform and are not believed likely. Attacks against DH512 are considered just feasible. However, for an attack the target would have to re-use the DH512 private key, which is not recommended anyway. Also applications directly using the low level API BN_mod_exp may be affected if they use BN_FLG_CONSTTIME. Fixed in OpenSSL 1.1.1e (Affected 1.1.1-1.1.1d). Fixed in OpenSSL 1.0.2u (Affected 1.0.2-1.0.2t).",
"Severity": "MEDIUM",
"References": [
"http://lists.opensuse.org/opensuse-security-announce/2020-01/msg00030.html",
"http://packetstormsecurity.com/files/155754/Slackware-Security-Advisory-openssl-Updates.html",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1551",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=419102400a2811582a7a3d4a4e317d72e5ce0a8f",
"https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=f1c5eea8a817075d31e43f5876993c6710238c98",
"https://github.com/openssl/openssl/pull/10575",
"https://seclists.org/bugtraq/2019/Dec/39",
"https://seclists.org/bugtraq/2019/Dec/46",
"https://security.netapp.com/advisory/ntap-20191210-0001/",
"https://www.debian.org/security/2019/dsa-4594",
"https://www.openssl.org/news/secadv/20191206.txt",
"https://www.tenable.com/security/tns-2019-09"
]
},
{
"VulnerabilityID": "CVE-2019-1563",
"PkgName": "openssl",
"InstalledVersion": "1.1.1c-r0",
"FixedVersion": "1.1.1d-r0",
"Layer": {
"DiffID": "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0",
"Digest": "sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609"
},
"SeveritySource": "nvd",
"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.1.1c-r0",
"FixedVersion": "1.1.1d-r0",
"Layer": {
"DiffID": "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0",
"Digest": "sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609"
},
"SeveritySource": "nvd",
"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"
]
}
]
}
]

View File

@@ -8,13 +8,13 @@ import (
"time"
"github.com/spf13/afero"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/internal/artifact"
"github.com/aquasecurity/trivy/internal/client"
"github.com/aquasecurity/trivy/internal/server"
"github.com/aquasecurity/trivy/internal/standalone"
tdb "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnerability"
@@ -27,164 +27,202 @@ type VersionInfo struct {
var (
templateFlag = cli.StringFlag{
Name: "template, t",
Value: "",
Usage: "output template",
EnvVar: "TRIVY_TEMPLATE",
Name: "template",
Aliases: []string{"t"},
Value: "",
Usage: "output template",
EnvVars: []string{"TRIVY_TEMPLATE"},
}
formatFlag = cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: "format (table, json, template)",
EnvVar: "TRIVY_FORMAT",
Name: "format",
Aliases: []string{"f"},
Value: "table",
Usage: "format (table, json, template)",
EnvVars: []string{"TRIVY_FORMAT"},
}
inputFlag = cli.StringFlag{
Name: "input, i",
Value: "",
Usage: "input file path instead of image name",
EnvVar: "TRIVY_INPUT",
Name: "input",
Aliases: []string{"i"},
Value: "",
Usage: "input file path instead of image name",
EnvVars: []string{"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",
Name: "severity",
Aliases: []string{"s"},
Value: strings.Join(types.SeverityNames, ","),
Usage: "severities of vulnerabilities to be displayed (comma separated)",
EnvVars: []string{"TRIVY_SEVERITY"},
}
outputFlag = cli.StringFlag{
Name: "output, o",
Usage: "output file name",
EnvVar: "TRIVY_OUTPUT",
Name: "output",
Aliases: []string{"o"},
Usage: "output file name",
EnvVars: []string{"TRIVY_OUTPUT"},
}
exitCodeFlag = cli.IntFlag{
Name: "exit-code",
Usage: "Exit code when vulnerabilities were found",
Value: 0,
EnvVar: "TRIVY_EXIT_CODE",
Name: "exit-code",
Usage: "Exit code when vulnerabilities were found",
Value: 0,
EnvVars: []string{"TRIVY_EXIT_CODE"},
}
skipUpdateFlag = cli.BoolFlag{
Name: "skip-update",
Usage: "skip db update",
EnvVar: "TRIVY_SKIP_UPDATE",
Name: "skip-update",
Usage: "skip db update",
EnvVars: []string{"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",
Name: "download-db-only",
Usage: "download/update vulnerability database but don't run a scan",
EnvVars: []string{"TRIVY_DOWNLOAD_DB_ONLY"},
}
resetFlag = cli.BoolFlag{
Name: "reset",
Usage: "remove all caches and database",
EnvVar: "TRIVY_RESET",
Name: "reset",
Usage: "remove all caches and database",
EnvVars: []string{"TRIVY_RESET"},
}
clearCacheFlag = cli.BoolFlag{
Name: "clear-cache, c",
Usage: "clear image caches without scanning",
EnvVar: "TRIVY_CLEAR_CACHE",
Name: "clear-cache",
Aliases: []string{"c"},
Usage: "clear image caches without scanning",
EnvVars: []string{"TRIVY_CLEAR_CACHE"},
}
quietFlag = cli.BoolFlag{
Name: "quiet, q",
Usage: "suppress progress bar and log output",
EnvVar: "TRIVY_QUIET",
Name: "quiet",
Aliases: []string{"q"},
Usage: "suppress progress bar and log output",
EnvVars: []string{"TRIVY_QUIET"},
}
noProgressFlag = cli.BoolFlag{
Name: "no-progress",
Usage: "suppress progress bar",
EnvVar: "TRIVY_NO_PROGRESS",
Name: "no-progress",
Usage: "suppress progress bar",
EnvVars: []string{"TRIVY_NO_PROGRESS"},
}
ignoreUnfixedFlag = cli.BoolFlag{
Name: "ignore-unfixed",
Usage: "display only fixed vulnerabilities",
EnvVar: "TRIVY_IGNORE_UNFIXED",
Name: "ignore-unfixed",
Usage: "display only fixed vulnerabilities",
EnvVars: []string{"TRIVY_IGNORE_UNFIXED"},
}
debugFlag = cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",
EnvVar: "TRIVY_DEBUG",
Name: "debug",
Aliases: []string{"d"},
Usage: "debug mode",
EnvVars: []string{"TRIVY_DEBUG"},
}
removedPkgsFlag = cli.BoolFlag{
Name: "removed-pkgs",
Usage: "detect vulnerabilities of removed packages (only for Alpine)",
EnvVar: "TRIVY_REMOVED_PKGS",
Name: "removed-pkgs",
Usage: "detect vulnerabilities of removed packages (only for Alpine)",
EnvVars: []string{"TRIVY_REMOVED_PKGS"},
}
vulnTypeFlag = cli.StringFlag{
Name: "vuln-type",
Value: "os,library",
Usage: "comma-separated list of vulnerability types (os,library)",
EnvVar: "TRIVY_VULN_TYPE",
Name: "vuln-type",
Value: "os,library",
Usage: "comma-separated list of vulnerability types (os,library)",
EnvVars: []string{"TRIVY_VULN_TYPE"},
}
cacheDirFlag = cli.StringFlag{
Name: "cache-dir",
Value: utils.DefaultCacheDir(),
Usage: "cache directory",
EnvVar: "TRIVY_CACHE_DIR",
Name: "cache-dir",
Value: utils.DefaultCacheDir(),
Usage: "cache directory",
EnvVars: []string{"TRIVY_CACHE_DIR"},
}
ignoreFileFlag = cli.StringFlag{
Name: "ignorefile",
Value: vulnerability.DefaultIgnoreFile,
Usage: "specify .trivyignore file",
EnvVar: "TRIVY_IGNOREFILE",
Name: "ignorefile",
Value: vulnerability.DefaultIgnoreFile,
Usage: "specify .trivyignore file",
EnvVars: []string{"TRIVY_IGNOREFILE"},
}
timeoutFlag = cli.DurationFlag{
Name: "timeout",
Value: time.Second * 120,
Usage: "docker timeout",
EnvVar: "TRIVY_TIMEOUT",
Name: "timeout",
Value: time.Second * 120,
Usage: "docker timeout",
EnvVars: []string{"TRIVY_TIMEOUT"},
}
lightFlag = cli.BoolFlag{
Name: "light",
Usage: "light mode: it's faster, but vulnerability descriptions and references are not displayed",
EnvVar: "TRIVY_LIGHT",
Name: "light",
Usage: "light mode: it's faster, but vulnerability descriptions and references are not displayed",
EnvVars: []string{"TRIVY_LIGHT"},
}
token = cli.StringFlag{
Name: "token",
Usage: "for authentication",
EnvVar: "TRIVY_TOKEN",
Name: "token",
Usage: "for authentication",
EnvVars: []string{"TRIVY_TOKEN"},
}
tokenHeader = cli.StringFlag{
Name: "token-header",
Value: "Trivy-Token",
Usage: "specify a header name for token",
EnvVar: "TRIVY_TOKEN_HEADER",
Name: "token-header",
Value: "Trivy-Token",
Usage: "specify a header name for token",
EnvVars: []string{"TRIVY_TOKEN_HEADER"},
}
globalFlags = []cli.Flag{
&quietFlag,
&debugFlag,
&cacheDirFlag,
}
imageFlags = []cli.Flag{
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&skipUpdateFlag,
&downloadDBOnlyFlag,
&resetFlag,
&clearCacheFlag,
&noProgressFlag,
&ignoreUnfixedFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&timeoutFlag,
&lightFlag,
}
// deprecated options
deprecatedFlags = []cli.Flag{
&cli.StringFlag{
Name: "only-update",
Usage: "deprecated",
EnvVars: []string{"TRIVY_ONLY_UPDATE"},
},
&cli.BoolFlag{
Name: "refresh",
Usage: "deprecated",
EnvVars: []string{"TRIVY_REFRESH"},
},
&cli.BoolFlag{
Name: "auto-refresh",
Usage: "deprecated",
EnvVars: []string{"TRIVY_AUTO_REFRESH"},
},
}
)
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}}
`
cli.VersionPrinter = func(c *cli.Context) {
showVersion(c.String("cache-dir"), c.String("format"), c.App.Version, c.App.Writer)
}
@@ -192,61 +230,52 @@ OPTIONS:
app := cli.NewApp()
app.Name = "trivy"
app.Version = version
app.ArgsUsage = "image_name"
app.ArgsUsage = "target"
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,
removedPkgsFlag,
vulnTypeFlag,
cacheDirFlag,
ignoreFileFlag,
timeoutFlag,
lightFlag,
flags := append(globalFlags, setHidden(deprecatedFlags, true)...)
flags = append(flags, setHidden(imageFlags, true)...)
// 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{
app.Flags = flags
app.Commands = []*cli.Command{
NewImageCommand(),
NewFilesystemCommand(),
NewRepositoryCommand(),
NewClientCommand(),
NewServerCommand(),
}
app.Action = standalone.Run
app.Action = artifact.ImageRun
return app
}
func setHidden(flags []cli.Flag, hidden bool) []cli.Flag {
var newFlags []cli.Flag
for _, flag := range flags {
var f cli.Flag
switch pf := flag.(type) {
case *cli.StringFlag:
stringFlag := *pf
stringFlag.Hidden = hidden
f = &stringFlag
case *cli.BoolFlag:
boolFlag := *pf
boolFlag.Hidden = hidden
f = &boolFlag
case *cli.IntFlag:
intFlag := *pf
intFlag.Hidden = hidden
f = &intFlag
case *cli.DurationFlag:
durationFlag := *pf
durationFlag.Hidden = hidden
f = &durationFlag
}
newFlags = append(newFlags, f)
}
return newFlags
}
func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer) {
var dbMeta *db.Metadata
@@ -288,69 +317,137 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
}
}
func NewClientCommand() cli.Command {
return cli.Command{
Name: "client",
Aliases: []string{"c"},
Usage: "client mode",
Action: client.Run,
func NewImageCommand() *cli.Command {
return &cli.Command{
Name: "image",
Aliases: []string{"i"},
ArgsUsage: "image_name",
Usage: "scan an image",
Action: artifact.ImageRun,
Flags: imageFlags,
}
}
func NewFilesystemCommand() *cli.Command {
return &cli.Command{
Name: "filesystem",
Aliases: []string{"fs"},
ArgsUsage: "dir",
Usage: "scan local filesystem",
Action: artifact.FilesystemRun,
Flags: []cli.Flag{
templateFlag,
formatFlag,
inputFlag,
severityFlag,
outputFlag,
exitCodeFlag,
clearCacheFlag,
quietFlag,
ignoreUnfixedFlag,
debugFlag,
removedPkgsFlag,
vulnTypeFlag,
ignoreFileFlag,
cacheDirFlag,
timeoutFlag,
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&clearCacheFlag,
&quietFlag,
&ignoreUnfixedFlag,
&debugFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&cacheDirFlag,
&timeoutFlag,
&noProgressFlag,
},
}
}
func NewRepositoryCommand() *cli.Command {
return &cli.Command{
Name: "repository",
Aliases: []string{"repo"},
ArgsUsage: "repo_url",
Usage: "scan remote repository",
Action: artifact.RepositoryRun,
Flags: []cli.Flag{
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&clearCacheFlag,
&quietFlag,
&ignoreUnfixedFlag,
&debugFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&cacheDirFlag,
&timeoutFlag,
&noProgressFlag,
},
}
}
func NewClientCommand() *cli.Command {
return &cli.Command{
Name: "client",
Aliases: []string{"c"},
ArgsUsage: "image_name",
Usage: "client mode",
Action: client.Run,
Flags: []cli.Flag{
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&clearCacheFlag,
&quietFlag,
&ignoreUnfixedFlag,
&debugFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&cacheDirFlag,
&timeoutFlag,
// original flags
token,
tokenHeader,
cli.StringFlag{
Name: "remote",
Value: "http://localhost:4954",
Usage: "server address",
EnvVar: "TRIVY_REMOTE",
&token,
&tokenHeader,
&cli.StringFlag{
Name: "remote",
Value: "http://localhost:4954",
Usage: "server address",
EnvVars: []string{"TRIVY_REMOTE"},
},
cli.StringSliceFlag{
Name: "custom-headers",
Usage: "custom headers",
EnvVar: "TRIVY_CUSTOM_HEADERS",
&cli.StringSliceFlag{
Name: "custom-headers",
Usage: "custom headers",
EnvVars: []string{"TRIVY_CUSTOM_HEADERS"},
},
},
}
}
func NewServerCommand() cli.Command {
return cli.Command{
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,
&skipUpdateFlag,
&downloadDBOnlyFlag,
&resetFlag,
&quietFlag,
&debugFlag,
&cacheDirFlag,
// original flags
token,
tokenHeader,
cli.StringFlag{
Name: "listen",
Value: "localhost:4954",
Usage: "listen address",
EnvVar: "TRIVY_LISTEN",
&token,
&tokenHeader,
&cli.StringFlag{
Name: "listen",
Value: "localhost:4954",
Usage: "listen address",
EnvVars: []string{"TRIVY_LISTEN"},
},
},
}

View File

@@ -0,0 +1,72 @@
package config
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/internal/config"
)
type Config struct {
config.GlobalConfig
config.ArtifactConfig
config.DBConfig
config.ImageConfig
config.ReportConfig
// deprecated
onlyUpdate string
// deprecated
refresh bool
// deprecated
autoRefresh bool
}
func New(c *cli.Context) (Config, error) {
gc, err := config.NewGlobalConfig(c)
if err != nil {
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
}
return Config{
GlobalConfig: gc,
ArtifactConfig: config.NewArtifactConfig(c),
DBConfig: config.NewDBConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),
onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),
autoRefresh: c.Bool("auto-refresh"),
}, nil
}
func (c *Config) Init(image bool) error {
if err := c.ReportConfig.Init(c.Logger); err != nil {
return err
}
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 err := c.DBConfig.Init(); err != nil {
return err
}
// --clear-cache, --download-db-only and --reset don't conduct the scan
if c.ClearCache || c.DownloadDBOnly || c.Reset {
return nil
}
if err := c.ArtifactConfig.Init(c.Context.Args(), c.Logger); err != nil {
cli.ShowAppHelp(c.Context)
return err
}
if image {
if err := c.ImageConfig.Init(c.Context.Args(), c.Logger); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,249 @@
package config
import (
"flag"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/internal/config"
)
func TestConfig_Init(t *testing.T) {
tests := []struct {
name string
globalConfig config.GlobalConfig
dbConfig config.DBConfig
imageConfig config.ImageConfig
reportConfig config.ReportConfig
args []string
logs []string
want Config
wantErr string
}{
{
name: "happy path",
reportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
},
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
want: Config{
GlobalConfig: config.GlobalConfig{
Quiet: true,
},
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.10",
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
},
},
},
{
name: "happy path: reset",
args: []string{"--reset"},
want: Config{
DBConfig: config.DBConfig{
Reset: true,
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
},
},
{
name: "happy path with an unknown severity",
args: []string{"--severity", "CRITICAL,INVALID", "centos:7"},
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "centos:7",
},
},
},
{
name: "deprecated options",
args: []string{"--only-update", "alpine", "--severity", "LOW", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "debian:buster",
},
onlyUpdate: "alpine",
},
},
{
name: "invalid option combination: --template enabled without --format",
args: []string{"--template", "@contrib/gitlab.tpl", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
{
name: "invalid option combination: --template and --format json",
args: []string{"--format", "json", "--template", "@contrib/gitlab.tpl", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
Format: "json",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
{
name: "invalid option combination: --format template without --template",
args: []string{"--format", "template", "--severity", "MEDIUM", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
{
name: "with latest tag",
args: []string{"--auto-refresh", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "gcr.io/distroless/base",
},
autoRefresh: true,
},
},
{
name: "sad: skip and download db",
args: []string{"--skip-update", "--download-db-only", "alpine:3.10"},
wantErr: "--skip-update and --download-db-only options can not be specified both",
},
{
name: "sad: multiple image names",
args: []string{"centos:7", "alpine:3.10"},
logs: []string{
"multiple targets cannot be specified",
},
wantErr: "arguments error",
},
{
name: "sad: no image name",
logs: []string{
"trivy requires at least 1 argument or --input option",
},
wantErr: "arguments error",
},
{
name: "sad: invalid image name",
args: []string{`!"#$%&'()`},
wantErr: "could not parse reference",
},
}
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)
set.Bool("quiet", false, "")
set.Bool("no-progress", false, "")
set.Bool("reset", false, "")
set.Bool("skip-update", false, "")
set.Bool("download-db-only", false, "")
set.Bool("auto-refresh", false, "")
set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "")
set.String("only-update", "", "")
set.String("template", "", "")
set.String("format", "", "")
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c, err := New(ctx)
require.NoError(t, err, err)
c.GlobalConfig.Logger = logger.Sugar()
err = c.Init(true)
// 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.GlobalConfig.Context = ctx
tt.want.GlobalConfig.Logger = logger.Sugar()
assert.Equal(t, tt.want, c, tt.name)
})
}
}

36
internal/artifact/fs.go Normal file
View File

@@ -0,0 +1,36 @@
package artifact
import (
"context"
"time"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/artifact/config"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
return s, cleanup, nil
}
func FilesystemRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}
// initialize config
if err = c.Init(false); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
return run(c, filesystemScanner)
}

View File

@@ -0,0 +1,50 @@
package artifact
import (
"context"
"time"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/artifact/config"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
return s, func() {}, nil
}
func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
}
return s, cleanup, nil
}
func ImageRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}
// initialize config
if err = c.Init(true); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
if c.Input != "" {
// scan tar file
return run(c, archiveScanner)
}
return run(c, dockerScanner)
}

View File

@@ -1,6 +1,6 @@
// +build wireinject
package standalone
package artifact
import (
"context"
@@ -12,18 +12,28 @@ import (
"github.com/google/wire"
)
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache,
timeout time.Duration) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache,
timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil
}
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}

View File

@@ -0,0 +1,36 @@
package artifact
import (
"context"
"time"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/artifact/config"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
return s, cleanup, nil
}
func RepositoryRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}
// initialize config
if err = c.Init(false); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
return run(c, repositoryScanner)
}

View File

@@ -1,42 +1,32 @@
package standalone
package artifact
import (
"context"
l "log"
"os"
"time"
"github.com/aquasecurity/trivy-db/pkg/db"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/artifact/config"
"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/scanner"
"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)
}
type InitializeScanner func(context.Context, string, cache.ArtifactCache, cache.LocalArtifactCache, time.Duration) (
scanner.Scanner, func(), error)
func run(c config.Config) (err error) {
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
func run(c config.Config, initializeScanner InitializeScanner) 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, err := cache.NewFSCache(c.CacheDir)
@@ -70,32 +60,25 @@ func run(c config.Config) (err error) {
}
defer db.Close()
var scanner scanner.Scanner
ctx := context.Background()
cleanup := func() {}
target := c.Target
if c.Input != "" {
// scan tar file
scanner, err = initializeArchiveScanner(ctx, c.Input, cacheClient, cacheClient, c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
} else {
// scan an image in Docker Engine or Docker Registry
scanner, cleanup, err = initializeDockerScanner(ctx, c.ImageName, cacheClient, cacheClient, c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the docker scanner: %w", err)
}
target = c.Input
}
ctx := context.Background()
scanner, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize a scanner: %w", err)
}
defer cleanup()
scanOptions := types.ScanOptions{
VulnType: c.VulnType,
ScanRemovedPackages: c.ScanRemovedPkgs,
ScanRemovedPackages: c.ScanRemovedPkgs, // this is valid only for image subcommand
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
results, err := scanner.ScanImage(scanOptions)
results, err := scanner.ScanArtifact(ctx, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}

View File

@@ -0,0 +1,96 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package artifact
import (
"context"
"github.com/aquasecurity/fanal/applier"
image2 "github.com/aquasecurity/fanal/artifact/image"
local2 "github.com/aquasecurity/fanal/artifact/local"
"github.com/aquasecurity/fanal/artifact/remote"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"time"
)
// Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, nil, err
}
imageImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
artifact := image2.NewArtifact(imageImage, artifactCache)
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
cleanup()
}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration) (scanner.Scanner, error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
imageImage, err := image.NewArchiveImage(filePath)
if err != nil {
return scanner.Scanner{}, err
}
artifact := image2.NewArtifact(imageImage, artifactCache)
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, nil
}
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
artifact := local2.NewArtifact(dir, artifactCache)
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
}, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
artifact, cleanup, err := remote.NewArtifact(url, artifactCache)
if err != nil {
return scanner.Scanner{}, nil, err
}
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
cleanup()
}, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
return client
}

View File

@@ -2,97 +2,53 @@ package config
import (
"net/http"
"os"
"strings"
"time"
"github.com/genuinetools/reg/registry"
"github.com/urfave/cli"
"go.uber.org/zap"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/internal/config"
)
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
ScanRemovedPkgs bool
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
config.GlobalConfig
config.ArtifactConfig
config.ImageConfig
config.ReportConfig
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
// this field is populated in Init()
CustomHeaders http.Header
}
func New(c *cli.Context) (Config, error) {
debug := c.Bool("debug")
quiet := c.Bool("quiet")
logger, err := log.NewLogger(debug, quiet)
gc, err := config.NewGlobalConfig(c)
if err != nil {
return Config{}, xerrors.New("failed to create a logger")
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
}
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"),
ScanRemovedPkgs: c.Bool("removed-pkgs"),
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"),
GlobalConfig: gc,
ArtifactConfig: config.NewArtifactConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),
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
// --clear-cache doesn't conduct the scan
if c.ClearCache {
return nil
}
c.CustomHeaders = splitCustomHeaders(c.customHeaders)
// add token to custom headers
@@ -100,59 +56,22 @@ func (c *Config) Init() (err error) {
c.CustomHeaders.Set(c.tokenHeader, c.token)
}
// --clear-cache doesn't conduct the scan
if c.ClearCache {
return nil
if err := c.ReportConfig.Init(c.Logger); err != nil {
return err
}
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")
if err := c.ArtifactConfig.Init(c.Context.Args(), c.Logger); err != nil {
return err
}
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")
}
if err := c.ImageConfig.Init(c.Context.Args(), c.Logger); err != nil {
cli.ShowAppHelp(c.Context)
return err
}
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 {

View File

@@ -6,193 +6,235 @@ import (
"os"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"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"
"github.com/aquasecurity/trivy/internal/config"
)
func TestNew(t *testing.T) {
func TestConfig_Init(t *testing.T) {
tests := []struct {
name string
args []string
want Config
name string
globalConfig config.GlobalConfig
imageConfig config.ImageConfig
reportConfig config.ReportConfig
args []string
logs []string
want Config
wantErr string
}{
{
name: "happy path",
args: []string{"-quiet", "--cache-dir", "/tmp/test"},
reportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
},
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
want: Config{
Quiet: true,
CacheDir: "/tmp/test",
GlobalConfig: config.GlobalConfig{
Quiet: true,
},
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.10",
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
},
CustomHeaders: http.Header{},
},
},
}
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"},
name: "happy path with token and token header",
args: []string{"--token", "secret", "--token-header", "X-Trivy-Token", "alpine:3.11"},
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",
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.11",
},
token: "secret",
tokenHeader: "X-Trivy-Token",
CustomHeaders: http.Header{
"Trivy-Token": []string{"foobar"},
"X-Trivy-Token": []string{"secret"},
},
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
vulnType: "os,library",
name: "happy path with good custom headers",
args: []string{"--custom-headers", "foo:bar", "alpine:3.11"},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.11",
},
customHeaders: []string{"foo:bar"},
CustomHeaders: http.Header{
"Foo": []string{"bar"},
},
},
args: []string{"centos:7"},
},
{
name: "happy path with bad custom headers",
args: []string{"--custom-headers", "foobaz", "alpine:3.11"},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.11",
},
customHeaders: []string{"foobaz"},
CustomHeaders: http.Header{},
},
},
{
name: "happy path with an unknown severity",
args: []string{"--severity", "CRITICAL,INVALID", "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),
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "centos:7",
},
CustomHeaders: http.Header{},
},
},
{
name: "invalid option combination: --template enabled without --format",
args: []string{"--template", "@contrib/gitlab.tpl", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
},
},
{
name: "invalid option combination: --template and --format json",
args: []string{"--format", "json", "--template", "@contrib/gitlab.tpl", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
Format: "json",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
},
},
{
name: "invalid option combination: --format template without --template",
args: []string{"--format", "template", "--severity", "MEDIUM", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
},
},
{
name: "invalid option combination: --format template without --template",
args: []string{"--format", "template", "--severity", "MEDIUM", "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{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: 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),
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ArtifactConfig: config.ArtifactConfig{
Target: "gcr.io/distroless/base",
},
CustomHeaders: 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",
"multiple targets 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",
},
name: "sad: invalid image name",
args: []string{`!"#$%&'()`},
wantErr: "invalid image: parsing image",
wantErr: "could not parse reference",
},
}
for _, tt := range tests {
@@ -202,33 +244,25 @@ func TestConfig_Init(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
set.Bool("quiet", false, "")
set.Bool("no-progress", false, "")
set.Bool("clear-cache", false, "")
set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "")
set.String("template", "", "")
set.String("format", "", "")
set.String("token", "", "")
set.String("token-header", "", "")
set.Var(&cli.StringSlice{}, "custom-headers", "")
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,
}
c, err := New(ctx)
require.NoError(t, err, err)
err := c.Init()
c.GlobalConfig.Logger = logger.Sugar()
err = c.Init()
// tests log messages
var gotMessages []string
@@ -240,16 +274,15 @@ func TestConfig_Init(t *testing.T) {
// 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)
tt.want.GlobalConfig.Context = ctx
tt.want.GlobalConfig.Logger = logger.Sugar()
assert.Equal(t, tt.want, c, tt.name)
})
}
}

View File

@@ -13,13 +13,13 @@ import (
"github.com/google/wire"
)
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, customHeaders client.CustomHeaders,
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration) (scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, customHeaders client.CustomHeaders,
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil

View File

@@ -4,6 +4,9 @@ import (
"context"
"os"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/internal/client/config"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/log"
@@ -12,8 +15,6 @@ import (
"github.com/aquasecurity/trivy/pkg/scanner"
"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 {
@@ -57,7 +58,7 @@ func run(c config.Config) (err error) {
}
} else {
// scan an image in Docker Engine or Docker Registry
scanner, cleanup, err = initializeDockerScanner(ctx, c.ImageName, remoteCache,
scanner, cleanup, err = initializeDockerScanner(ctx, c.Target, remoteCache,
client.CustomHeaders(c.CustomHeaders), client.RemoteURL(c.RemoteAddr), c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the docker scanner: %w", err)
@@ -71,7 +72,7 @@ func run(c config.Config) (err error) {
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
results, err := scanner.ScanImage(scanOptions)
results, err := scanner.ScanArtifact(ctx, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}

View File

@@ -7,9 +7,9 @@ package client
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
image2 "github.com/aquasecurity/fanal/artifact/image"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/fanal/image"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
@@ -20,33 +20,33 @@ import (
// Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, func(), error) {
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, func(), error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, nil, err
}
extractor, cleanup, err := docker.NewDockerExtractor(ctx, imageName, dockerOption)
imageImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
config := analyzer.New(extractor, layerCache)
scanner2 := scanner.NewScanner(clientScanner, config)
artifact := image2.NewArtifact(imageImage, artifactCache)
scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, func() {
cleanup()
}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
extractor, err := docker.NewArchiveImageExtractor(filePath)
imageImage, err := image.NewArchiveImage(filePath)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scanner2 := scanner.NewScanner(clientScanner, config)
artifact := image2.NewArtifact(imageImage, artifactCache)
scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, nil
}

View File

@@ -0,0 +1,42 @@
package config
import (
"time"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
)
type ArtifactConfig struct {
Input string
Timeout time.Duration
ClearCache bool
// this field is populated in Init()
Target string
}
func NewArtifactConfig(c *cli.Context) ArtifactConfig {
return ArtifactConfig{
Input: c.String("input"),
Timeout: c.Duration("timeout"),
ClearCache: c.Bool("clear-cache"),
}
}
func (c *ArtifactConfig) Init(args cli.Args, logger *zap.SugaredLogger) (err error) {
if c.Input == "" && args.Len() == 0 {
logger.Error(`trivy requires at least 1 argument or --input option`)
return xerrors.New("arguments error")
} else if args.Len() > 1 {
logger.Error(`multiple targets cannot be specified`)
return xerrors.New("arguments error")
}
if c.Input == "" {
c.Target = args.First()
}
return nil
}

View File

@@ -0,0 +1,80 @@
package config_test
import (
"flag"
"testing"
"github.com/aquasecurity/trivy/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
func TestArtifactConfig_Init(t *testing.T) {
tests := []struct {
name string
args []string
logs []string
want config.ArtifactConfig
wantErr string
}{
{
name: "happy path",
args: []string{"alpine:3.10"},
want: config.ArtifactConfig{
Target: "alpine:3.10",
},
},
{
name: "sad: multiple image names",
args: []string{"centos:7", "alpine:3.10"},
logs: []string{
"multiple targets cannot be specified",
},
wantErr: "arguments error",
},
{
name: "sad: no image name",
logs: []string{
"trivy requires at least 1 argument or --input option",
},
wantErr: "arguments error",
},
}
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.NewArtifactConfig(ctx)
err := c.Init(ctx.Args(), logger.Sugar())
// 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)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, c, tt.name)
})
}
}

31
internal/config/db.go Normal file
View File

@@ -0,0 +1,31 @@
package config
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)
type DBConfig struct {
Reset bool
DownloadDBOnly bool
SkipUpdate bool
Light bool
NoProgress bool
}
func NewDBConfig(c *cli.Context) DBConfig {
return DBConfig{
Reset: c.Bool("reset"),
DownloadDBOnly: c.Bool("download-db-only"),
SkipUpdate: c.Bool("skip-update"),
Light: c.Bool("light"),
NoProgress: c.Bool("no-progress"),
}
}
func (c *DBConfig) Init() (err error) {
if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("--skip-update and --download-db-only options can not be specified both")
}
return nil
}

View File

@@ -0,0 +1,88 @@
package config_test
import (
"flag"
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/internal/config"
)
func TestNewDBConfig(t *testing.T) {
tests := []struct {
name string
args []string
want config.DBConfig
}{
{
name: "happy path",
args: []string{"--reset", "--skip-update"},
want: config.DBConfig{
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("reset", false, "")
set.Bool("skip-update", false, "")
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got := config.NewDBConfig(c)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestDBConfig_Init(t *testing.T) {
type fields struct {
Reset bool
DownloadDBOnly bool
SkipUpdate bool
Light bool
}
tests := []struct {
name string
fields fields
wantErr string
}{
{
name: "happy path",
fields: fields{
Light: true,
},
},
{
name: "sad path",
fields: fields{
DownloadDBOnly: true,
SkipUpdate: true,
},
wantErr: "--skip-update and --download-db-only options can not be specified both",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &config.DBConfig{
Reset: tt.fields.Reset,
DownloadDBOnly: tt.fields.DownloadDBOnly,
SkipUpdate: tt.fields.SkipUpdate,
Light: tt.fields.Light,
}
err := c.Init()
if tt.wantErr != "" {
assert.EqualError(t, err, tt.wantErr, err)
} else {
assert.NoError(t, err)
}
})
}
}

38
internal/config/global.go Normal file
View File

@@ -0,0 +1,38 @@
package config
import (
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/log"
)
type GlobalConfig struct {
Context *cli.Context
Logger *zap.SugaredLogger
AppVersion string
Quiet bool
Debug bool
CacheDir string
}
func NewGlobalConfig(c *cli.Context) (GlobalConfig, error) {
quiet := c.Bool("quiet")
debug := c.Bool("debug")
logger, err := log.NewLogger(quiet, debug)
if err != nil {
return GlobalConfig{}, xerrors.New("failed to create a logger")
}
return GlobalConfig{
Context: c,
Logger: logger,
AppVersion: c.App.Version,
Quiet: quiet,
Debug: debug,
CacheDir: c.String("cache-dir"),
}, nil
}

View File

@@ -0,0 +1,46 @@
package config_test
import (
"flag"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/internal/config"
)
func TestNewGlobalConfig(t *testing.T) {
tests := []struct {
name string
args []string
want config.GlobalConfig
}{
{
name: "happy path",
args: []string{"--quiet", "--debug"},
want: config.GlobalConfig{
Quiet: true,
Debug: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &cli.App{}
set := flag.NewFlagSet("test", 0)
set.Bool("debug", false, "")
set.Bool("quiet", false, "")
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got, err := config.NewGlobalConfig(c)
require.NoError(t, err, err)
assert.Equal(t, tt.want.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.Debug, got.Debug, tt.name)
assert.Equal(t, tt.want.CacheDir, got.CacheDir, tt.name)
})
}
}

35
internal/config/image.go Normal file
View File

@@ -0,0 +1,35 @@
package config
import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
)
type ImageConfig struct {
ScanRemovedPkgs bool
}
func NewImageConfig(c *cli.Context) ImageConfig {
return ImageConfig{
ScanRemovedPkgs: c.Bool("removed-pkgs"),
}
}
func (c *ImageConfig) Init(args cli.Args, logger *zap.SugaredLogger) (err error) {
imageName := args.First()
// Check whether 'latest' tag is used
if imageName != "" {
ref, err := name.ParseReference(imageName)
if err != nil {
return xerrors.Errorf("invalid image: %w", err)
}
if ref.Identifier() == "latest" {
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
}

View File

@@ -0,0 +1,72 @@
package config_test
import (
"flag"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
"github.com/aquasecurity/trivy/internal/config"
)
func TestImageConfig_Init(t *testing.T) {
tests := []struct {
name string
args []string
logs []string
wantErr string
}{
{
name: "happy path",
args: []string{"alpine:3.10"},
},
{
name: "with latest tag",
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",
},
},
{
name: "sad: invalid image name",
args: []string{`!"#$%&'()`},
wantErr: "could not parse reference",
},
}
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.NewImageConfig(ctx)
err := c.Init(ctx.Args(), logger.Sugar())
// 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)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
})
}
}

87
internal/config/report.go Normal file
View File

@@ -0,0 +1,87 @@
package config
import (
"os"
"strings"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
)
type ReportConfig struct {
Format string
Template string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
// these variables are not exported
vulnType string
output string
severities string
// these variables are populated by Init()
VulnType []string
Output *os.File
Severities []dbTypes.Severity
}
func NewReportConfig(c *cli.Context) ReportConfig {
return ReportConfig{
output: c.String("output"),
Format: c.String("format"),
Template: c.String("template"),
vulnType: c.String("vuln-type"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
}
}
func (c *ReportConfig) Init(logger *zap.SugaredLogger) (err error) {
if c.Template != "" {
if c.Format == "" {
logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
} else if c.Format != "template" {
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 == "" {
logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
}
c.Severities = c.splitSeverity(logger, c.severities)
c.VulnType = strings.Split(c.vulnType, ",")
// for testability
c.severities = ""
c.vulnType = ""
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)
}
}
return nil
}
func (c *ReportConfig) splitSeverity(logger *zap.SugaredLogger, severity string) []dbTypes.Severity {
logger.Debugf("Severities: %s", severity)
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -0,0 +1,162 @@
package config
import (
"flag"
"os"
"testing"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
func TestReportReportConfig_Init(t *testing.T) {
type fields struct {
output string
Format string
Template string
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
VulnType []string
Output *os.File
Severities []dbTypes.Severity
}
tests := []struct {
name string
fields fields
args []string
logs []string
want ReportConfig
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
},
args: []string{"alpine:3.10"},
want: ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
},
},
{
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: ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
VulnType: []string{"os", "library"},
Output: os.Stdout,
},
},
{
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: ReportConfig{
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
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: ReportConfig{
Format: "json",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
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: ReportConfig{
Format: "template",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
VulnType: []string{""},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
core, obs := observer.New(zap.InfoLevel)
logger := zap.New(core)
set := flag.NewFlagSet("test", 0)
_ = set.Parse(tt.args)
c := &ReportConfig{
output: tt.fields.output,
Format: tt.fields.Format,
Template: tt.fields.Template,
vulnType: tt.fields.vulnType,
severities: tt.fields.severities,
IgnoreFile: tt.fields.IgnoreFile,
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
ExitCode: tt.fields.ExitCode,
Output: tt.fields.Output,
}
err := c.Init(logger.Sugar())
// 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)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

View File

@@ -17,15 +17,15 @@ import (
var SuperSet = wire.NewSet(
cache.NewFSCache,
wire.Bind(new(cache.LocalImageCache), new(cache.FSCache)),
wire.Bind(new(cache.LocalArtifactCache), new(cache.FSCache)),
NewCache,
)
type Cache struct {
client cache.LocalImageCache
client cache.LocalArtifactCache
}
func NewCache(client cache.LocalImageCache) Cache {
func NewCache(client cache.LocalArtifactCache) Cache {
return Cache{client: client}
}

View File

@@ -1,52 +1,37 @@
package config
import (
"github.com/urfave/cli"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/internal/config"
"github.com/urfave/cli/v2"
)
type Config struct {
context *cli.Context
Quiet bool
Debug bool
CacheDir string
Reset bool
DownloadDBOnly bool
SkipUpdate bool
config.GlobalConfig
config.DBConfig
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,
// the error is ignored because logger is unnecessary
gc, _ := config.NewGlobalConfig(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"),
return Config{
GlobalConfig: gc,
DBConfig: config.NewDBConfig(c),
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")
if err := c.DBConfig.Init(); err != nil {
return err
}
c.AppVersion = c.context.App.Version
return nil
}

View File

@@ -1,30 +1,36 @@
package config
package config_test
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"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/internal/config"
c "github.com/aquasecurity/trivy/internal/server/config"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
args []string
want Config
want c.Config
}{
{
name: "happy path",
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update"},
want: Config{
Quiet: true,
Reset: true,
SkipUpdate: true,
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update", "--listen", "localhost:8080"},
want: c.Config{
GlobalConfig: config.GlobalConfig{
Quiet: true,
},
DBConfig: config.DBConfig{
Reset: true,
SkipUpdate: true,
NoProgress: true,
},
Listen: "localhost:8080",
},
},
}
@@ -36,108 +42,54 @@ func TestNew(t *testing.T) {
set.Bool("no-progress", false, "")
set.Bool("reset", false, "")
set.Bool("skip-update", false, "")
set.String("listen", "", "")
c := cli.NewContext(app, set, nil)
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
tt.want.context = c
tt.want.GlobalConfig.Context = ctx
got := New(c)
assert.Equal(t, tt.want, got, tt.name)
got := c.New(ctx)
assert.Equal(t, tt.want.GlobalConfig.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.DBConfig, got.DBConfig, tt.name)
assert.Equal(t, tt.want.Listen, got.Listen, 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 string
globalConfig config.GlobalConfig
dbConfig config.DBConfig
args []string
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,
dbConfig: config.DBConfig{
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,
dbConfig: config.DBConfig{
SkipUpdate: true,
DownloadDBOnly: true,
},
args: []string{"alpine:3.10"},
wantErr: "The --skip-update and --download-db-only option can not be specified both",
wantErr: "--skip-update and --download-db-only options 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,
c := &c.Config{
DBConfig: tt.dbConfig,
}
err := c.Init()
@@ -151,9 +103,6 @@ func TestConfig_Init(t *testing.T) {
default:
assert.NoError(t, err, tt.name)
}
tt.want.context = ctx
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

View File

@@ -1,7 +1,7 @@
package server
import (
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"

View File

@@ -1,174 +0,0 @@
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
ScanRemovedPkgs bool
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"),
ScanRemovedPkgs: c.Bool("removed-pkgs"),
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
}

View File

@@ -1,361 +0,0 @@
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)
})
}
}

View File

@@ -1,65 +0,0 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package standalone
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"time"
)
// Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache, timeout time.Duration) (scanner.Scanner, func(), error) {
applier := analyzer.NewApplier(localImageCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applier, detector, libraryDetector)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, nil, err
}
extractor, cleanup, err := docker.NewDockerExtractor(ctx, imageName, dockerOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
config := analyzer.New(extractor, layerCache)
scannerScanner := scanner.NewScanner(localScanner, config)
return scannerScanner, func() {
cleanup()
}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache, timeout time.Duration) (scanner.Scanner, error) {
applier := analyzer.NewApplier(localImageCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applier, detector, libraryDetector)
extractor, err := docker.NewArchiveImageExtractor(filePath)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scannerScanner := scanner.NewScanner(localScanner, config)
return scannerScanner, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
return client
}

16
pkg/cache/remote.go vendored
View File

@@ -20,32 +20,32 @@ type RemoteCache struct {
type RemoteURL string
func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ImageCache {
func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ArtifactCache {
ctx := client.WithCustomHeaders(context.Background(), customHeaders)
c := rpcCache.NewCacheProtobufClient(string(url), &http.Client{})
return &RemoteCache{ctx: ctx, client: c}
}
func (c RemoteCache) PutImage(imageID string, imageInfo types.ImageInfo) error {
_, err := c.client.PutImage(c.ctx, rpc.ConvertToRpcImageInfo(imageID, imageInfo))
func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) error {
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRpcArtifactInfo(imageID, imageInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
func (c RemoteCache) PutLayer(diffID string, layerInfo types.LayerInfo) error {
_, err := c.client.PutLayer(c.ctx, rpc.ConvertToRpcLayerInfo(diffID, layerInfo))
func (c RemoteCache) PutBlob(diffID string, layerInfo types.BlobInfo) error {
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRpcBlobInfo(diffID, layerInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
func (c RemoteCache) MissingLayers(imageID string, layerIDs []string) (bool, []string, error) {
layers, err := c.client.MissingLayers(c.ctx, rpc.ConvertToMissingLayersRequest(imageID, layerIDs))
func (c RemoteCache) MissingBlobs(imageID string, layerIDs []string) (bool, []string, error) {
layers, err := c.client.MissingBlobs(c.ctx, rpc.ConvertToMissingBlobsRequest(imageID, layerIDs))
if err != nil {
return false, nil, xerrors.Errorf("unable to fetch missing layers: %w", err)
}
return layers.MissingImage, layers.MissingLayerIds, nil
return layers.MissingArtifact, layers.MissingBlobIds, nil
}

View File

@@ -25,29 +25,29 @@ type mockCacheServer struct {
cache fcache.Cache
}
func (s *mockCacheServer) PutImage(_ context.Context, in *rpcCache.PutImageRequest) (*google_protobuf.Empty, error) {
if strings.Contains(in.ImageId, "invalid") {
func (s *mockCacheServer) PutArtifact(_ context.Context, in *rpcCache.PutArtifactRequest) (*google_protobuf.Empty, error) {
if strings.Contains(in.ArtifactId, "invalid") {
return &google_protobuf.Empty{}, xerrors.New("invalid image ID")
}
return &google_protobuf.Empty{}, nil
}
func (s *mockCacheServer) PutLayer(_ context.Context, in *rpcCache.PutLayerRequest) (*google_protobuf.Empty, error) {
func (s *mockCacheServer) PutBlob(_ context.Context, in *rpcCache.PutBlobRequest) (*google_protobuf.Empty, error) {
if strings.Contains(in.DiffId, "invalid") {
return &google_protobuf.Empty{}, xerrors.New("invalid layer ID")
}
return &google_protobuf.Empty{}, nil
}
func (s *mockCacheServer) MissingLayers(_ context.Context, in *rpcCache.MissingLayersRequest) (*rpcCache.MissingLayersResponse, error) {
func (s *mockCacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBlobsRequest) (*rpcCache.MissingBlobsResponse, error) {
var layerIDs []string
for _, layerID := range in.LayerIds[:len(in.LayerIds)-1] {
for _, layerID := range in.BlobIds[:len(in.BlobIds)-1] {
if strings.Contains(layerID, "invalid") {
return nil, xerrors.New("invalid layer ID")
}
layerIDs = append(layerIDs, layerID)
}
return &rpcCache.MissingLayersResponse{MissingImage: true, MissingLayerIds: layerIDs}, nil
return &rpcCache.MissingBlobsResponse{MissingArtifact: true, MissingBlobIds: layerIDs}, nil
}
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
@@ -60,7 +60,7 @@ func withToken(base http.Handler, token, tokenHeader string) http.Handler {
})
}
func TestRemoteCache_PutImage(t *testing.T) {
func TestRemoteCache_PutArtifact(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
@@ -68,7 +68,7 @@ func TestRemoteCache_PutImage(t *testing.T) {
type args struct {
imageID string
imageInfo types.ImageInfo
imageInfo types.ArtifactInfo
customHeaders http.Header
}
tests := []struct {
@@ -80,7 +80,7 @@ func TestRemoteCache_PutImage(t *testing.T) {
name: "happy path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
imageInfo: types.ImageInfo{
imageInfo: types.ArtifactInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Time{},
@@ -102,7 +102,7 @@ func TestRemoteCache_PutImage(t *testing.T) {
name: "sad path",
args: args{
imageID: "sha256:invalid",
imageInfo: types.ImageInfo{
imageInfo: types.ArtifactInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Time{},
@@ -135,7 +135,7 @@ func TestRemoteCache_PutImage(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
err := c.PutImage(tt.args.imageID, tt.args.imageInfo)
err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
@@ -147,7 +147,7 @@ func TestRemoteCache_PutImage(t *testing.T) {
}
}
func TestRemoteCache_PutLayer(t *testing.T) {
func TestRemoteCache_PutBlob(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
@@ -155,7 +155,7 @@ func TestRemoteCache_PutLayer(t *testing.T) {
type args struct {
diffID string
layerInfo types.LayerInfo
layerInfo types.BlobInfo
customHeaders http.Header
}
tests := []struct {
@@ -196,7 +196,7 @@ func TestRemoteCache_PutLayer(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
err := c.PutLayer(tt.args.diffID, tt.args.layerInfo)
err := c.PutBlob(tt.args.diffID, tt.args.layerInfo)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
@@ -208,7 +208,7 @@ func TestRemoteCache_PutLayer(t *testing.T) {
}
}
func TestRemoteCache_MissingLayers(t *testing.T) {
func TestRemoteCache_MissingBlobs(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
@@ -274,7 +274,7 @@ func TestRemoteCache_MissingLayers(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
gotMissingImage, gotMissingLayerIDs, err := c.MissingLayers(tt.args.imageID, tt.args.layerIDs)
gotMissingImage, gotMissingLayerIDs, err := c.MissingBlobs(tt.args.imageID, tt.args.layerIDs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)

View File

@@ -1,23 +1,16 @@
package bundler
import (
"os"
"strings"
"github.com/knqyf263/go-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/bundler"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
bundlerSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/bundler"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
const (
scannerType = "bundler"
)
var (
platformReplacer = strings.NewReplacer(
"-java", "+java",
@@ -34,7 +27,7 @@ type VulnSrc interface {
Get(pkgName string) ([]bundlerSrc.Advisory, error)
}
type Scanner struct {
type Advisory struct {
vs VulnSrc
}
@@ -45,16 +38,16 @@ func massageLockFileVersion(version string) string {
return platformReplacer.Replace(version)
}
func NewScanner() *Scanner {
return &Scanner{
func NewAdvisory() *Advisory {
return &Advisory{
vs: bundlerSrc.NewVulnSrc(),
}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
func (a *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := a.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get %s advisories: %w", s.Type(), err)
return nil, xerrors.Errorf("failed to get bundler advisories: %w", err)
}
var vulns []types.DetectedVulnerability
@@ -76,20 +69,3 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Detec
}
return vulns, nil
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
libs, err := bundler.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid Gemfile.lock format: %w", err)
}
for _, lib := range libs {
lib.Version = massageLockFileVersion(lib.Version)
}
return libs, nil
}
func (s *Scanner) Type() string {
return scannerType
}

View File

@@ -46,7 +46,7 @@ func TestScanner_Detect(t *testing.T) {
PatchedVersions: []string{">= 1.9.26"},
},
}, nil)
s := Scanner{
s := Advisory{
vs: mockVulnSrc,
}
@@ -55,7 +55,7 @@ func TestScanner_Detect(t *testing.T) {
v, _ := version.NewVersion(versionStr)
vulns, err := s.Detect("ffi", v)
vulns, err := s.DetectVulnerabilities("ffi", v)
assert.Nil(t, err)
assert.Equal(t, 1, len(vulns))

View File

@@ -1,37 +1,30 @@
package cargo
import (
"os"
"strings"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/go-dep-parser/pkg/cargo"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
cargoSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/cargo"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-version"
"golang.org/x/xerrors"
)
const (
scannerType = "cargo"
)
type Scanner struct {
type Advisory struct {
vs cargoSrc.VulnSrc
}
func NewScanner() *Scanner {
return &Scanner{
func NewAdvisory() *Advisory {
return &Advisory{
vs: cargoSrc.NewVulnSrc(),
}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get %s advisories: %w", s.Type(), err)
return nil, xerrors.Errorf("failed to get cargo advisories: %w", err)
}
var vulns []types.DetectedVulnerability
@@ -50,15 +43,3 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Detec
}
return vulns, nil
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
libs, err := cargo.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid Cargo.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) Type() string {
return scannerType
}

View File

@@ -2,39 +2,32 @@ package composer
import (
"fmt"
"os"
"strings"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/composer"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
composerSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/composer"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-version"
)
const (
scannerType = "composer"
)
type Scanner struct {
type Advisory struct {
vs composerSrc.VulnSrc
}
func NewScanner() *Scanner {
return &Scanner{
func NewAdvisory() *Advisory {
return &Advisory{
vs: composerSrc.NewVulnSrc(),
}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
ref := fmt.Sprintf("composer://%s", pkgName)
advisories, err := s.vs.Get(ref)
if err != nil {
return nil, xerrors.Errorf("failed to get %s advisories: %w", s.Type(), err)
return nil, xerrors.Errorf("failed to get composer advisories: %w", err)
}
var vulns []types.DetectedVulnerability
@@ -64,14 +57,3 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Detec
}
return vulns, nil
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
libs, err := composer.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid composer.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) Type() string {
return scannerType
}

View File

@@ -37,9 +37,9 @@ func NewDetector(factory Factory) Detector {
func (d Detector) Detect(_, filePath string, _ time.Time, pkgs []ftypes.LibraryInfo) ([]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")
driver, err := d.driverFactory.NewDriver(filepath.Base(filePath))
if err != nil {
return nil, xerrors.Errorf("failed to new driver: %w", err)
}
vulns, err := detect(driver, pkgs)

View File

@@ -1,50 +1,112 @@
package library
import (
"os"
"fmt"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/fanal/analyzer/library"
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
"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/ghsa"
"github.com/aquasecurity/trivy/pkg/detector/library/node"
"github.com/aquasecurity/trivy/pkg/detector/library/python"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/knqyf263/go-version"
"golang.org/x/xerrors"
)
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, error)
}
type Factory interface {
NewDriver(filename string) Driver
type advisory interface {
DetectVulnerabilities(string, *version.Version) ([]types.DetectedVulnerability, error)
}
type DriverFactory struct{}
func (d DriverFactory) NewDriver(filename string) Driver {
func (d DriverFactory) NewDriver(filename string) (Driver, error) {
// TODO: use DI
var scanner Driver
var driver Driver
switch filename {
case "Gemfile.lock":
scanner = bundler.NewScanner()
driver = NewBundlerDriver()
case "Cargo.lock":
scanner = cargo.NewScanner()
driver = NewCargoDriver()
case "composer.lock":
scanner = composer.NewScanner()
driver = NewComposerDriver()
case "package-lock.json":
scanner = node.NewScanner(node.ScannerTypeNpm)
driver = NewNpmDriver()
case "yarn.lock":
scanner = node.NewScanner(node.ScannerTypeYarn)
driver = NewYarnDriver()
case "Pipfile.lock":
scanner = python.NewScanner(python.ScannerTypePipenv)
driver = NewPipenvDriver()
case "poetry.lock":
scanner = python.NewScanner(python.ScannerTypePoetry)
driver = NewPoetryDriver()
default:
return nil
return Driver{}, xerrors.New(fmt.Sprintf("unsupport filename %s", filename))
}
return scanner
return driver, nil
}
type Driver struct {
pkgManager string
advisories []advisory
}
func NewDriver(p string, advisories ...advisory) Driver {
return Driver{pkgManager: p, advisories: advisories}
}
func (driver *Driver) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
var detectedVulnerabilities []types.DetectedVulnerability
uniqVulnIdMap := make(map[string]struct{})
for _, d := range driver.advisories {
vulns, err := d.DetectVulnerabilities(pkgName, pkgVer)
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
for _, vuln := range vulns {
if _, ok := uniqVulnIdMap[vuln.VulnerabilityID]; ok {
continue
}
uniqVulnIdMap[vuln.VulnerabilityID] = struct{}{}
detectedVulnerabilities = append(detectedVulnerabilities, vuln)
}
}
return detectedVulnerabilities, nil
}
func NewBundlerDriver() Driver {
return NewDriver(library.Bundler, ghsa.NewAdvisory(ecosystem.Rubygems), bundler.NewAdvisory())
}
func NewComposerDriver() Driver {
return NewDriver(library.Composer, ghsa.NewAdvisory(ecosystem.Composer), composer.NewAdvisory())
}
func NewCargoDriver() Driver {
return NewDriver(library.Cargo, cargo.NewAdvisory())
}
func NewNpmDriver() Driver {
return NewDriver(library.Npm, ghsa.NewAdvisory(ecosystem.Npm), node.NewAdvisory())
}
func NewYarnDriver() Driver {
return NewDriver(library.Yarn, ghsa.NewAdvisory(ecosystem.Npm), node.NewAdvisory())
}
func NewPipenvDriver() Driver {
return NewDriver(library.Pipenv, ghsa.NewAdvisory(ecosystem.Pip), python.NewAdvisory())
}
func NewPoetryDriver() Driver {
return NewDriver(library.Poetry, ghsa.NewAdvisory(ecosystem.Pip), python.NewAdvisory())
}
func (d *Driver) Type() string {
return d.pkgManager
}

View File

@@ -0,0 +1,50 @@
package ghsa
import (
"strings"
"github.com/knqyf263/go-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
type VulnSrc interface {
Get(pkgName string) ([]ghsa.Advisory, error)
}
type Advisory struct {
vs VulnSrc
}
func NewAdvisory(ecosystem ghsa.Ecosystem) *Advisory {
return &Advisory{
vs: ghsa.NewVulnSrc(ecosystem),
}
}
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get ghsa advisories: %w", err)
}
var vulns []types.DetectedVulnerability
for _, advisory := range advisories {
if !utils.MatchVersions(pkgVer, advisory.VulnerableVersions) {
continue
}
vuln := types.DetectedVulnerability{
VulnerabilityID: advisory.VulnerabilityID,
PkgName: pkgName,
InstalledVersion: pkgVer.String(),
FixedVersion: strings.Join(advisory.PatchedVersions, ", "),
}
vulns = append(vulns, vuln)
}
return vulns, nil
}

View File

@@ -1,42 +1,31 @@
package node
import (
"os"
"strings"
version "github.com/knqyf263/go-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/npm"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/go-dep-parser/pkg/yarn"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/node"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
const (
ScannerTypeNpm = "npm"
ScannerTypeYarn = "yarn"
)
type Scanner struct {
scannerType string
vs node.VulnSrc
type Advisory struct {
vs node.VulnSrc
}
func NewScanner(scannerType string) *Scanner {
return &Scanner{
scannerType: scannerType,
vs: node.NewVulnSrc(),
func NewAdvisory() *Advisory {
return &Advisory{
vs: node.NewVulnSrc(),
}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
replacer := strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc", " <", ", <", " >", ", >")
advisories, err := s.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get %s advisories: %w", s.Type(), err)
return nil, xerrors.Errorf("failed to get node advisories: %w", err)
}
var vulns []types.DetectedVulnerability
@@ -73,30 +62,3 @@ func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Detec
}
return vulns, nil
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
if s.Type() == ScannerTypeNpm {
return s.parseNpm(f)
}
return s.parseYarn(f)
}
func (s *Scanner) parseNpm(f *os.File) ([]ptypes.Library, error) {
libs, err := npm.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid package-lock.json format: %w", err)
}
return libs, nil
}
func (s *Scanner) parseYarn(f *os.File) ([]ptypes.Library, error) {
libs, err := yarn.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid yarn.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) Type() string {
return s.scannerType
}

View File

@@ -0,0 +1,58 @@
package python
import (
"strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/python"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-version"
)
type Advisory struct {
vs python.VulnSrc
}
func NewAdvisory() *Advisory {
return &Advisory{
vs: python.NewVulnSrc(),
}
}
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get python advisories: %w", err)
}
var vulns []types.DetectedVulnerability
for _, advisory := range advisories {
if !utils.MatchVersions(pkgVer, advisory.Specs) {
continue
}
vuln := types.DetectedVulnerability{
VulnerabilityID: advisory.VulnerabilityID,
PkgName: pkgName,
InstalledVersion: pkgVer.String(),
FixedVersion: createFixedVersions(advisory.Specs),
}
vulns = append(vulns, vuln)
}
return vulns, nil
}
func createFixedVersions(specs []string) string {
var fixedVersions []string
for _, spec := range specs {
for _, s := range strings.Split(spec, ",") {
if !strings.HasPrefix(s, "<=") && strings.HasPrefix(s, "<") {
fixedVersions = append(fixedVersions, strings.TrimPrefix(s, "<"))
}
}
}
return strings.Join(fixedVersions, ", ")
}

View File

@@ -1,96 +0,0 @@
package python
import (
"os"
"strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/python"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/pipenv"
"github.com/aquasecurity/go-dep-parser/pkg/poetry"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/knqyf263/go-version"
)
const (
ScannerTypePipenv = "pipenv"
ScannerTypePoetry = "poetry"
)
type Scanner struct {
scannerType string
vs python.VulnSrc
}
func NewScanner(scannerType string) *Scanner {
return &Scanner{
scannerType: scannerType,
vs: python.NewVulnSrc(),
}
}
func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get %s advisories: %w", s.Type(), err)
}
var vulns []types.DetectedVulnerability
for _, advisory := range advisories {
if !utils.MatchVersions(pkgVer, advisory.Specs) {
continue
}
vuln := types.DetectedVulnerability{
VulnerabilityID: advisory.VulnerabilityID,
PkgName: pkgName,
InstalledVersion: pkgVer.String(),
FixedVersion: createFixedVersions(advisory.Specs),
}
vulns = append(vulns, vuln)
}
return vulns, nil
}
func createFixedVersions(specs []string) string {
var fixedVersions []string
for _, spec := range specs {
for _, s := range strings.Split(spec, ",") {
if !strings.HasPrefix(s, "<=") && strings.HasPrefix(s, "<") {
fixedVersions = append(fixedVersions, strings.TrimPrefix(s, "<"))
}
}
}
return strings.Join(fixedVersions, ", ")
}
func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) {
if s.Type() == ScannerTypePipenv {
return s.parsePipenv(f)
}
return s.parsePoetry(f)
}
func (s *Scanner) parsePipenv(f *os.File) ([]ptypes.Library, error) {
libs, err := pipenv.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid Pipfile.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) parsePoetry(f *os.File) ([]ptypes.Library, error) {
libs, err := poetry.Parse(f)
if err != nil {
return nil, xerrors.Errorf("invalid poetry.lock format: %w", err)
}
return libs, nil
}
func (s *Scanner) Type() string {
return s.scannerType
}

View File

@@ -45,9 +45,9 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
err := r.Retry(func() error {
var err error
res, err = s.client.Scan(ctx, &rpc.ScanRequest{
Target: target,
ImageId: imageID,
LayerIds: layerIDs,
Target: target,
ArtifactId: imageID,
BlobIds: layerIDs,
Options: &rpc.ScanOptions{
VulnType: options.VulnType,
},

View File

@@ -124,9 +124,9 @@ func TestScanner_Scan(t *testing.T) {
Args: scanArgs{
CtxAnything: true,
Request: &scanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Target: "alpine:3.11",
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &scanner.ScanOptions{
VulnType: []string{"os"},
},
@@ -211,9 +211,9 @@ func TestScanner_Scan(t *testing.T) {
Args: scanArgs{
CtxAnything: true,
Request: &scanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Target: "alpine:3.11",
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &scanner.ScanOptions{
VulnType: []string{"os"},
},

View File

@@ -188,28 +188,28 @@ func ConvertFromRpcApplications(rpcApps []*common.Application) []ftypes.Applicat
return apps
}
func ConvertFromRpcPutImageRequest(req *cache.PutImageRequest) ftypes.ImageInfo {
created, _ := ptypes.Timestamp(req.ImageInfo.Created)
return ftypes.ImageInfo{
SchemaVersion: int(req.ImageInfo.SchemaVersion),
Architecture: req.ImageInfo.Architecture,
func ConvertFromRpcPutArtifactRequest(req *cache.PutArtifactRequest) ftypes.ArtifactInfo {
created, _ := ptypes.Timestamp(req.ArtifactInfo.Created)
return ftypes.ArtifactInfo{
SchemaVersion: int(req.ArtifactInfo.SchemaVersion),
Architecture: req.ArtifactInfo.Architecture,
Created: created,
DockerVersion: req.ImageInfo.DockerVersion,
OS: req.ImageInfo.Os,
HistoryPackages: ConvertFromRpcPkgs(req.ImageInfo.HistoryPackages),
DockerVersion: req.ArtifactInfo.DockerVersion,
OS: req.ArtifactInfo.Os,
HistoryPackages: ConvertFromRpcPkgs(req.ArtifactInfo.HistoryPackages),
}
}
func ConvertFromRpcPutLayerRequest(req *cache.PutLayerRequest) ftypes.LayerInfo {
return ftypes.LayerInfo{
SchemaVersion: int(req.LayerInfo.SchemaVersion),
Digest: req.LayerInfo.Digest,
DiffID: req.LayerInfo.DiffId,
OS: ConvertFromRpcOS(req.LayerInfo.Os),
PackageInfos: ConvertFromRpcPackageInfos(req.LayerInfo.PackageInfos),
Applications: ConvertFromRpcApplications(req.LayerInfo.Applications),
OpaqueDirs: req.LayerInfo.OpaqueDirs,
WhiteoutFiles: req.LayerInfo.WhiteoutFiles,
func ConvertFromRpcPutBlobRequest(req *cache.PutBlobRequest) ftypes.BlobInfo {
return ftypes.BlobInfo{
SchemaVersion: int(req.BlobInfo.SchemaVersion),
Digest: req.BlobInfo.Digest,
DiffID: req.BlobInfo.DiffId,
OS: ConvertFromRpcOS(req.BlobInfo.Os),
PackageInfos: ConvertFromRpcPackageInfos(req.BlobInfo.PackageInfos),
Applications: ConvertFromRpcApplications(req.BlobInfo.Applications),
OpaqueDirs: req.BlobInfo.OpaqueDirs,
WhiteoutFiles: req.BlobInfo.WhiteoutFiles,
}
}
@@ -223,15 +223,15 @@ func ConvertToRpcOS(fos *ftypes.OS) *common.OS {
}
}
func ConvertToRpcImageInfo(imageID string, imageInfo ftypes.ImageInfo) *cache.PutImageRequest {
func ConvertToRpcArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *cache.PutArtifactRequest {
t, err := ptypes.TimestampProto(imageInfo.Created)
if err != nil {
log.Logger.Warnf("invalid timestamp: %s", err)
}
return &cache.PutImageRequest{
ImageId: imageID,
ImageInfo: &cache.ImageInfo{
return &cache.PutArtifactRequest{
ArtifactId: imageID,
ArtifactInfo: &cache.ArtifactInfo{
SchemaVersion: int32(imageInfo.SchemaVersion),
Architecture: imageInfo.Architecture,
Created: t,
@@ -242,7 +242,7 @@ func ConvertToRpcImageInfo(imageID string, imageInfo ftypes.ImageInfo) *cache.Pu
}
}
func ConvertToRpcLayerInfo(diffID string, layerInfo ftypes.LayerInfo) *cache.PutLayerRequest {
func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
var packageInfos []*common.PackageInfo
for _, pkgInfo := range layerInfo.PackageInfos {
packageInfos = append(packageInfos, &common.PackageInfo{
@@ -268,10 +268,10 @@ func ConvertToRpcLayerInfo(diffID string, layerInfo ftypes.LayerInfo) *cache.Put
})
}
return &cache.PutLayerRequest{
return &cache.PutBlobRequest{
DiffId: diffID,
LayerInfo: &cache.LayerInfo{
SchemaVersion: ftypes.LayerJSONSchemaVersion,
BlobInfo: &cache.BlobInfo{
SchemaVersion: ftypes.BlobJSONSchemaVersion,
Digest: layerInfo.Digest,
DiffId: layerInfo.DiffID,
Os: ConvertToRpcOS(layerInfo.OS),
@@ -283,10 +283,10 @@ func ConvertToRpcLayerInfo(diffID string, layerInfo ftypes.LayerInfo) *cache.Put
}
}
func ConvertToMissingLayersRequest(imageID string, layerIDs []string) *cache.MissingLayersRequest {
return &cache.MissingLayersRequest{
ImageId: imageID,
LayerIds: layerIDs,
func ConvertToMissingBlobsRequest(imageID string, layerIDs []string) *cache.MissingBlobsRequest {
return &cache.MissingBlobsRequest{
ArtifactId: imageID,
BlobIds: layerIDs,
}
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/google/wire"
)
func initializeScanServer(localLayerCache cache.LocalImageCache) *ScanServer {
func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServer {
wire.Build(ScanSuperSet)
return &ScanServer{}
}

View File

@@ -36,7 +36,7 @@ func NewScanServer(s scanner.Driver, vulnClient vulnerability.Operation) *ScanSe
func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
options := types.ScanOptions{VulnType: in.Options.VulnType}
results, os, eosl, err := s.localScanner.Scan(in.Target, in.ImageId, in.LayerIds, options)
results, os, eosl, err := s.localScanner.Scan(in.Target, in.ArtifactId, in.BlobIds, options)
if err != nil {
return nil, xerrors.Errorf("failed scan, %s: %w", in.Target, err)
}
@@ -55,40 +55,40 @@ func NewCacheServer(c cache.Cache) *CacheServer {
return &CacheServer{cache: c}
}
func (s *CacheServer) PutImage(_ context.Context, in *rpcCache.PutImageRequest) (*google_protobuf.Empty, error) {
if in.ImageInfo == nil {
func (s *CacheServer) PutArtifact(_ context.Context, in *rpcCache.PutArtifactRequest) (*google_protobuf.Empty, error) {
if in.ArtifactInfo == nil {
return nil, xerrors.Errorf("empty image info")
}
imageInfo := rpc.ConvertFromRpcPutImageRequest(in)
if err := s.cache.PutImage(in.ImageId, imageInfo); err != nil {
imageInfo := rpc.ConvertFromRpcPutArtifactRequest(in)
if err := s.cache.PutArtifact(in.ArtifactId, imageInfo); err != nil {
return nil, xerrors.Errorf("unable to store image info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
func (s *CacheServer) PutLayer(_ context.Context, in *rpcCache.PutLayerRequest) (*google_protobuf.Empty, error) {
if in.LayerInfo == nil {
func (s *CacheServer) PutBlob(_ context.Context, in *rpcCache.PutBlobRequest) (*google_protobuf.Empty, error) {
if in.BlobInfo == nil {
return nil, xerrors.Errorf("empty layer info")
}
layerInfo := rpc.ConvertFromRpcPutLayerRequest(in)
if err := s.cache.PutLayer(in.DiffId, layerInfo); err != nil {
layerInfo := rpc.ConvertFromRpcPutBlobRequest(in)
if err := s.cache.PutBlob(in.DiffId, layerInfo); err != nil {
return nil, xerrors.Errorf("unable to store layer info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
func (s *CacheServer) MissingLayers(_ context.Context, in *rpcCache.MissingLayersRequest) (*rpcCache.MissingLayersResponse, error) {
func (s *CacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBlobsRequest) (*rpcCache.MissingBlobsResponse, error) {
var layerIDs []string
for _, layerID := range in.LayerIds {
l, err := s.cache.GetLayer(layerID)
if err != nil || l.SchemaVersion != ftypes.LayerJSONSchemaVersion {
layerIDs = append(layerIDs, layerID)
for _, blobID := range in.BlobIds {
l, err := s.cache.GetBlob(blobID)
if err != nil || l.SchemaVersion != ftypes.BlobJSONSchemaVersion {
layerIDs = append(layerIDs, blobID)
}
}
var missingImage bool
img, err := s.cache.GetImage(in.ImageId)
if err != nil || img.SchemaVersion != ftypes.ImageJSONSchemaVersion {
img, err := s.cache.GetArtifact(in.ArtifactId)
if err != nil || img.SchemaVersion != ftypes.ArtifactJSONSchemaVersion {
missingImage = true
}
return &rpcCache.MissingLayersResponse{MissingImage: missingImage, MissingLayerIds: layerIDs}, nil
return &rpcCache.MissingBlobsResponse{MissingArtifact: missingImage, MissingBlobIds: layerIDs}, nil
}

View File

@@ -6,19 +6,16 @@ import (
"testing"
"time"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/golang/protobuf/ptypes"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
ftypes "github.com/aquasecurity/fanal/types"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
@@ -30,8 +27,8 @@ import (
)
type mockCache struct {
cache.MockImageCache
cache.MockLocalImageCache
cache.MockArtifactCache
cache.MockLocalArtifactCache
}
func TestScanServer_Scan(t *testing.T) {
@@ -41,7 +38,7 @@ func TestScanServer_Scan(t *testing.T) {
tests := []struct {
name string
args args
scanExpectation scanner.ScanExpectation
scanExpectation scanner.DriverScanExpectation
fillInfoExpectation vulnerability.FillInfoExpectation
want *rpcScanner.ScanResponse
wantErr string
@@ -50,19 +47,19 @@ func TestScanServer_Scan(t *testing.T) {
name: "happy path",
args: args{
in: &rpcScanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
Target: "alpine:3.11",
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
},
},
scanExpectation: scanner.ScanExpectation{
Args: scanner.ScanArgs{
scanExpectation: scanner.DriverScanExpectation{
Args: scanner.DriverScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: scanner.ScanReturns{
Returns: scanner.DriverScanReturns{
Results: report.Results{
{
Target: "alpine:3.11 (alpine 3.11)",
@@ -128,19 +125,19 @@ func TestScanServer_Scan(t *testing.T) {
name: "sad path: Scan returns an error",
args: args{
in: &rpcScanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
Target: "alpine:3.11",
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
},
},
scanExpectation: scanner.ScanExpectation{
Args: scanner.ScanArgs{
scanExpectation: scanner.DriverScanExpectation{
Args: scanner.DriverScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: scanner.ScanReturns{
Returns: scanner.DriverScanReturns{
Err: errors.New("error"),
},
},
@@ -170,23 +167,23 @@ func TestScanServer_Scan(t *testing.T) {
}
}
func TestCacheServer_PutImage(t *testing.T) {
func TestCacheServer_PutArtifact(t *testing.T) {
type args struct {
in *rpcCache.PutImageRequest
in *rpcCache.PutArtifactRequest
}
tests := []struct {
name string
args args
putImage cache.ImageCachePutImageExpectation
putImage cache.ArtifactCachePutArtifactExpectation
want *google_protobuf.Empty
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.PutImageRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: &rpcCache.ImageInfo{
in: &rpcCache.PutArtifactRequest{
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ArtifactInfo: &rpcCache.ArtifactInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: func() *timestamp.Timestamp {
@@ -199,10 +196,10 @@ func TestCacheServer_PutImage(t *testing.T) {
},
},
},
putImage: cache.ImageCachePutImageExpectation{
Args: cache.ImageCachePutImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: ftypes.ImageInfo{
putImage: cache.ArtifactCachePutArtifactExpectation{
Args: cache.ArtifactCachePutArtifactArgs{
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ArtifactInfo: ftypes.ArtifactInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
@@ -216,9 +213,9 @@ func TestCacheServer_PutImage(t *testing.T) {
{
name: "sad path",
args: args{
in: &rpcCache.PutImageRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: &rpcCache.ImageInfo{
in: &rpcCache.PutArtifactRequest{
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ArtifactInfo: &rpcCache.ArtifactInfo{
SchemaVersion: 1,
Created: func() *timestamp.Timestamp {
d := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC)
@@ -228,15 +225,15 @@ func TestCacheServer_PutImage(t *testing.T) {
},
},
},
putImage: cache.ImageCachePutImageExpectation{
Args: cache.ImageCachePutImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: ftypes.ImageInfo{
putImage: cache.ArtifactCachePutArtifactExpectation{
Args: cache.ArtifactCachePutArtifactArgs{
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ArtifactInfo: ftypes.ArtifactInfo{
SchemaVersion: 1,
Created: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
},
},
Returns: cache.ImageCachePutImageReturns{
Returns: cache.ArtifactCachePutArtifactReturns{
Err: xerrors.New("error"),
},
},
@@ -245,7 +242,7 @@ func TestCacheServer_PutImage(t *testing.T) {
{
name: "sad path: empty image info",
args: args{
in: &rpcCache.PutImageRequest{},
in: &rpcCache.PutArtifactRequest{},
},
wantErr: "empty image info",
},
@@ -253,10 +250,10 @@ func TestCacheServer_PutImage(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCache := new(mockCache)
mockCache.ApplyPutImageExpectation(tt.putImage)
mockCache.ApplyPutArtifactExpectation(tt.putImage)
s := NewCacheServer(mockCache)
got, err := s.PutImage(context.Background(), tt.args.in)
got, err := s.PutArtifact(context.Background(), tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
@@ -271,23 +268,23 @@ func TestCacheServer_PutImage(t *testing.T) {
}
}
func TestCacheServer_PutLayer(t *testing.T) {
func TestCacheServer_PutBlob(t *testing.T) {
type args struct {
in *rpcCache.PutLayerRequest
in *rpcCache.PutBlobRequest
}
tests := []struct {
name string
args args
putLayer cache.ImageCachePutLayerExpectation
putLayer cache.ArtifactCachePutBlobExpectation
want *google_protobuf.Empty
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.PutLayerRequest{
in: &rpcCache.PutBlobRequest{
DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
LayerInfo: &rpcCache.LayerInfo{
BlobInfo: &rpcCache.BlobInfo{
SchemaVersion: 1,
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
@@ -345,10 +342,10 @@ func TestCacheServer_PutLayer(t *testing.T) {
},
},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
LayerInfo: ftypes.LayerInfo{
putLayer: cache.ArtifactCachePutBlobExpectation{
Args: cache.ArtifactCachePutBlobArgs{
BlobID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
BlobInfo: ftypes.BlobInfo{
SchemaVersion: 1,
Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
@@ -415,18 +412,18 @@ func TestCacheServer_PutLayer(t *testing.T) {
{
name: "sad path",
args: args{
in: &rpcCache.PutLayerRequest{
LayerInfo: &rpcCache.LayerInfo{
in: &rpcCache.PutBlobRequest{
BlobInfo: &rpcCache.BlobInfo{
SchemaVersion: 1,
},
},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
DiffIDAnything: true,
LayerInfoAnything: true,
putLayer: cache.ArtifactCachePutBlobExpectation{
Args: cache.ArtifactCachePutBlobArgs{
BlobIDAnything: true,
BlobInfoAnything: true,
},
Returns: cache.ImageCachePutLayerReturns{
Returns: cache.ArtifactCachePutBlobReturns{
Err: xerrors.New("error"),
},
},
@@ -435,14 +432,14 @@ func TestCacheServer_PutLayer(t *testing.T) {
{
name: "sad path: empty layer info",
args: args{
in: &rpcCache.PutLayerRequest{},
in: &rpcCache.PutBlobRequest{},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
DiffIDAnything: true,
LayerInfoAnything: true,
putLayer: cache.ArtifactCachePutBlobExpectation{
Args: cache.ArtifactCachePutBlobArgs{
BlobIDAnything: true,
BlobInfoAnything: true,
},
Returns: cache.ImageCachePutLayerReturns{
Returns: cache.ArtifactCachePutBlobReturns{
Err: xerrors.New("error"),
},
},
@@ -452,10 +449,10 @@ func TestCacheServer_PutLayer(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCache := new(mockCache)
mockCache.ApplyPutLayerExpectation(tt.putLayer)
mockCache.ApplyPutBlobExpectation(tt.putLayer)
s := NewCacheServer(mockCache)
got, err := s.PutLayer(context.Background(), tt.args.in)
got, err := s.PutBlob(context.Background(), tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
@@ -470,113 +467,113 @@ func TestCacheServer_PutLayer(t *testing.T) {
}
}
func TestCacheServer_MissingLayers(t *testing.T) {
func TestCacheServer_MissingBlobs(t *testing.T) {
type args struct {
ctx context.Context
in *rpcCache.MissingLayersRequest
in *rpcCache.MissingBlobsRequest
}
tests := []struct {
name string
args args
getLayerExpectations []cache.LocalImageCacheGetLayerExpectation
getImageExpectations []cache.LocalImageCacheGetImageExpectation
want *rpcCache.MissingLayersResponse
getLayerExpectations []cache.LocalArtifactCacheGetBlobExpectation
getImageExpectations []cache.LocalArtifactCacheGetArtifactExpectation
want *rpcCache.MissingBlobsResponse
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.MissingLayersRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{
in: &rpcCache.MissingBlobsRequest{
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
},
},
getLayerExpectations: []cache.LocalImageCacheGetLayerExpectation{
getLayerExpectations: []cache.LocalArtifactCacheGetBlobExpectation{
{
Args: cache.LocalImageCacheGetLayerArgs{
DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
Args: cache.LocalArtifactCacheGetBlobArgs{
BlobID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{},
Returns: cache.LocalArtifactCacheGetBlobReturns{
BlobInfo: ftypes.BlobInfo{},
},
},
{
Args: cache.LocalImageCacheGetLayerArgs{
DiffID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
Args: cache.LocalArtifactCacheGetBlobArgs{
BlobID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
Returns: cache.LocalArtifactCacheGetBlobReturns{
BlobInfo: ftypes.BlobInfo{
SchemaVersion: 1,
},
},
},
},
getImageExpectations: []cache.LocalImageCacheGetImageExpectation{
getImageExpectations: []cache.LocalArtifactCacheGetArtifactExpectation{
{
Args: cache.LocalImageCacheGetImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
Args: cache.LocalArtifactCacheGetArtifactArgs{
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
},
Returns: cache.LocalImageCacheGetImageReturns{
ImageInfo: ftypes.ImageInfo{
Returns: cache.LocalArtifactCacheGetArtifactReturns{
ArtifactInfo: ftypes.ArtifactInfo{
SchemaVersion: 1,
},
},
},
},
want: &rpcCache.MissingLayersResponse{
MissingImage: false,
MissingLayerIds: []string{"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02"},
want: &rpcCache.MissingBlobsResponse{
MissingArtifact: false,
MissingBlobIds: []string{"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02"},
},
},
{
name: "schema version doesn't match",
args: args{
in: &rpcCache.MissingLayersRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{
in: &rpcCache.MissingBlobsRequest{
ArtifactId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
},
},
getLayerExpectations: []cache.LocalImageCacheGetLayerExpectation{
getLayerExpectations: []cache.LocalArtifactCacheGetBlobExpectation{
{
Args: cache.LocalImageCacheGetLayerArgs{
DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
Args: cache.LocalArtifactCacheGetBlobArgs{
BlobID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
Returns: cache.LocalArtifactCacheGetBlobReturns{
BlobInfo: ftypes.BlobInfo{
SchemaVersion: 0,
},
},
},
{
Args: cache.LocalImageCacheGetLayerArgs{
DiffID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
Args: cache.LocalArtifactCacheGetBlobArgs{
BlobID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
Returns: cache.LocalArtifactCacheGetBlobReturns{
BlobInfo: ftypes.BlobInfo{
SchemaVersion: -1,
},
},
},
},
getImageExpectations: []cache.LocalImageCacheGetImageExpectation{
getImageExpectations: []cache.LocalArtifactCacheGetArtifactExpectation{
{
Args: cache.LocalImageCacheGetImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
Args: cache.LocalArtifactCacheGetArtifactArgs{
ArtifactID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
},
Returns: cache.LocalImageCacheGetImageReturns{
ImageInfo: ftypes.ImageInfo{},
Returns: cache.LocalArtifactCacheGetArtifactReturns{
ArtifactInfo: ftypes.ArtifactInfo{},
},
},
},
want: &rpcCache.MissingLayersResponse{
MissingImage: true,
MissingLayerIds: []string{
want: &rpcCache.MissingBlobsResponse{
MissingArtifact: true,
MissingBlobIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
@@ -586,11 +583,11 @@ func TestCacheServer_MissingLayers(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCache := new(mockCache)
mockCache.ApplyGetLayerExpectations(tt.getLayerExpectations)
mockCache.ApplyGetImageExpectations(tt.getImageExpectations)
mockCache.ApplyGetBlobExpectations(tt.getLayerExpectations)
mockCache.ApplyGetArtifactExpectations(tt.getImageExpectations)
s := NewCacheServer(mockCache)
got, err := s.MissingLayers(tt.args.ctx, tt.args.in)
got, err := s.MissingBlobs(tt.args.ctx, tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
@@ -600,7 +597,7 @@ func TestCacheServer_MissingLayers(t *testing.T) {
}
assert.Equal(t, tt.want, got)
mockCache.MockLocalImageCache.AssertExpectations(t)
mockCache.MockLocalArtifactCache.AssertExpectations(t)
})
}
}

View File

@@ -6,7 +6,7 @@
package server
import (
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/applier"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
db2 "github.com/aquasecurity/trivy/pkg/db"
@@ -24,12 +24,12 @@ import (
// Injectors from inject.go:
func initializeScanServer(localLayerCache cache.LocalImageCache) *ScanServer {
applier := analyzer.NewApplier(localLayerCache)
func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServer {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
scanner := local.NewScanner(applier, detector, libraryDetector)
scanner := local.NewScanner(applierApplier, detector, libraryDetector)
config := db.Config{}
client := vulnerability.NewClient(config)
scanServer := NewScanServer(scanner, client)

View File

@@ -2,8 +2,10 @@
package local
import mock "github.com/stretchr/testify/mock"
import types "github.com/aquasecurity/fanal/types"
import (
types "github.com/aquasecurity/fanal/types"
mock "github.com/stretchr/testify/mock"
)
// MockApplier is an autogenerated mock type for the Applier type
type MockApplier struct {
@@ -11,14 +13,14 @@ type MockApplier struct {
}
type ApplierApplyLayersArgs struct {
ImageID string
ImageIDAnything bool
LayerIDs []string
LayerIDsAnything bool
ArtifactID string
ArtifactIDAnything bool
BlobIDs []string
BlobIDsAnything bool
}
type ApplierApplyLayersReturns struct {
Detail types.ImageDetail
Detail types.ArtifactDetail
Err error
}
@@ -29,15 +31,15 @@ type ApplierApplyLayersExpectation struct {
func (_m *MockApplier) ApplyApplyLayersExpectation(e ApplierApplyLayersExpectation) {
var args []interface{}
if e.Args.ImageIDAnything {
if e.Args.ArtifactIDAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ImageID)
args = append(args, e.Args.ArtifactID)
}
if e.Args.LayerIDsAnything {
if e.Args.BlobIDsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.LayerIDs)
args = append(args, e.Args.BlobIDs)
}
_m.On("ApplyLayers", args...).Return(e.Returns.Detail, e.Returns.Err)
}
@@ -48,20 +50,20 @@ func (_m *MockApplier) ApplyApplyLayersExpectations(expectations []ApplierApplyL
}
}
// ApplyLayers provides a mock function with given fields: imageID, layerIDs
func (_m *MockApplier) ApplyLayers(imageID string, layerIDs []string) (types.ImageDetail, error) {
ret := _m.Called(imageID, layerIDs)
// ApplyLayers provides a mock function with given fields: artifactID, blobIDs
func (_m *MockApplier) ApplyLayers(artifactID string, blobIDs []string) (types.ArtifactDetail, error) {
ret := _m.Called(artifactID, blobIDs)
var r0 types.ImageDetail
if rf, ok := ret.Get(0).(func(string, []string) types.ImageDetail); ok {
r0 = rf(imageID, layerIDs)
var r0 types.ArtifactDetail
if rf, ok := ret.Get(0).(func(string, []string) types.ArtifactDetail); ok {
r0 = rf(artifactID, blobIDs)
} else {
r0 = ret.Get(0).(types.ImageDetail)
r0 = ret.Get(0).(types.ArtifactDetail)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
r1 = rf(imageID, layerIDs)
r1 = rf(artifactID, blobIDs)
} else {
r1 = ret.Error(1)
}

View File

@@ -19,13 +19,15 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
_ "github.com/aquasecurity/fanal/analyzer/os/debian"
_ "github.com/aquasecurity/fanal/analyzer/os/photon"
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
_ "github.com/aquasecurity/fanal/analyzer/os/ubuntu"
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd"
"github.com/aquasecurity/fanal/applier"
ftypes "github.com/aquasecurity/fanal/types"
libDetector "github.com/aquasecurity/trivy/pkg/detector/library"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
@@ -36,8 +38,8 @@ import (
)
var SuperSet = wire.NewSet(
analyzer.NewApplier,
wire.Bind(new(Applier), new(analyzer.Applier)),
applier.NewApplier,
wire.Bind(new(Applier), new(applier.Applier)),
ospkgDetector.SuperSet,
wire.Bind(new(OspkgDetector), new(ospkgDetector.Detector)),
libDetector.SuperSet,
@@ -46,7 +48,7 @@ var SuperSet = wire.NewSet(
)
type Applier interface {
ApplyLayers(imageID string, layerIDs []string) (detail ftypes.ImageDetail, err error)
ApplyLayers(artifactID string, blobIDs []string) (detail ftypes.ArtifactDetail, err error)
}
type OspkgDetector interface {
@@ -72,7 +74,7 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
if err != nil {
switch err {
case analyzer.ErrUnknownOS:
log.Logger.Warn("This OS is not supported and vulnerabilities in OS packages are not detected.")
log.Logger.Warn("OS is not detected and vulnerabilities in OS packages are not detected.")
case analyzer.ErrNoPkgsDetected:
log.Logger.Warn("No OS package is detected. Make sure you haven't deleted any files that contain information about the installed packages.")
log.Logger.Warn(`e.g. files under "/lib/apk/db/", "/var/lib/dpkg/" and "/var/lib/rpm"`)

View File

@@ -43,10 +43,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
@@ -182,10 +182,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{},
Applications: []ftypes.Application{
{
@@ -260,10 +260,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
@@ -360,10 +360,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "fedora",
Name: "27",
@@ -455,10 +455,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
BlobIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: nil,
},
Err: analyzer.ErrUnknownOS,
@@ -476,10 +476,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
@@ -616,7 +616,7 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Err: errors.New("error"),
@@ -633,10 +633,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
@@ -684,10 +684,10 @@ func TestScanner_Scan(t *testing.T) {
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
Detail: ftypes.ArtifactDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",

View File

@@ -1,64 +0,0 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package scanner
import context "context"
import mock "github.com/stretchr/testify/mock"
import types "github.com/aquasecurity/fanal/types"
// MockAnalyzer is an autogenerated mock type for the Analyzer type
type MockAnalyzer struct {
mock.Mock
}
type AnalyzerAnalyzeArgs struct {
Ctx context.Context
CtxAnything bool
}
type AnalyzerAnalyzeReturns struct {
Info types.ImageReference
Err error
}
type AnalyzerAnalyzeExpectation struct {
Args AnalyzerAnalyzeArgs
Returns AnalyzerAnalyzeReturns
}
func (_m *MockAnalyzer) ApplyAnalyzeExpectation(e AnalyzerAnalyzeExpectation) {
var args []interface{}
if e.Args.CtxAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Ctx)
}
_m.On("Analyze", args...).Return(e.Returns.Info, e.Returns.Err)
}
func (_m *MockAnalyzer) ApplyAnalyzeExpectations(expectations []AnalyzerAnalyzeExpectation) {
for _, e := range expectations {
_m.ApplyAnalyzeExpectation(e)
}
}
// Analyze provides a mock function with given fields: ctx
func (_m *MockAnalyzer) Analyze(ctx context.Context) (types.ImageReference, error) {
ret := _m.Called(ctx)
var r0 types.ImageReference
if rf, ok := ret.Get(0).(func(context.Context) types.ImageReference); ok {
r0 = rf(ctx)
} else {
r0 = ret.Get(0).(types.ImageReference)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@@ -2,17 +2,21 @@
package scanner
import fanaltypes "github.com/aquasecurity/fanal/types"
import mock "github.com/stretchr/testify/mock"
import report "github.com/aquasecurity/trivy/pkg/report"
import types "github.com/aquasecurity/trivy/pkg/types"
import (
fanaltypes "github.com/aquasecurity/fanal/types"
mock "github.com/stretchr/testify/mock"
report "github.com/aquasecurity/trivy/pkg/report"
types "github.com/aquasecurity/trivy/pkg/types"
)
// MockDriver is an autogenerated mock type for the Driver type
type MockDriver struct {
mock.Mock
}
type ScanArgs struct {
type DriverScanArgs struct {
Target string
TargetAnything bool
ImageID string
@@ -23,19 +27,19 @@ type ScanArgs struct {
OptionsAnything bool
}
type ScanReturns struct {
type DriverScanReturns struct {
Results report.Results
OsFound *fanaltypes.OS
Eols bool
Err error
}
type ScanExpectation struct {
Args ScanArgs
Returns ScanReturns
type DriverScanExpectation struct {
Args DriverScanArgs
Returns DriverScanReturns
}
func (_m *MockDriver) ApplyScanExpectation(e ScanExpectation) {
func (_m *MockDriver) ApplyScanExpectation(e DriverScanExpectation) {
var args []interface{}
if e.Args.TargetAnything {
args = append(args, mock.Anything)
@@ -60,7 +64,7 @@ func (_m *MockDriver) ApplyScanExpectation(e ScanExpectation) {
_m.On("Scan", args...).Return(e.Returns.Results, e.Returns.OsFound, e.Returns.Eols, e.Returns.Err)
}
func (_m *MockDriver) ApplyScanExpectations(expectations []ScanExpectation) {
func (_m *MockDriver) ApplyScanExpectations(expectations []DriverScanExpectation) {
for _, e := range expectations {
_m.ApplyScanExpectation(e)
}

View File

@@ -6,9 +6,11 @@ import (
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/fanal/artifact"
aimage "github.com/aquasecurity/fanal/artifact/image"
flocal "github.com/aquasecurity/fanal/artifact/local"
"github.com/aquasecurity/fanal/artifact/remote"
"github.com/aquasecurity/fanal/image"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
@@ -19,8 +21,6 @@ import (
// StandaloneSuperSet is used in the standalone mode
var StandaloneSuperSet = wire.NewSet(
analyzer.New,
wire.Bind(new(Analyzer), new(analyzer.Config)),
local.SuperSet,
wire.Bind(new(Driver), new(local.Scanner)),
NewScanner,
@@ -28,21 +28,30 @@ var StandaloneSuperSet = wire.NewSet(
var StandaloneDockerSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
image.NewDockerImage,
aimage.NewArtifact,
StandaloneSuperSet,
)
var StandaloneArchiveSet = wire.NewSet(
docker.NewArchiveImageExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
image.NewArchiveImage,
aimage.NewArtifact,
StandaloneSuperSet,
)
var StandaloneFilesystemSet = wire.NewSet(
flocal.NewArtifact,
StandaloneSuperSet,
)
var StandaloneRepositorySet = wire.NewSet(
remote.NewArtifact,
StandaloneSuperSet,
)
// RemoteSuperSet is used in the client mode
var RemoteSuperSet = wire.NewSet(
analyzer.New,
wire.Bind(new(Analyzer), new(analyzer.Config)),
aimage.NewArtifact,
client.SuperSet,
wire.Bind(new(Driver), new(client.Scanner)),
NewScanner,
@@ -50,45 +59,38 @@ var RemoteSuperSet = wire.NewSet(
var RemoteDockerSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
image.NewDockerImage,
RemoteSuperSet,
)
var RemoteArchiveSet = wire.NewSet(
docker.NewArchiveImageExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
image.NewArchiveImage,
RemoteSuperSet,
)
type Scanner struct {
driver Driver
analyzer Analyzer
artifact artifact.Artifact
}
type Driver interface {
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (results report.Results, osFound *ftypes.OS, eols bool, err error)
}
type Analyzer interface {
Analyze(ctx context.Context) (info ftypes.ImageReference, err error)
func NewScanner(driver Driver, ar artifact.Artifact) Scanner {
return Scanner{driver: driver, artifact: ar}
}
func NewScanner(driver Driver, ac Analyzer) Scanner {
return Scanner{driver: driver, analyzer: ac}
}
func (s Scanner) ScanImage(options types.ScanOptions) (report.Results, error) {
ctx := context.Background()
imageInfo, err := s.analyzer.Analyze(ctx)
func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (report.Results, error) {
artifactInfo, err := s.artifact.Inspect(ctx)
if err != nil {
return nil, xerrors.Errorf("failed analysis: %w", err)
}
log.Logger.Debugf("Image ID: %s", imageInfo.ID)
log.Logger.Debugf("Layer IDs: %v", imageInfo.LayerIDs)
log.Logger.Debugf("Artifact ID: %s", artifactInfo.ID)
log.Logger.Debugf("Blob IDs: %v", artifactInfo.BlobIDs)
results, osFound, eosl, err := s.driver.Scan(imageInfo.Name, imageInfo.ID, imageInfo.LayerIDs, options)
results, osFound, eosl, err := s.driver.Scan(artifactInfo.Name, artifactInfo.ID, artifactInfo.BlobIDs, options)
if err != nil {
return nil, xerrors.Errorf("scan failed: %w", err)
}

View File

@@ -1,6 +1,7 @@
package scanner
import (
"context"
"errors"
"os"
"testing"
@@ -8,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/fanal/artifact"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
@@ -20,15 +22,15 @@ func TestMain(m *testing.M) {
os.Exit(code)
}
func TestScanner_ScanImage(t *testing.T) {
func TestScanner_ScanArtifact(t *testing.T) {
type args struct {
options types.ScanOptions
}
tests := []struct {
name string
args args
analyzeExpectation AnalyzerAnalyzeExpectation
scanExpectation ScanExpectation
inspectExpectation artifact.ArtifactInspectExpectation
scanExpectation DriverScanExpectation
wantResults report.Results
wantErr string
}{
@@ -37,26 +39,26 @@ func TestScanner_ScanImage(t *testing.T) {
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
inspectExpectation: artifact.ArtifactInspectExpectation{
Args: artifact.ArtifactInspectArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Info: ftypes.ImageReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Returns: artifact.ArtifactInspectReturns{
Reference: ftypes.ArtifactReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
},
},
scanExpectation: ScanExpectation{
Args: ScanArgs{
scanExpectation: DriverScanExpectation{
Args: DriverScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: types.ScanOptions{VulnType: []string{"os"}},
},
Returns: ScanReturns{
Returns: DriverScanReturns{
Results: report.Results{
{
Target: "alpine:3.11",
@@ -128,11 +130,11 @@ func TestScanner_ScanImage(t *testing.T) {
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
inspectExpectation: artifact.ArtifactInspectExpectation{
Args: artifact.ArtifactInspectArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Returns: artifact.ArtifactInspectReturns{
Err: errors.New("error"),
},
},
@@ -143,26 +145,26 @@ func TestScanner_ScanImage(t *testing.T) {
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
inspectExpectation: artifact.ArtifactInspectExpectation{
Args: artifact.ArtifactInspectArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Info: ftypes.ImageReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Returns: artifact.ArtifactInspectReturns{
Reference: ftypes.ArtifactReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
},
},
scanExpectation: ScanExpectation{
Args: ScanArgs{
scanExpectation: DriverScanExpectation{
Args: DriverScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: types.ScanOptions{VulnType: []string{"os"}},
},
Returns: ScanReturns{
Returns: DriverScanReturns{
Err: errors.New("error"),
},
},
@@ -174,11 +176,11 @@ func TestScanner_ScanImage(t *testing.T) {
d := new(MockDriver)
d.ApplyScanExpectation(tt.scanExpectation)
analyzer := new(MockAnalyzer)
analyzer.ApplyAnalyzeExpectation(tt.analyzeExpectation)
mockArtifact := new(artifact.MockArtifact)
mockArtifact.ApplyInspectExpectation(tt.inspectExpectation)
s := NewScanner(d, analyzer)
gotResults, err := s.ScanImage(tt.args.options)
s := NewScanner(d, mockArtifact)
gotResults, err := s.ScanArtifact(context.Background(), tt.args.options)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
require.Contains(t, err.Error(), tt.wantErr, tt.name)

View File

@@ -3,26 +3,30 @@ package types
import (
"time"
"github.com/aquasecurity/fanal/types"
"github.com/caarlos0/env/v6"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/types"
)
type DockerConfig struct {
UserName string `env:"TRIVY_USERNAME"`
Password string `env:"TRIVY_PASSWORD"`
Insecure bool `env:"TRIVY_INSECURE" envDefault:"false"`
NonSSL bool `env:"TRIVY_NON_SSL" envDefault:"false"`
UserName string `env:"TRIVY_USERNAME"`
Password string `env:"TRIVY_PASSWORD"`
RegistryToken string `env:"TRIVY_REGISTRY_TOKEN"`
Insecure bool `env:"TRIVY_INSECURE" envDefault:"false"`
NonSSL bool `env:"TRIVY_NON_SSL" envDefault:"false"`
}
func GetDockerOption(timeout time.Duration) (types.DockerOption, error) {
cfg := DockerConfig{}
if err := env.Parse(&cfg); err != nil {
return types.DockerOption{}, err
return types.DockerOption{}, xerrors.Errorf("unable to parse environment variables: %w", err)
}
return types.DockerOption{
UserName: cfg.UserName,
Password: cfg.Password,
RegistryToken: cfg.RegistryToken,
Timeout: timeout,
InsecureSkipTLSVerify: cfg.Insecure,
NonSSL: cfg.NonSSL,

View File

@@ -60,7 +60,7 @@ func TestClient_FillInfo(t *testing.T) {
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
},
reportType: vulnerability.Ubuntu,
reportType: vulnerability.RedHat,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
@@ -146,7 +146,7 @@ func TestClient_FillInfo(t *testing.T) {
},
},
{
name: "happy path, with only OS vulnerability, yes vendor severity",
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and vendor vectors",
getVulnerability: []db.GetVulnerabilityExpectation{
{
Args: db.GetVulnerabilityArgs{
@@ -160,6 +160,16 @@ func TestClient_FillInfo(t *testing.T) {
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.RedHat: dbTypes.SeverityLow, // CentOS uses RedHat
},
VendorVectors: map[string]dbTypes.CVSSVector{
vulnerability.Nvd: {
V2: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
V3: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
vulnerability.RedHat: {
V2: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
},
References: []string{"http://example.com"},
},
},
@@ -179,6 +189,16 @@ func TestClient_FillInfo(t *testing.T) {
Description: "dos vulnerability",
Severity: dbTypes.SeverityLow.String(),
References: []string{"http://example.com"},
VendorVectors: map[string]dbTypes.CVSSVector{
vulnerability.Nvd: {
V2: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
V3: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
vulnerability.RedHat: {
V2: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
},
},
SeveritySource: vulnerability.RedHat,
},

View File

@@ -23,7 +23,7 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ImageInfo struct {
type ArtifactInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
Created *timestamp.Timestamp `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
@@ -35,121 +35,121 @@ type ImageInfo struct {
XXX_sizecache int32 `json:"-"`
}
func (m *ImageInfo) Reset() { *m = ImageInfo{} }
func (m *ImageInfo) String() string { return proto.CompactTextString(m) }
func (*ImageInfo) ProtoMessage() {}
func (*ImageInfo) Descriptor() ([]byte, []int) {
func (m *ArtifactInfo) Reset() { *m = ArtifactInfo{} }
func (m *ArtifactInfo) String() string { return proto.CompactTextString(m) }
func (*ArtifactInfo) ProtoMessage() {}
func (*ArtifactInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{0}
}
func (m *ImageInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ImageInfo.Unmarshal(m, b)
func (m *ArtifactInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ArtifactInfo.Unmarshal(m, b)
}
func (m *ImageInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ImageInfo.Marshal(b, m, deterministic)
func (m *ArtifactInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ArtifactInfo.Marshal(b, m, deterministic)
}
func (m *ImageInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_ImageInfo.Merge(m, src)
func (m *ArtifactInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_ArtifactInfo.Merge(m, src)
}
func (m *ImageInfo) XXX_Size() int {
return xxx_messageInfo_ImageInfo.Size(m)
func (m *ArtifactInfo) XXX_Size() int {
return xxx_messageInfo_ArtifactInfo.Size(m)
}
func (m *ImageInfo) XXX_DiscardUnknown() {
xxx_messageInfo_ImageInfo.DiscardUnknown(m)
func (m *ArtifactInfo) XXX_DiscardUnknown() {
xxx_messageInfo_ArtifactInfo.DiscardUnknown(m)
}
var xxx_messageInfo_ImageInfo proto.InternalMessageInfo
var xxx_messageInfo_ArtifactInfo proto.InternalMessageInfo
func (m *ImageInfo) GetSchemaVersion() int32 {
func (m *ArtifactInfo) GetSchemaVersion() int32 {
if m != nil {
return m.SchemaVersion
}
return 0
}
func (m *ImageInfo) GetArchitecture() string {
func (m *ArtifactInfo) GetArchitecture() string {
if m != nil {
return m.Architecture
}
return ""
}
func (m *ImageInfo) GetCreated() *timestamp.Timestamp {
func (m *ArtifactInfo) GetCreated() *timestamp.Timestamp {
if m != nil {
return m.Created
}
return nil
}
func (m *ImageInfo) GetDockerVersion() string {
func (m *ArtifactInfo) GetDockerVersion() string {
if m != nil {
return m.DockerVersion
}
return ""
}
func (m *ImageInfo) GetOs() string {
func (m *ArtifactInfo) GetOs() string {
if m != nil {
return m.Os
}
return ""
}
func (m *ImageInfo) GetHistoryPackages() []*common.Package {
func (m *ArtifactInfo) GetHistoryPackages() []*common.Package {
if m != nil {
return m.HistoryPackages
}
return nil
}
type PutImageRequest struct {
ImageId string `protobuf:"bytes,1,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
ImageInfo *ImageInfo `protobuf:"bytes,2,opt,name=image_info,json=imageInfo,proto3" json:"image_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
type PutArtifactRequest struct {
ArtifactId string `protobuf:"bytes,1,opt,name=artifact_id,json=artifactId,proto3" json:"artifact_id,omitempty"`
ArtifactInfo *ArtifactInfo `protobuf:"bytes,2,opt,name=artifact_info,json=artifactInfo,proto3" json:"artifact_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutImageRequest) Reset() { *m = PutImageRequest{} }
func (m *PutImageRequest) String() string { return proto.CompactTextString(m) }
func (*PutImageRequest) ProtoMessage() {}
func (*PutImageRequest) Descriptor() ([]byte, []int) {
func (m *PutArtifactRequest) Reset() { *m = PutArtifactRequest{} }
func (m *PutArtifactRequest) String() string { return proto.CompactTextString(m) }
func (*PutArtifactRequest) ProtoMessage() {}
func (*PutArtifactRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{1}
}
func (m *PutImageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutImageRequest.Unmarshal(m, b)
func (m *PutArtifactRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutArtifactRequest.Unmarshal(m, b)
}
func (m *PutImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutImageRequest.Marshal(b, m, deterministic)
func (m *PutArtifactRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutArtifactRequest.Marshal(b, m, deterministic)
}
func (m *PutImageRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutImageRequest.Merge(m, src)
func (m *PutArtifactRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutArtifactRequest.Merge(m, src)
}
func (m *PutImageRequest) XXX_Size() int {
return xxx_messageInfo_PutImageRequest.Size(m)
func (m *PutArtifactRequest) XXX_Size() int {
return xxx_messageInfo_PutArtifactRequest.Size(m)
}
func (m *PutImageRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutImageRequest.DiscardUnknown(m)
func (m *PutArtifactRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutArtifactRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PutImageRequest proto.InternalMessageInfo
var xxx_messageInfo_PutArtifactRequest proto.InternalMessageInfo
func (m *PutImageRequest) GetImageId() string {
func (m *PutArtifactRequest) GetArtifactId() string {
if m != nil {
return m.ImageId
return m.ArtifactId
}
return ""
}
func (m *PutImageRequest) GetImageInfo() *ImageInfo {
func (m *PutArtifactRequest) GetArtifactInfo() *ArtifactInfo {
if m != nil {
return m.ImageInfo
return m.ArtifactInfo
}
return nil
}
type LayerInfo struct {
type BlobInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
Os *common.OS `protobuf:"bytes,2,opt,name=os,proto3" json:"os,omitempty"`
PackageInfos []*common.PackageInfo `protobuf:"bytes,3,rep,name=package_infos,json=packageInfos,proto3" json:"package_infos,omitempty"`
@@ -163,130 +163,130 @@ type LayerInfo struct {
XXX_sizecache int32 `json:"-"`
}
func (m *LayerInfo) Reset() { *m = LayerInfo{} }
func (m *LayerInfo) String() string { return proto.CompactTextString(m) }
func (*LayerInfo) ProtoMessage() {}
func (*LayerInfo) Descriptor() ([]byte, []int) {
func (m *BlobInfo) Reset() { *m = BlobInfo{} }
func (m *BlobInfo) String() string { return proto.CompactTextString(m) }
func (*BlobInfo) ProtoMessage() {}
func (*BlobInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{2}
}
func (m *LayerInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LayerInfo.Unmarshal(m, b)
func (m *BlobInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlobInfo.Unmarshal(m, b)
}
func (m *LayerInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_LayerInfo.Marshal(b, m, deterministic)
func (m *BlobInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BlobInfo.Marshal(b, m, deterministic)
}
func (m *LayerInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_LayerInfo.Merge(m, src)
func (m *BlobInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_BlobInfo.Merge(m, src)
}
func (m *LayerInfo) XXX_Size() int {
return xxx_messageInfo_LayerInfo.Size(m)
func (m *BlobInfo) XXX_Size() int {
return xxx_messageInfo_BlobInfo.Size(m)
}
func (m *LayerInfo) XXX_DiscardUnknown() {
xxx_messageInfo_LayerInfo.DiscardUnknown(m)
func (m *BlobInfo) XXX_DiscardUnknown() {
xxx_messageInfo_BlobInfo.DiscardUnknown(m)
}
var xxx_messageInfo_LayerInfo proto.InternalMessageInfo
var xxx_messageInfo_BlobInfo proto.InternalMessageInfo
func (m *LayerInfo) GetSchemaVersion() int32 {
func (m *BlobInfo) GetSchemaVersion() int32 {
if m != nil {
return m.SchemaVersion
}
return 0
}
func (m *LayerInfo) GetOs() *common.OS {
func (m *BlobInfo) GetOs() *common.OS {
if m != nil {
return m.Os
}
return nil
}
func (m *LayerInfo) GetPackageInfos() []*common.PackageInfo {
func (m *BlobInfo) GetPackageInfos() []*common.PackageInfo {
if m != nil {
return m.PackageInfos
}
return nil
}
func (m *LayerInfo) GetApplications() []*common.Application {
func (m *BlobInfo) GetApplications() []*common.Application {
if m != nil {
return m.Applications
}
return nil
}
func (m *LayerInfo) GetOpaqueDirs() []string {
func (m *BlobInfo) GetOpaqueDirs() []string {
if m != nil {
return m.OpaqueDirs
}
return nil
}
func (m *LayerInfo) GetWhiteoutFiles() []string {
func (m *BlobInfo) GetWhiteoutFiles() []string {
if m != nil {
return m.WhiteoutFiles
}
return nil
}
func (m *LayerInfo) GetDigest() string {
func (m *BlobInfo) GetDigest() string {
if m != nil {
return m.Digest
}
return ""
}
func (m *LayerInfo) GetDiffId() string {
func (m *BlobInfo) GetDiffId() string {
if m != nil {
return m.DiffId
}
return ""
}
type PutLayerRequest struct {
DiffId string `protobuf:"bytes,1,opt,name=diff_id,json=diffId,proto3" json:"diff_id,omitempty"`
LayerInfo *LayerInfo `protobuf:"bytes,3,opt,name=layer_info,json=layerInfo,proto3" json:"layer_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
type PutBlobRequest struct {
DiffId string `protobuf:"bytes,1,opt,name=diff_id,json=diffId,proto3" json:"diff_id,omitempty"`
BlobInfo *BlobInfo `protobuf:"bytes,3,opt,name=blob_info,json=blobInfo,proto3" json:"blob_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutLayerRequest) Reset() { *m = PutLayerRequest{} }
func (m *PutLayerRequest) String() string { return proto.CompactTextString(m) }
func (*PutLayerRequest) ProtoMessage() {}
func (*PutLayerRequest) Descriptor() ([]byte, []int) {
func (m *PutBlobRequest) Reset() { *m = PutBlobRequest{} }
func (m *PutBlobRequest) String() string { return proto.CompactTextString(m) }
func (*PutBlobRequest) ProtoMessage() {}
func (*PutBlobRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{3}
}
func (m *PutLayerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutLayerRequest.Unmarshal(m, b)
func (m *PutBlobRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutBlobRequest.Unmarshal(m, b)
}
func (m *PutLayerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutLayerRequest.Marshal(b, m, deterministic)
func (m *PutBlobRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutBlobRequest.Marshal(b, m, deterministic)
}
func (m *PutLayerRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutLayerRequest.Merge(m, src)
func (m *PutBlobRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutBlobRequest.Merge(m, src)
}
func (m *PutLayerRequest) XXX_Size() int {
return xxx_messageInfo_PutLayerRequest.Size(m)
func (m *PutBlobRequest) XXX_Size() int {
return xxx_messageInfo_PutBlobRequest.Size(m)
}
func (m *PutLayerRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutLayerRequest.DiscardUnknown(m)
func (m *PutBlobRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutBlobRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PutLayerRequest proto.InternalMessageInfo
var xxx_messageInfo_PutBlobRequest proto.InternalMessageInfo
func (m *PutLayerRequest) GetDiffId() string {
func (m *PutBlobRequest) GetDiffId() string {
if m != nil {
return m.DiffId
}
return ""
}
func (m *PutLayerRequest) GetLayerInfo() *LayerInfo {
func (m *PutBlobRequest) GetBlobInfo() *BlobInfo {
if m != nil {
return m.LayerInfo
return m.BlobInfo
}
return nil
}
@@ -338,155 +338,155 @@ func (m *PutResponse) GetEosl() bool {
return false
}
type MissingLayersRequest struct {
ImageId string `protobuf:"bytes,1,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
LayerIds []string `protobuf:"bytes,2,rep,name=layer_ids,json=layerIds,proto3" json:"layer_ids,omitempty"`
type MissingBlobsRequest struct {
ArtifactId string `protobuf:"bytes,1,opt,name=artifact_id,json=artifactId,proto3" json:"artifact_id,omitempty"`
BlobIds []string `protobuf:"bytes,2,rep,name=blob_ids,json=blobIds,proto3" json:"blob_ids,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MissingLayersRequest) Reset() { *m = MissingLayersRequest{} }
func (m *MissingLayersRequest) String() string { return proto.CompactTextString(m) }
func (*MissingLayersRequest) ProtoMessage() {}
func (*MissingLayersRequest) Descriptor() ([]byte, []int) {
func (m *MissingBlobsRequest) Reset() { *m = MissingBlobsRequest{} }
func (m *MissingBlobsRequest) String() string { return proto.CompactTextString(m) }
func (*MissingBlobsRequest) ProtoMessage() {}
func (*MissingBlobsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{5}
}
func (m *MissingLayersRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingLayersRequest.Unmarshal(m, b)
func (m *MissingBlobsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingBlobsRequest.Unmarshal(m, b)
}
func (m *MissingLayersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingLayersRequest.Marshal(b, m, deterministic)
func (m *MissingBlobsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingBlobsRequest.Marshal(b, m, deterministic)
}
func (m *MissingLayersRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingLayersRequest.Merge(m, src)
func (m *MissingBlobsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingBlobsRequest.Merge(m, src)
}
func (m *MissingLayersRequest) XXX_Size() int {
return xxx_messageInfo_MissingLayersRequest.Size(m)
func (m *MissingBlobsRequest) XXX_Size() int {
return xxx_messageInfo_MissingBlobsRequest.Size(m)
}
func (m *MissingLayersRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MissingLayersRequest.DiscardUnknown(m)
func (m *MissingBlobsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MissingBlobsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MissingLayersRequest proto.InternalMessageInfo
var xxx_messageInfo_MissingBlobsRequest proto.InternalMessageInfo
func (m *MissingLayersRequest) GetImageId() string {
func (m *MissingBlobsRequest) GetArtifactId() string {
if m != nil {
return m.ImageId
return m.ArtifactId
}
return ""
}
func (m *MissingLayersRequest) GetLayerIds() []string {
func (m *MissingBlobsRequest) GetBlobIds() []string {
if m != nil {
return m.LayerIds
return m.BlobIds
}
return nil
}
type MissingLayersResponse struct {
MissingImage bool `protobuf:"varint,1,opt,name=missing_image,json=missingImage,proto3" json:"missing_image,omitempty"`
MissingLayerIds []string `protobuf:"bytes,2,rep,name=missing_layer_ids,json=missingLayerIds,proto3" json:"missing_layer_ids,omitempty"`
type MissingBlobsResponse struct {
MissingArtifact bool `protobuf:"varint,1,opt,name=missing_artifact,json=missingArtifact,proto3" json:"missing_artifact,omitempty"`
MissingBlobIds []string `protobuf:"bytes,2,rep,name=missing_blob_ids,json=missingBlobIds,proto3" json:"missing_blob_ids,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MissingLayersResponse) Reset() { *m = MissingLayersResponse{} }
func (m *MissingLayersResponse) String() string { return proto.CompactTextString(m) }
func (*MissingLayersResponse) ProtoMessage() {}
func (*MissingLayersResponse) Descriptor() ([]byte, []int) {
func (m *MissingBlobsResponse) Reset() { *m = MissingBlobsResponse{} }
func (m *MissingBlobsResponse) String() string { return proto.CompactTextString(m) }
func (*MissingBlobsResponse) ProtoMessage() {}
func (*MissingBlobsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{6}
}
func (m *MissingLayersResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingLayersResponse.Unmarshal(m, b)
func (m *MissingBlobsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingBlobsResponse.Unmarshal(m, b)
}
func (m *MissingLayersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingLayersResponse.Marshal(b, m, deterministic)
func (m *MissingBlobsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingBlobsResponse.Marshal(b, m, deterministic)
}
func (m *MissingLayersResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingLayersResponse.Merge(m, src)
func (m *MissingBlobsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingBlobsResponse.Merge(m, src)
}
func (m *MissingLayersResponse) XXX_Size() int {
return xxx_messageInfo_MissingLayersResponse.Size(m)
func (m *MissingBlobsResponse) XXX_Size() int {
return xxx_messageInfo_MissingBlobsResponse.Size(m)
}
func (m *MissingLayersResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MissingLayersResponse.DiscardUnknown(m)
func (m *MissingBlobsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MissingBlobsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MissingLayersResponse proto.InternalMessageInfo
var xxx_messageInfo_MissingBlobsResponse proto.InternalMessageInfo
func (m *MissingLayersResponse) GetMissingImage() bool {
func (m *MissingBlobsResponse) GetMissingArtifact() bool {
if m != nil {
return m.MissingImage
return m.MissingArtifact
}
return false
}
func (m *MissingLayersResponse) GetMissingLayerIds() []string {
func (m *MissingBlobsResponse) GetMissingBlobIds() []string {
if m != nil {
return m.MissingLayerIds
return m.MissingBlobIds
}
return nil
}
func init() {
proto.RegisterType((*ImageInfo)(nil), "trivy.cache.v1.ImageInfo")
proto.RegisterType((*PutImageRequest)(nil), "trivy.cache.v1.PutImageRequest")
proto.RegisterType((*LayerInfo)(nil), "trivy.cache.v1.LayerInfo")
proto.RegisterType((*PutLayerRequest)(nil), "trivy.cache.v1.PutLayerRequest")
proto.RegisterType((*ArtifactInfo)(nil), "trivy.cache.v1.ArtifactInfo")
proto.RegisterType((*PutArtifactRequest)(nil), "trivy.cache.v1.PutArtifactRequest")
proto.RegisterType((*BlobInfo)(nil), "trivy.cache.v1.BlobInfo")
proto.RegisterType((*PutBlobRequest)(nil), "trivy.cache.v1.PutBlobRequest")
proto.RegisterType((*PutResponse)(nil), "trivy.cache.v1.PutResponse")
proto.RegisterType((*MissingLayersRequest)(nil), "trivy.cache.v1.MissingLayersRequest")
proto.RegisterType((*MissingLayersResponse)(nil), "trivy.cache.v1.MissingLayersResponse")
proto.RegisterType((*MissingBlobsRequest)(nil), "trivy.cache.v1.MissingBlobsRequest")
proto.RegisterType((*MissingBlobsResponse)(nil), "trivy.cache.v1.MissingBlobsResponse")
}
func init() { proto.RegisterFile("rpc/cache/service.proto", fileDescriptor_1f1f7d564abadf42) }
var fileDescriptor_1f1f7d564abadf42 = []byte{
// 674 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xd3, 0x30,
0x14, 0x56, 0xdb, 0x75, 0x6d, 0x4e, 0xdb, 0x6d, 0x58, 0x6c, 0xcb, 0xba, 0x8b, 0x55, 0x81, 0x49,
0x15, 0x17, 0x89, 0x28, 0x08, 0x71, 0x03, 0x02, 0x06, 0x48, 0x95, 0xf8, 0x99, 0x0c, 0xe2, 0x02,
0x21, 0x55, 0x5e, 0xe2, 0xa4, 0xd6, 0x9a, 0x38, 0xb3, 0x9d, 0xa2, 0x3e, 0x00, 0x6f, 0xc7, 0xbb,
0xf0, 0x0a, 0x28, 0x27, 0xc9, 0xd6, 0x96, 0xf2, 0x77, 0xe7, 0xf3, 0x93, 0xef, 0x7c, 0xe7, 0xfb,
0xec, 0xc0, 0xa1, 0x4a, 0x7d, 0xcf, 0x67, 0xfe, 0x94, 0x7b, 0x9a, 0xab, 0xb9, 0xf0, 0xb9, 0x9b,
0x2a, 0x69, 0x24, 0xd9, 0x31, 0x4a, 0xcc, 0x17, 0x2e, 0x96, 0xdc, 0xf9, 0xfd, 0xfe, 0x49, 0x24,
0x65, 0x34, 0xe3, 0x1e, 0x56, 0x2f, 0xb2, 0xd0, 0x33, 0x22, 0xe6, 0xda, 0xb0, 0x38, 0x2d, 0x3e,
0xe8, 0x3f, 0x8a, 0x84, 0x99, 0x66, 0x17, 0xae, 0x2f, 0x63, 0x8f, 0x5d, 0x65, 0x4c, 0x73, 0x3f,
0x53, 0xc2, 0x2c, 0x3c, 0x04, 0xf2, 0x70, 0x8e, 0x8c, 0x63, 0x99, 0xac, 0x0e, 0xea, 0x1f, 0xaf,
0x03, 0xf3, 0x38, 0x35, 0x8b, 0xa2, 0xe8, 0x7c, 0xab, 0x83, 0x35, 0x8e, 0x59, 0xc4, 0xc7, 0x49,
0x28, 0xc9, 0x29, 0xec, 0x68, 0x7f, 0xca, 0x63, 0x36, 0x99, 0x73, 0xa5, 0x85, 0x4c, 0xec, 0xda,
0xa0, 0x36, 0x6c, 0xd2, 0x5e, 0x91, 0xfd, 0x54, 0x24, 0x89, 0x03, 0x5d, 0xa6, 0xfc, 0xa9, 0x30,
0xdc, 0x37, 0x99, 0xe2, 0x76, 0x7d, 0x50, 0x1b, 0x5a, 0x74, 0x25, 0x47, 0x1e, 0x42, 0xcb, 0x57,
0x9c, 0x19, 0x1e, 0xd8, 0x8d, 0x41, 0x6d, 0xd8, 0x19, 0xf5, 0xdd, 0x82, 0x87, 0x5b, 0xf1, 0x70,
0x3f, 0x56, 0x0b, 0xd2, 0xaa, 0x35, 0x27, 0x10, 0x48, 0xff, 0x92, 0xab, 0x6b, 0x02, 0x5b, 0x88,
0xdd, 0x2b, 0xb2, 0x15, 0x81, 0x1d, 0xa8, 0x4b, 0x6d, 0x37, 0xb1, 0x54, 0x97, 0x9a, 0x3c, 0x83,
0xbd, 0xa9, 0xd0, 0x46, 0xaa, 0xc5, 0x24, 0x65, 0xfe, 0x25, 0x8b, 0xb8, 0xb6, 0xb7, 0x07, 0x8d,
0x61, 0x67, 0xb4, 0xef, 0x96, 0x32, 0xa3, 0x32, 0xee, 0x79, 0x51, 0xa5, 0xbb, 0x65, 0x7b, 0x19,
0x6b, 0x27, 0x84, 0xdd, 0xf3, 0xcc, 0xa0, 0x12, 0x94, 0x5f, 0x65, 0x5c, 0x1b, 0x72, 0x04, 0x6d,
0x91, 0xc7, 0x13, 0x11, 0xa0, 0x0c, 0x16, 0x6d, 0x61, 0x3c, 0x0e, 0xc8, 0x63, 0x80, 0xb2, 0x94,
0x84, 0x12, 0xd7, 0xef, 0x8c, 0x8e, 0xdc, 0x55, 0x43, 0xdd, 0x6b, 0x59, 0xa9, 0x25, 0xaa, 0xa3,
0xf3, 0xbd, 0x0e, 0xd6, 0x1b, 0xb6, 0xe0, 0xea, 0x7f, 0xf4, 0x1e, 0xe0, 0xba, 0xc5, 0x98, 0xbd,
0xd5, 0x85, 0xde, 0x7f, 0x40, 0x01, 0x9e, 0x42, 0xaf, 0x5c, 0x1c, 0x29, 0x69, 0xbb, 0x81, 0xdb,
0x1f, 0x6d, 0xdc, 0x1e, 0x39, 0x75, 0xd3, 0x9b, 0x40, 0x93, 0x27, 0xd0, 0x65, 0x69, 0x3a, 0x13,
0x3e, 0x33, 0x42, 0x26, 0xda, 0xde, 0xda, 0xf4, 0xf9, 0xf3, 0x9b, 0x0e, 0xba, 0xd2, 0x4e, 0x4e,
0xa0, 0x23, 0x53, 0x76, 0x95, 0xf1, 0x49, 0x20, 0x54, 0x6e, 0x4c, 0x63, 0x68, 0x51, 0x28, 0x52,
0x2f, 0x85, 0xd2, 0xf9, 0xa2, 0x5f, 0xf3, 0xbb, 0x21, 0x33, 0x33, 0x09, 0xc5, 0xac, 0xb4, 0xc7,
0xa2, 0xbd, 0x2a, 0xfb, 0x3a, 0x4f, 0x92, 0x03, 0xd8, 0x0e, 0x44, 0xc4, 0xb5, 0xb1, 0x5b, 0x28,
0x78, 0x19, 0x91, 0x43, 0x68, 0x05, 0x22, 0x0c, 0x73, 0x27, 0xda, 0x55, 0x21, 0x0c, 0xc7, 0x81,
0x13, 0xa0, 0x6d, 0x28, 0x68, 0x65, 0xdb, 0x52, 0x6f, 0x6d, 0xb9, 0x37, 0x37, 0x6d, 0x96, 0x37,
0x16, 0xa6, 0x35, 0x36, 0x9b, 0x76, 0xed, 0x0d, 0xb5, 0x66, 0xd5, 0xd1, 0x39, 0x83, 0xce, 0x79,
0x66, 0x28, 0xd7, 0xa9, 0x4c, 0x34, 0x2f, 0xed, 0xa8, 0xfd, 0xc1, 0x0e, 0x02, 0x5b, 0x5c, 0xea,
0x19, 0x5a, 0xd6, 0xa6, 0x78, 0x76, 0xde, 0xc1, 0xed, 0xb7, 0x42, 0x6b, 0x91, 0x44, 0x38, 0x43,
0xff, 0xc3, 0x35, 0x3b, 0x06, 0xab, 0x64, 0x1c, 0xe4, 0xf6, 0xe7, 0x82, 0xb5, 0x0b, 0x56, 0x81,
0x76, 0xa6, 0xb0, 0xbf, 0x86, 0x57, 0xd2, 0xbb, 0x03, 0xbd, 0xb8, 0x28, 0x4c, 0x10, 0x08, 0x51,
0xdb, 0xb4, 0x5b, 0x26, 0xf1, 0x5a, 0x92, 0x7b, 0x70, 0xab, 0x6a, 0x5a, 0x1f, 0xb1, 0x1b, 0x2f,
0xc1, 0x8e, 0x03, 0x3d, 0xfa, 0x51, 0x83, 0xe6, 0x59, 0x2e, 0x10, 0x39, 0x83, 0x76, 0xf5, 0x4a,
0xc8, 0xc9, 0xba, 0x74, 0x6b, 0xef, 0xa7, 0x7f, 0xf0, 0xcb, 0x83, 0x7f, 0x95, 0xff, 0x78, 0x4a,
0x10, 0x44, 0xdf, 0x08, 0xb2, 0xec, 0xe6, 0x6f, 0x41, 0xbe, 0x40, 0x6f, 0x65, 0x7b, 0x72, 0x77,
0x1d, 0x69, 0x93, 0xd8, 0xfd, 0xd3, 0xbf, 0x74, 0x15, 0x12, 0xbe, 0x68, 0x7d, 0x6e, 0x62, 0xc7,
0xc5, 0x36, 0x8e, 0x7d, 0xf0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x08, 0x8d, 0x6f, 0x9e, 0xc6, 0x05,
0x00, 0x00,
// 682 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0xdd, 0x6e, 0xd3, 0x3e,
0x18, 0xc6, 0xd5, 0x76, 0x5d, 0xdb, 0xb7, 0x1f, 0x9b, 0xfc, 0xff, 0xb3, 0x65, 0x05, 0xb1, 0x2a,
0x80, 0x54, 0x4e, 0x52, 0x51, 0x3e, 0xce, 0x40, 0x74, 0x03, 0xa4, 0x1e, 0x20, 0x4a, 0x40, 0x48,
0x70, 0x52, 0x5c, 0xc7, 0x69, 0xad, 0x35, 0x71, 0x66, 0x3b, 0x85, 0xde, 0x01, 0xf7, 0xc6, 0xcd,
0x70, 0x09, 0xc8, 0x76, 0xb2, 0x7e, 0xac, 0x4c, 0x70, 0x16, 0x3f, 0x7e, 0xf3, 0xbc, 0xaf, 0x7f,
0x8f, 0x13, 0x38, 0x16, 0x09, 0xe9, 0x11, 0x4c, 0x66, 0xb4, 0x27, 0xa9, 0x58, 0x30, 0x42, 0xbd,
0x44, 0x70, 0xc5, 0x51, 0x4b, 0x09, 0xb6, 0x58, 0x7a, 0x66, 0xcb, 0x5b, 0x3c, 0x6a, 0x9f, 0x4e,
0x39, 0x9f, 0xce, 0x69, 0xcf, 0xec, 0x4e, 0xd2, 0xb0, 0xa7, 0x58, 0x44, 0xa5, 0xc2, 0x51, 0x62,
0x5f, 0x68, 0x3f, 0x9b, 0x32, 0x35, 0x4b, 0x27, 0x1e, 0xe1, 0x51, 0x0f, 0x5f, 0xa6, 0x58, 0x52,
0x92, 0x0a, 0xa6, 0x96, 0x3d, 0x63, 0xd4, 0x33, 0x7d, 0x78, 0x14, 0xf1, 0x78, 0xb3, 0x51, 0xfb,
0xf6, 0xb6, 0x31, 0x8d, 0x12, 0xb5, 0xb4, 0x9b, 0xee, 0x8f, 0x22, 0x34, 0x06, 0x42, 0xb1, 0x10,
0x13, 0x35, 0x8c, 0x43, 0x8e, 0x1e, 0x40, 0x4b, 0x92, 0x19, 0x8d, 0xf0, 0x78, 0x41, 0x85, 0x64,
0x3c, 0x76, 0x0a, 0x9d, 0x42, 0xb7, 0xec, 0x37, 0xad, 0xfa, 0xc9, 0x8a, 0xc8, 0x85, 0x06, 0x16,
0x64, 0xc6, 0x14, 0x25, 0x2a, 0x15, 0xd4, 0x29, 0x76, 0x0a, 0xdd, 0x9a, 0xbf, 0xa1, 0xa1, 0x27,
0x50, 0x21, 0x82, 0x62, 0x45, 0x03, 0xa7, 0xd4, 0x29, 0x74, 0xeb, 0xfd, 0xb6, 0x67, 0x47, 0xf1,
0xf2, 0x51, 0xbc, 0x8f, 0xf9, 0x19, 0xfd, 0xbc, 0x54, 0x0f, 0x10, 0x70, 0x72, 0x41, 0xc5, 0xd5,
0x00, 0x7b, 0xc6, 0xbb, 0x69, 0xd5, 0x7c, 0x80, 0x16, 0x14, 0xb9, 0x74, 0xca, 0x66, 0xab, 0xc8,
0x25, 0x7a, 0x09, 0x87, 0x33, 0x26, 0x15, 0x17, 0xcb, 0x71, 0x82, 0xc9, 0x05, 0x9e, 0x52, 0xe9,
0xec, 0x77, 0x4a, 0xdd, 0x7a, 0xff, 0x96, 0x97, 0x91, 0x36, 0x70, 0xbc, 0x91, 0xdd, 0xf5, 0x0f,
0xb2, 0xf2, 0x6c, 0x2d, 0xdd, 0xef, 0x80, 0x46, 0xa9, 0xca, 0x61, 0xf8, 0xf4, 0x32, 0xa5, 0x52,
0xa1, 0x53, 0xa8, 0xe3, 0x4c, 0x1a, 0xb3, 0xc0, 0xc0, 0xa8, 0xf9, 0x90, 0x4b, 0xc3, 0x00, 0x0d,
0xa0, 0xb9, 0x2a, 0x88, 0x43, 0x6e, 0x50, 0xd4, 0xfb, 0x77, 0xbc, 0xcd, 0x7c, 0xbd, 0x75, 0xca,
0x1a, 0xd4, 0x6a, 0xe5, 0xfe, 0x2c, 0x42, 0xf5, 0x6c, 0xce, 0x27, 0xff, 0x12, 0x40, 0xc7, 0x9c,
0xdf, 0xf6, 0x3a, 0xdc, 0x3c, 0xe1, 0xbb, 0x0f, 0x86, 0xc8, 0x0b, 0x68, 0x66, 0x24, 0xcc, 0x5c,
0xd2, 0x29, 0x19, 0x1c, 0x27, 0x3b, 0x71, 0xd8, 0xa9, 0x92, 0xd5, 0x42, 0xa2, 0xe7, 0xd0, 0xc0,
0x49, 0x32, 0x67, 0x04, 0x2b, 0xc6, 0x63, 0xe9, 0xec, 0xed, 0x7a, 0x7d, 0xb0, 0xaa, 0xf0, 0x37,
0xca, 0x35, 0x38, 0x9e, 0xe0, 0xcb, 0x94, 0x8e, 0x03, 0x26, 0x74, 0x52, 0x25, 0x0d, 0xce, 0x4a,
0xaf, 0x98, 0x90, 0xfa, 0xa0, 0xdf, 0xf4, 0x65, 0xe1, 0xa9, 0x1a, 0x87, 0x6c, 0x9e, 0xe5, 0x55,
0xf3, 0x9b, 0xb9, 0xfa, 0x46, 0x8b, 0xe8, 0x08, 0xf6, 0x03, 0x36, 0xa5, 0x52, 0x39, 0x15, 0xc3,
0x3e, 0x5b, 0xa1, 0x63, 0xa8, 0x04, 0x2c, 0x0c, 0x75, 0x28, 0xd5, 0x7c, 0x23, 0x0c, 0x87, 0x81,
0xfb, 0x15, 0x5a, 0xa3, 0x54, 0x69, 0x9e, 0x79, 0x86, 0x6b, 0xa5, 0x85, 0xf5, 0x52, 0xf4, 0x14,
0x6a, 0x93, 0x39, 0x9f, 0xd8, 0xdc, 0xec, 0x1d, 0x75, 0xb6, 0x73, 0xcb, 0x83, 0xf1, 0xab, 0x93,
0xec, 0xc9, 0x3d, 0x87, 0xfa, 0x28, 0x55, 0x3e, 0x95, 0x09, 0x8f, 0x25, 0xcd, 0xa2, 0x28, 0xdc,
0x10, 0x05, 0x82, 0x3d, 0xca, 0xe5, 0xdc, 0xc4, 0x55, 0xf5, 0xcd, 0xb3, 0xfb, 0x1e, 0xfe, 0x7b,
0xcb, 0xa4, 0x64, 0xf1, 0x54, 0x77, 0x90, 0x7f, 0x7d, 0xdf, 0x4e, 0xa0, 0x6a, 0x67, 0x0e, 0x74,
0xfc, 0x1a, 0x58, 0xc5, 0x0c, 0x16, 0x48, 0xf7, 0x02, 0xfe, 0xdf, 0xb4, 0xcc, 0x06, 0x7c, 0x08,
0x87, 0x91, 0xd5, 0xc7, 0xb9, 0x91, 0x31, 0xae, 0xfa, 0x07, 0x99, 0x9e, 0x5f, 0x4e, 0xd4, 0x5d,
0x95, 0x6e, 0x75, 0x69, 0x45, 0x2b, 0xeb, 0x61, 0x20, 0xfb, 0xbf, 0x0a, 0x50, 0x3e, 0xd7, 0x90,
0xd0, 0xd0, 0xe0, 0xb8, 0xb2, 0x70, 0xb7, 0x09, 0x5e, 0xff, 0xaa, 0xda, 0x47, 0xd7, 0xfe, 0x04,
0xaf, 0xf5, 0x4f, 0x09, 0x0d, 0xa0, 0x92, 0x65, 0x87, 0xee, 0xee, 0xb0, 0x59, 0x0b, 0xf5, 0x8f,
0x16, 0x9f, 0xa1, 0xb1, 0x0e, 0x01, 0xdd, 0xdb, 0xf6, 0xd9, 0x41, 0xbd, 0x7d, 0xff, 0xe6, 0x22,
0xcb, 0xf1, 0xac, 0xf2, 0xa5, 0x6c, 0x0a, 0x26, 0xfb, 0xa6, 0xe7, 0xe3, 0xdf, 0x01, 0x00, 0x00,
0xff, 0xff, 0xf9, 0x1a, 0xc0, 0xa0, 0xdd, 0x05, 0x00, 0x00,
}

View File

@@ -8,12 +8,12 @@ import "github.com/aquasecurity/trivy/rpc/common/service.proto";
import "google/protobuf/empty.proto";
service Cache {
rpc PutImage(PutImageRequest) returns (google.protobuf.Empty);
rpc PutLayer(PutLayerRequest) returns (google.protobuf.Empty);
rpc MissingLayers(MissingLayersRequest) returns (MissingLayersResponse);
rpc PutArtifact(PutArtifactRequest) returns (google.protobuf.Empty);
rpc PutBlob(PutBlobRequest) returns (google.protobuf.Empty);
rpc MissingBlobs(MissingBlobsRequest) returns (MissingBlobsResponse);
}
message ImageInfo {
message ArtifactInfo {
int32 schema_version = 1;
string architecture = 2;
google.protobuf.Timestamp created = 3;
@@ -22,12 +22,12 @@ message ImageInfo {
repeated common.Package history_packages = 6;
}
message PutImageRequest {
string image_id = 1;
ImageInfo image_info = 2;
message PutArtifactRequest {
string artifact_id = 1;
ArtifactInfo artifact_info = 2;
}
message LayerInfo {
message BlobInfo {
int32 schema_version = 1;
common.OS os = 2;
repeated common.PackageInfo package_infos = 3;
@@ -38,9 +38,9 @@ message LayerInfo {
string diff_id = 8;
}
message PutLayerRequest {
string diff_id = 1;
LayerInfo layer_info = 3;
message PutBlobRequest {
string diff_id = 1;
BlobInfo blob_info = 3;
}
message PutResponse {
@@ -48,12 +48,12 @@ message PutResponse {
bool eosl = 2;
}
message MissingLayersRequest {
string image_id = 1;
repeated string layer_ids = 2;
message MissingBlobsRequest {
string artifact_id = 1;
repeated string blob_ids = 2;
}
message MissingLayersResponse {
bool missing_image = 1;
repeated string missing_layer_ids = 2;
message MissingBlobsResponse {
bool missing_artifact = 1;
repeated string missing_blob_ids = 2;
}

View File

@@ -17,6 +17,7 @@ import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
@@ -30,16 +31,19 @@ import io "io"
import json "encoding/json"
import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// ===============
// Cache Interface
// ===============
type Cache interface {
PutImage(context.Context, *PutImageRequest) (*google_protobuf1.Empty, error)
PutArtifact(context.Context, *PutArtifactRequest) (*google_protobuf1.Empty, error)
PutLayer(context.Context, *PutLayerRequest) (*google_protobuf1.Empty, error)
PutBlob(context.Context, *PutBlobRequest) (*google_protobuf1.Empty, error)
MissingLayers(context.Context, *MissingLayersRequest) (*MissingLayersResponse, error)
MissingBlobs(context.Context, *MissingBlobsRequest) (*MissingBlobsResponse, error)
}
// =====================
@@ -66,9 +70,9 @@ func NewCacheProtobufClient(addr string, client HTTPClient, opts ...twirp.Client
prefix := urlBase(addr) + CachePathPrefix
urls := [3]string{
prefix + "PutImage",
prefix + "PutLayer",
prefix + "MissingLayers",
prefix + "PutArtifact",
prefix + "PutBlob",
prefix + "MissingBlobs",
}
return &cacheProtobufClient{
@@ -78,10 +82,10 @@ func NewCacheProtobufClient(addr string, client HTTPClient, opts ...twirp.Client
}
}
func (c *cacheProtobufClient) PutImage(ctx context.Context, in *PutImageRequest) (*google_protobuf1.Empty, error) {
func (c *cacheProtobufClient) PutArtifact(ctx context.Context, in *PutArtifactRequest) (*google_protobuf1.Empty, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "PutImage")
ctx = ctxsetters.WithMethodName(ctx, "PutArtifact")
out := new(google_protobuf1.Empty)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
@@ -98,10 +102,10 @@ func (c *cacheProtobufClient) PutImage(ctx context.Context, in *PutImageRequest)
return out, nil
}
func (c *cacheProtobufClient) PutLayer(ctx context.Context, in *PutLayerRequest) (*google_protobuf1.Empty, error) {
func (c *cacheProtobufClient) PutBlob(ctx context.Context, in *PutBlobRequest) (*google_protobuf1.Empty, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "PutLayer")
ctx = ctxsetters.WithMethodName(ctx, "PutBlob")
out := new(google_protobuf1.Empty)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out)
if err != nil {
@@ -118,11 +122,11 @@ func (c *cacheProtobufClient) PutLayer(ctx context.Context, in *PutLayerRequest)
return out, nil
}
func (c *cacheProtobufClient) MissingLayers(ctx context.Context, in *MissingLayersRequest) (*MissingLayersResponse, error) {
func (c *cacheProtobufClient) MissingBlobs(ctx context.Context, in *MissingBlobsRequest) (*MissingBlobsResponse, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "MissingLayers")
out := new(MissingLayersResponse)
ctx = ctxsetters.WithMethodName(ctx, "MissingBlobs")
out := new(MissingBlobsResponse)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
@@ -162,9 +166,9 @@ func NewCacheJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOpti
prefix := urlBase(addr) + CachePathPrefix
urls := [3]string{
prefix + "PutImage",
prefix + "PutLayer",
prefix + "MissingLayers",
prefix + "PutArtifact",
prefix + "PutBlob",
prefix + "MissingBlobs",
}
return &cacheJSONClient{
@@ -174,10 +178,10 @@ func NewCacheJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOpti
}
}
func (c *cacheJSONClient) PutImage(ctx context.Context, in *PutImageRequest) (*google_protobuf1.Empty, error) {
func (c *cacheJSONClient) PutArtifact(ctx context.Context, in *PutArtifactRequest) (*google_protobuf1.Empty, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "PutImage")
ctx = ctxsetters.WithMethodName(ctx, "PutArtifact")
out := new(google_protobuf1.Empty)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
@@ -194,10 +198,10 @@ func (c *cacheJSONClient) PutImage(ctx context.Context, in *PutImageRequest) (*g
return out, nil
}
func (c *cacheJSONClient) PutLayer(ctx context.Context, in *PutLayerRequest) (*google_protobuf1.Empty, error) {
func (c *cacheJSONClient) PutBlob(ctx context.Context, in *PutBlobRequest) (*google_protobuf1.Empty, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "PutLayer")
ctx = ctxsetters.WithMethodName(ctx, "PutBlob")
out := new(google_protobuf1.Empty)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out)
if err != nil {
@@ -214,11 +218,11 @@ func (c *cacheJSONClient) PutLayer(ctx context.Context, in *PutLayerRequest) (*g
return out, nil
}
func (c *cacheJSONClient) MissingLayers(ctx context.Context, in *MissingLayersRequest) (*MissingLayersResponse, error) {
func (c *cacheJSONClient) MissingBlobs(ctx context.Context, in *MissingBlobsRequest) (*MissingBlobsResponse, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1")
ctx = ctxsetters.WithServiceName(ctx, "Cache")
ctx = ctxsetters.WithMethodName(ctx, "MissingLayers")
out := new(MissingLayersResponse)
ctx = ctxsetters.WithMethodName(ctx, "MissingBlobs")
out := new(MissingBlobsResponse)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
@@ -282,14 +286,14 @@ func (s *cacheServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
}
switch req.URL.Path {
case "/twirp/trivy.cache.v1.Cache/PutImage":
s.servePutImage(ctx, resp, req)
case "/twirp/trivy.cache.v1.Cache/PutArtifact":
s.servePutArtifact(ctx, resp, req)
return
case "/twirp/trivy.cache.v1.Cache/PutLayer":
s.servePutLayer(ctx, resp, req)
case "/twirp/trivy.cache.v1.Cache/PutBlob":
s.servePutBlob(ctx, resp, req)
return
case "/twirp/trivy.cache.v1.Cache/MissingLayers":
s.serveMissingLayers(ctx, resp, req)
case "/twirp/trivy.cache.v1.Cache/MissingBlobs":
s.serveMissingBlobs(ctx, resp, req)
return
default:
msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
@@ -299,7 +303,7 @@ func (s *cacheServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
}
}
func (s *cacheServer) servePutImage(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutArtifact(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
header := req.Header.Get("Content-Type")
i := strings.Index(header, ";")
if i == -1 {
@@ -307,9 +311,9 @@ func (s *cacheServer) servePutImage(ctx context.Context, resp http.ResponseWrite
}
switch strings.TrimSpace(strings.ToLower(header[:i])) {
case "application/json":
s.servePutImageJSON(ctx, resp, req)
s.servePutArtifactJSON(ctx, resp, req)
case "application/protobuf":
s.servePutImageProtobuf(ctx, resp, req)
s.servePutArtifactProtobuf(ctx, resp, req)
default:
msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
twerr := badRouteError(msg, req.Method, req.URL.Path)
@@ -317,16 +321,16 @@ func (s *cacheServer) servePutImage(ctx context.Context, resp http.ResponseWrite
}
}
func (s *cacheServer) servePutImageJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutArtifactJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "PutImage")
ctx = ctxsetters.WithMethodName(ctx, "PutArtifact")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
reqContent := new(PutImageRequest)
reqContent := new(PutArtifactRequest)
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
@@ -337,7 +341,7 @@ func (s *cacheServer) servePutImageJSON(ctx context.Context, resp http.ResponseW
var respContent *google_protobuf1.Empty
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.PutImage(ctx, reqContent)
respContent, err = s.Cache.PutArtifact(ctx, reqContent)
}()
if err != nil {
@@ -345,7 +349,7 @@ func (s *cacheServer) servePutImageJSON(ctx context.Context, resp http.ResponseW
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutImage. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutArtifact. nil responses are not supported"))
return
}
@@ -372,9 +376,9 @@ func (s *cacheServer) servePutImageJSON(ctx context.Context, resp http.ResponseW
callResponseSent(ctx, s.hooks)
}
func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutArtifactProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "PutImage")
ctx = ctxsetters.WithMethodName(ctx, "PutArtifact")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
@@ -386,7 +390,7 @@ func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.Respo
s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
return
}
reqContent := new(PutImageRequest)
reqContent := new(PutArtifactRequest)
if err = proto.Unmarshal(buf, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
return
@@ -396,7 +400,7 @@ func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.Respo
var respContent *google_protobuf1.Empty
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.PutImage(ctx, reqContent)
respContent, err = s.Cache.PutArtifact(ctx, reqContent)
}()
if err != nil {
@@ -404,7 +408,7 @@ func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.Respo
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutImage. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutArtifact. nil responses are not supported"))
return
}
@@ -416,6 +420,16 @@ func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.Respo
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -428,7 +442,7 @@ func (s *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.Respo
callResponseSent(ctx, s.hooks)
}
func (s *cacheServer) servePutLayer(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutBlob(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
header := req.Header.Get("Content-Type")
i := strings.Index(header, ";")
if i == -1 {
@@ -436,9 +450,9 @@ func (s *cacheServer) servePutLayer(ctx context.Context, resp http.ResponseWrite
}
switch strings.TrimSpace(strings.ToLower(header[:i])) {
case "application/json":
s.servePutLayerJSON(ctx, resp, req)
s.servePutBlobJSON(ctx, resp, req)
case "application/protobuf":
s.servePutLayerProtobuf(ctx, resp, req)
s.servePutBlobProtobuf(ctx, resp, req)
default:
msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
twerr := badRouteError(msg, req.Method, req.URL.Path)
@@ -446,16 +460,16 @@ func (s *cacheServer) servePutLayer(ctx context.Context, resp http.ResponseWrite
}
}
func (s *cacheServer) servePutLayerJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutBlobJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "PutLayer")
ctx = ctxsetters.WithMethodName(ctx, "PutBlob")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
reqContent := new(PutLayerRequest)
reqContent := new(PutBlobRequest)
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
@@ -466,7 +480,7 @@ func (s *cacheServer) servePutLayerJSON(ctx context.Context, resp http.ResponseW
var respContent *google_protobuf1.Empty
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.PutLayer(ctx, reqContent)
respContent, err = s.Cache.PutBlob(ctx, reqContent)
}()
if err != nil {
@@ -474,7 +488,7 @@ func (s *cacheServer) servePutLayerJSON(ctx context.Context, resp http.ResponseW
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutLayer. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutBlob. nil responses are not supported"))
return
}
@@ -501,9 +515,9 @@ func (s *cacheServer) servePutLayerJSON(ctx context.Context, resp http.ResponseW
callResponseSent(ctx, s.hooks)
}
func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) servePutBlobProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "PutLayer")
ctx = ctxsetters.WithMethodName(ctx, "PutBlob")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
@@ -515,7 +529,7 @@ func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.Respo
s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
return
}
reqContent := new(PutLayerRequest)
reqContent := new(PutBlobRequest)
if err = proto.Unmarshal(buf, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
return
@@ -525,7 +539,7 @@ func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.Respo
var respContent *google_protobuf1.Empty
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.PutLayer(ctx, reqContent)
respContent, err = s.Cache.PutBlob(ctx, reqContent)
}()
if err != nil {
@@ -533,7 +547,7 @@ func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.Respo
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutLayer. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutBlob. nil responses are not supported"))
return
}
@@ -545,6 +559,16 @@ func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.Respo
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -557,7 +581,7 @@ func (s *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.Respo
callResponseSent(ctx, s.hooks)
}
func (s *cacheServer) serveMissingLayers(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) serveMissingBlobs(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
header := req.Header.Get("Content-Type")
i := strings.Index(header, ";")
if i == -1 {
@@ -565,9 +589,9 @@ func (s *cacheServer) serveMissingLayers(ctx context.Context, resp http.Response
}
switch strings.TrimSpace(strings.ToLower(header[:i])) {
case "application/json":
s.serveMissingLayersJSON(ctx, resp, req)
s.serveMissingBlobsJSON(ctx, resp, req)
case "application/protobuf":
s.serveMissingLayersProtobuf(ctx, resp, req)
s.serveMissingBlobsProtobuf(ctx, resp, req)
default:
msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
twerr := badRouteError(msg, req.Method, req.URL.Path)
@@ -575,16 +599,16 @@ func (s *cacheServer) serveMissingLayers(ctx context.Context, resp http.Response
}
}
func (s *cacheServer) serveMissingLayersJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) serveMissingBlobsJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "MissingLayers")
ctx = ctxsetters.WithMethodName(ctx, "MissingBlobs")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
reqContent := new(MissingLayersRequest)
reqContent := new(MissingBlobsRequest)
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
@@ -592,10 +616,10 @@ func (s *cacheServer) serveMissingLayersJSON(ctx context.Context, resp http.Resp
}
// Call service method
var respContent *MissingLayersResponse
var respContent *MissingBlobsResponse
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.MissingLayers(ctx, reqContent)
respContent, err = s.Cache.MissingBlobs(ctx, reqContent)
}()
if err != nil {
@@ -603,7 +627,7 @@ func (s *cacheServer) serveMissingLayersJSON(ctx context.Context, resp http.Resp
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingLayersResponse and nil error while calling MissingLayers. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingBlobsResponse and nil error while calling MissingBlobs. nil responses are not supported"))
return
}
@@ -630,9 +654,9 @@ func (s *cacheServer) serveMissingLayersJSON(ctx context.Context, resp http.Resp
callResponseSent(ctx, s.hooks)
}
func (s *cacheServer) serveMissingLayersProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
func (s *cacheServer) serveMissingBlobsProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "MissingLayers")
ctx = ctxsetters.WithMethodName(ctx, "MissingBlobs")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
@@ -644,17 +668,17 @@ func (s *cacheServer) serveMissingLayersProtobuf(ctx context.Context, resp http.
s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
return
}
reqContent := new(MissingLayersRequest)
reqContent := new(MissingBlobsRequest)
if err = proto.Unmarshal(buf, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
return
}
// Call service method
var respContent *MissingLayersResponse
var respContent *MissingBlobsResponse
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Cache.MissingLayers(ctx, reqContent)
respContent, err = s.Cache.MissingBlobs(ctx, reqContent)
}()
if err != nil {
@@ -662,7 +686,7 @@ func (s *cacheServer) serveMissingLayersProtobuf(ctx context.Context, resp http.
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingLayersResponse and nil error while calling MissingLayers. nil responses are not supported"))
s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingBlobsResponse and nil error while calling MissingBlobs. nil responses are not supported"))
return
}
@@ -674,6 +698,16 @@ func (s *cacheServer) serveMissingLayersProtobuf(ctx context.Context, resp http.
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -832,6 +866,7 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -1084,7 +1119,15 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp)
}
respBodyBytes, err := ioutil.ReadAll(resp.Body)
r := resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
r, err = gzip.NewReader(r)
if err != nil {
return wrapInternal(err, "invalid gzip")
}
}
respBodyBytes, err := ioutil.ReadAll(r)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
@@ -1189,6 +1232,36 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
// compressWithGzip compresses the data with gzip
func compressWithGzip(data []byte) ([]byte, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
defer gz.Close()
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isGZipAcceptable(request *http.Request) bool {
for _, encoding := range request.Header["Accept-Encoding"] {
if encoding == "gzip" {
return true
}
}
return false
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return
@@ -1211,48 +1284,48 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
}
var twirpFileDescriptor0 = []byte{
// 674 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xd3, 0x30,
0x14, 0x56, 0xdb, 0x75, 0x6d, 0x4e, 0xdb, 0x6d, 0x58, 0x6c, 0xcb, 0xba, 0x8b, 0x55, 0x81, 0x49,
0x15, 0x17, 0x89, 0x28, 0x08, 0x71, 0x03, 0x02, 0x06, 0x48, 0x95, 0xf8, 0x99, 0x0c, 0xe2, 0x02,
0x21, 0x55, 0x5e, 0xe2, 0xa4, 0xd6, 0x9a, 0x38, 0xb3, 0x9d, 0xa2, 0x3e, 0x00, 0x6f, 0xc7, 0xbb,
0xf0, 0x0a, 0x28, 0x27, 0xc9, 0xd6, 0x96, 0xf2, 0x77, 0xe7, 0xf3, 0x93, 0xef, 0x7c, 0xe7, 0xfb,
0xec, 0xc0, 0xa1, 0x4a, 0x7d, 0xcf, 0x67, 0xfe, 0x94, 0x7b, 0x9a, 0xab, 0xb9, 0xf0, 0xb9, 0x9b,
0x2a, 0x69, 0x24, 0xd9, 0x31, 0x4a, 0xcc, 0x17, 0x2e, 0x96, 0xdc, 0xf9, 0xfd, 0xfe, 0x49, 0x24,
0x65, 0x34, 0xe3, 0x1e, 0x56, 0x2f, 0xb2, 0xd0, 0x33, 0x22, 0xe6, 0xda, 0xb0, 0x38, 0x2d, 0x3e,
0xe8, 0x3f, 0x8a, 0x84, 0x99, 0x66, 0x17, 0xae, 0x2f, 0x63, 0x8f, 0x5d, 0x65, 0x4c, 0x73, 0x3f,
0x53, 0xc2, 0x2c, 0x3c, 0x04, 0xf2, 0x70, 0x8e, 0x8c, 0x63, 0x99, 0xac, 0x0e, 0xea, 0x1f, 0xaf,
0x03, 0xf3, 0x38, 0x35, 0x8b, 0xa2, 0xe8, 0x7c, 0xab, 0x83, 0x35, 0x8e, 0x59, 0xc4, 0xc7, 0x49,
0x28, 0xc9, 0x29, 0xec, 0x68, 0x7f, 0xca, 0x63, 0x36, 0x99, 0x73, 0xa5, 0x85, 0x4c, 0xec, 0xda,
0xa0, 0x36, 0x6c, 0xd2, 0x5e, 0x91, 0xfd, 0x54, 0x24, 0x89, 0x03, 0x5d, 0xa6, 0xfc, 0xa9, 0x30,
0xdc, 0x37, 0x99, 0xe2, 0x76, 0x7d, 0x50, 0x1b, 0x5a, 0x74, 0x25, 0x47, 0x1e, 0x42, 0xcb, 0x57,
0x9c, 0x19, 0x1e, 0xd8, 0x8d, 0x41, 0x6d, 0xd8, 0x19, 0xf5, 0xdd, 0x82, 0x87, 0x5b, 0xf1, 0x70,
0x3f, 0x56, 0x0b, 0xd2, 0xaa, 0x35, 0x27, 0x10, 0x48, 0xff, 0x92, 0xab, 0x6b, 0x02, 0x5b, 0x88,
0xdd, 0x2b, 0xb2, 0x15, 0x81, 0x1d, 0xa8, 0x4b, 0x6d, 0x37, 0xb1, 0x54, 0x97, 0x9a, 0x3c, 0x83,
0xbd, 0xa9, 0xd0, 0x46, 0xaa, 0xc5, 0x24, 0x65, 0xfe, 0x25, 0x8b, 0xb8, 0xb6, 0xb7, 0x07, 0x8d,
0x61, 0x67, 0xb4, 0xef, 0x96, 0x32, 0xa3, 0x32, 0xee, 0x79, 0x51, 0xa5, 0xbb, 0x65, 0x7b, 0x19,
0x6b, 0x27, 0x84, 0xdd, 0xf3, 0xcc, 0xa0, 0x12, 0x94, 0x5f, 0x65, 0x5c, 0x1b, 0x72, 0x04, 0x6d,
0x91, 0xc7, 0x13, 0x11, 0xa0, 0x0c, 0x16, 0x6d, 0x61, 0x3c, 0x0e, 0xc8, 0x63, 0x80, 0xb2, 0x94,
0x84, 0x12, 0xd7, 0xef, 0x8c, 0x8e, 0xdc, 0x55, 0x43, 0xdd, 0x6b, 0x59, 0xa9, 0x25, 0xaa, 0xa3,
0xf3, 0xbd, 0x0e, 0xd6, 0x1b, 0xb6, 0xe0, 0xea, 0x7f, 0xf4, 0x1e, 0xe0, 0xba, 0xc5, 0x98, 0xbd,
0xd5, 0x85, 0xde, 0x7f, 0x40, 0x01, 0x9e, 0x42, 0xaf, 0x5c, 0x1c, 0x29, 0x69, 0xbb, 0x81, 0xdb,
0x1f, 0x6d, 0xdc, 0x1e, 0x39, 0x75, 0xd3, 0x9b, 0x40, 0x93, 0x27, 0xd0, 0x65, 0x69, 0x3a, 0x13,
0x3e, 0x33, 0x42, 0x26, 0xda, 0xde, 0xda, 0xf4, 0xf9, 0xf3, 0x9b, 0x0e, 0xba, 0xd2, 0x4e, 0x4e,
0xa0, 0x23, 0x53, 0x76, 0x95, 0xf1, 0x49, 0x20, 0x54, 0x6e, 0x4c, 0x63, 0x68, 0x51, 0x28, 0x52,
0x2f, 0x85, 0xd2, 0xf9, 0xa2, 0x5f, 0xf3, 0xbb, 0x21, 0x33, 0x33, 0x09, 0xc5, 0xac, 0xb4, 0xc7,
0xa2, 0xbd, 0x2a, 0xfb, 0x3a, 0x4f, 0x92, 0x03, 0xd8, 0x0e, 0x44, 0xc4, 0xb5, 0xb1, 0x5b, 0x28,
0x78, 0x19, 0x91, 0x43, 0x68, 0x05, 0x22, 0x0c, 0x73, 0x27, 0xda, 0x55, 0x21, 0x0c, 0xc7, 0x81,
0x13, 0xa0, 0x6d, 0x28, 0x68, 0x65, 0xdb, 0x52, 0x6f, 0x6d, 0xb9, 0x37, 0x37, 0x6d, 0x96, 0x37,
0x16, 0xa6, 0x35, 0x36, 0x9b, 0x76, 0xed, 0x0d, 0xb5, 0x66, 0xd5, 0xd1, 0x39, 0x83, 0xce, 0x79,
0x66, 0x28, 0xd7, 0xa9, 0x4c, 0x34, 0x2f, 0xed, 0xa8, 0xfd, 0xc1, 0x0e, 0x02, 0x5b, 0x5c, 0xea,
0x19, 0x5a, 0xd6, 0xa6, 0x78, 0x76, 0xde, 0xc1, 0xed, 0xb7, 0x42, 0x6b, 0x91, 0x44, 0x38, 0x43,
0xff, 0xc3, 0x35, 0x3b, 0x06, 0xab, 0x64, 0x1c, 0xe4, 0xf6, 0xe7, 0x82, 0xb5, 0x0b, 0x56, 0x81,
0x76, 0xa6, 0xb0, 0xbf, 0x86, 0x57, 0xd2, 0xbb, 0x03, 0xbd, 0xb8, 0x28, 0x4c, 0x10, 0x08, 0x51,
0xdb, 0xb4, 0x5b, 0x26, 0xf1, 0x5a, 0x92, 0x7b, 0x70, 0xab, 0x6a, 0x5a, 0x1f, 0xb1, 0x1b, 0x2f,
0xc1, 0x8e, 0x03, 0x3d, 0xfa, 0x51, 0x83, 0xe6, 0x59, 0x2e, 0x10, 0x39, 0x83, 0x76, 0xf5, 0x4a,
0xc8, 0xc9, 0xba, 0x74, 0x6b, 0xef, 0xa7, 0x7f, 0xf0, 0xcb, 0x83, 0x7f, 0x95, 0xff, 0x78, 0x4a,
0x10, 0x44, 0xdf, 0x08, 0xb2, 0xec, 0xe6, 0x6f, 0x41, 0xbe, 0x40, 0x6f, 0x65, 0x7b, 0x72, 0x77,
0x1d, 0x69, 0x93, 0xd8, 0xfd, 0xd3, 0xbf, 0x74, 0x15, 0x12, 0xbe, 0x68, 0x7d, 0x6e, 0x62, 0xc7,
0xc5, 0x36, 0x8e, 0x7d, 0xf0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x08, 0x8d, 0x6f, 0x9e, 0xc6, 0x05,
0x00, 0x00,
// 682 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0xdd, 0x6e, 0xd3, 0x3e,
0x18, 0xc6, 0xd5, 0x76, 0x5d, 0xdb, 0xb7, 0x1f, 0x9b, 0xfc, 0xff, 0xb3, 0x65, 0x05, 0xb1, 0x2a,
0x80, 0x54, 0x4e, 0x52, 0x51, 0x3e, 0xce, 0x40, 0x74, 0x03, 0xa4, 0x1e, 0x20, 0x4a, 0x40, 0x48,
0x70, 0x52, 0x5c, 0xc7, 0x69, 0xad, 0x35, 0x71, 0x66, 0x3b, 0x85, 0xde, 0x01, 0xf7, 0xc6, 0xcd,
0x70, 0x09, 0xc8, 0x76, 0xb2, 0x7e, 0xac, 0x4c, 0x70, 0x16, 0x3f, 0x7e, 0xf3, 0xbc, 0xaf, 0x7f,
0x8f, 0x13, 0x38, 0x16, 0x09, 0xe9, 0x11, 0x4c, 0x66, 0xb4, 0x27, 0xa9, 0x58, 0x30, 0x42, 0xbd,
0x44, 0x70, 0xc5, 0x51, 0x4b, 0x09, 0xb6, 0x58, 0x7a, 0x66, 0xcb, 0x5b, 0x3c, 0x6a, 0x9f, 0x4e,
0x39, 0x9f, 0xce, 0x69, 0xcf, 0xec, 0x4e, 0xd2, 0xb0, 0xa7, 0x58, 0x44, 0xa5, 0xc2, 0x51, 0x62,
0x5f, 0x68, 0x3f, 0x9b, 0x32, 0x35, 0x4b, 0x27, 0x1e, 0xe1, 0x51, 0x0f, 0x5f, 0xa6, 0x58, 0x52,
0x92, 0x0a, 0xa6, 0x96, 0x3d, 0x63, 0xd4, 0x33, 0x7d, 0x78, 0x14, 0xf1, 0x78, 0xb3, 0x51, 0xfb,
0xf6, 0xb6, 0x31, 0x8d, 0x12, 0xb5, 0xb4, 0x9b, 0xee, 0x8f, 0x22, 0x34, 0x06, 0x42, 0xb1, 0x10,
0x13, 0x35, 0x8c, 0x43, 0x8e, 0x1e, 0x40, 0x4b, 0x92, 0x19, 0x8d, 0xf0, 0x78, 0x41, 0x85, 0x64,
0x3c, 0x76, 0x0a, 0x9d, 0x42, 0xb7, 0xec, 0x37, 0xad, 0xfa, 0xc9, 0x8a, 0xc8, 0x85, 0x06, 0x16,
0x64, 0xc6, 0x14, 0x25, 0x2a, 0x15, 0xd4, 0x29, 0x76, 0x0a, 0xdd, 0x9a, 0xbf, 0xa1, 0xa1, 0x27,
0x50, 0x21, 0x82, 0x62, 0x45, 0x03, 0xa7, 0xd4, 0x29, 0x74, 0xeb, 0xfd, 0xb6, 0x67, 0x47, 0xf1,
0xf2, 0x51, 0xbc, 0x8f, 0xf9, 0x19, 0xfd, 0xbc, 0x54, 0x0f, 0x10, 0x70, 0x72, 0x41, 0xc5, 0xd5,
0x00, 0x7b, 0xc6, 0xbb, 0x69, 0xd5, 0x7c, 0x80, 0x16, 0x14, 0xb9, 0x74, 0xca, 0x66, 0xab, 0xc8,
0x25, 0x7a, 0x09, 0x87, 0x33, 0x26, 0x15, 0x17, 0xcb, 0x71, 0x82, 0xc9, 0x05, 0x9e, 0x52, 0xe9,
0xec, 0x77, 0x4a, 0xdd, 0x7a, 0xff, 0x96, 0x97, 0x91, 0x36, 0x70, 0xbc, 0x91, 0xdd, 0xf5, 0x0f,
0xb2, 0xf2, 0x6c, 0x2d, 0xdd, 0xef, 0x80, 0x46, 0xa9, 0xca, 0x61, 0xf8, 0xf4, 0x32, 0xa5, 0x52,
0xa1, 0x53, 0xa8, 0xe3, 0x4c, 0x1a, 0xb3, 0xc0, 0xc0, 0xa8, 0xf9, 0x90, 0x4b, 0xc3, 0x00, 0x0d,
0xa0, 0xb9, 0x2a, 0x88, 0x43, 0x6e, 0x50, 0xd4, 0xfb, 0x77, 0xbc, 0xcd, 0x7c, 0xbd, 0x75, 0xca,
0x1a, 0xd4, 0x6a, 0xe5, 0xfe, 0x2c, 0x42, 0xf5, 0x6c, 0xce, 0x27, 0xff, 0x12, 0x40, 0xc7, 0x9c,
0xdf, 0xf6, 0x3a, 0xdc, 0x3c, 0xe1, 0xbb, 0x0f, 0x86, 0xc8, 0x0b, 0x68, 0x66, 0x24, 0xcc, 0x5c,
0xd2, 0x29, 0x19, 0x1c, 0x27, 0x3b, 0x71, 0xd8, 0xa9, 0x92, 0xd5, 0x42, 0xa2, 0xe7, 0xd0, 0xc0,
0x49, 0x32, 0x67, 0x04, 0x2b, 0xc6, 0x63, 0xe9, 0xec, 0xed, 0x7a, 0x7d, 0xb0, 0xaa, 0xf0, 0x37,
0xca, 0x35, 0x38, 0x9e, 0xe0, 0xcb, 0x94, 0x8e, 0x03, 0x26, 0x74, 0x52, 0x25, 0x0d, 0xce, 0x4a,
0xaf, 0x98, 0x90, 0xfa, 0xa0, 0xdf, 0xf4, 0x65, 0xe1, 0xa9, 0x1a, 0x87, 0x6c, 0x9e, 0xe5, 0x55,
0xf3, 0x9b, 0xb9, 0xfa, 0x46, 0x8b, 0xe8, 0x08, 0xf6, 0x03, 0x36, 0xa5, 0x52, 0x39, 0x15, 0xc3,
0x3e, 0x5b, 0xa1, 0x63, 0xa8, 0x04, 0x2c, 0x0c, 0x75, 0x28, 0xd5, 0x7c, 0x23, 0x0c, 0x87, 0x81,
0xfb, 0x15, 0x5a, 0xa3, 0x54, 0x69, 0x9e, 0x79, 0x86, 0x6b, 0xa5, 0x85, 0xf5, 0x52, 0xf4, 0x14,
0x6a, 0x93, 0x39, 0x9f, 0xd8, 0xdc, 0xec, 0x1d, 0x75, 0xb6, 0x73, 0xcb, 0x83, 0xf1, 0xab, 0x93,
0xec, 0xc9, 0x3d, 0x87, 0xfa, 0x28, 0x55, 0x3e, 0x95, 0x09, 0x8f, 0x25, 0xcd, 0xa2, 0x28, 0xdc,
0x10, 0x05, 0x82, 0x3d, 0xca, 0xe5, 0xdc, 0xc4, 0x55, 0xf5, 0xcd, 0xb3, 0xfb, 0x1e, 0xfe, 0x7b,
0xcb, 0xa4, 0x64, 0xf1, 0x54, 0x77, 0x90, 0x7f, 0x7d, 0xdf, 0x4e, 0xa0, 0x6a, 0x67, 0x0e, 0x74,
0xfc, 0x1a, 0x58, 0xc5, 0x0c, 0x16, 0x48, 0xf7, 0x02, 0xfe, 0xdf, 0xb4, 0xcc, 0x06, 0x7c, 0x08,
0x87, 0x91, 0xd5, 0xc7, 0xb9, 0x91, 0x31, 0xae, 0xfa, 0x07, 0x99, 0x9e, 0x5f, 0x4e, 0xd4, 0x5d,
0x95, 0x6e, 0x75, 0x69, 0x45, 0x2b, 0xeb, 0x61, 0x20, 0xfb, 0xbf, 0x0a, 0x50, 0x3e, 0xd7, 0x90,
0xd0, 0xd0, 0xe0, 0xb8, 0xb2, 0x70, 0xb7, 0x09, 0x5e, 0xff, 0xaa, 0xda, 0x47, 0xd7, 0xfe, 0x04,
0xaf, 0xf5, 0x4f, 0x09, 0x0d, 0xa0, 0x92, 0x65, 0x87, 0xee, 0xee, 0xb0, 0x59, 0x0b, 0xf5, 0x8f,
0x16, 0x9f, 0xa1, 0xb1, 0x0e, 0x01, 0xdd, 0xdb, 0xf6, 0xd9, 0x41, 0xbd, 0x7d, 0xff, 0xe6, 0x22,
0xcb, 0xf1, 0xac, 0xf2, 0xa5, 0x6c, 0x0a, 0x26, 0xfb, 0xa6, 0xe7, 0xe3, 0xdf, 0x01, 0x00, 0x00,
0xff, 0xff, 0xf9, 0x1a, 0xc0, 0xa0, 0xdd, 0x05, 0x00, 0x00,
}

View File

@@ -17,6 +17,7 @@ import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
@@ -28,6 +29,9 @@ import io "io"
import json "encoding/json"
import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// ====================
// OSDetector Interface
// ====================
@@ -320,6 +324,16 @@ func (s *oSDetectorServer) serveDetectProtobuf(ctx context.Context, resp http.Re
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -636,6 +650,16 @@ func (s *libDetectorServer) serveDetectProtobuf(ctx context.Context, resp http.R
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -794,6 +818,7 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -1046,7 +1071,15 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp)
}
respBodyBytes, err := ioutil.ReadAll(resp.Body)
r := resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
r, err = gzip.NewReader(r)
if err != nil {
return wrapInternal(err, "invalid gzip")
}
}
respBodyBytes, err := ioutil.ReadAll(r)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
@@ -1151,6 +1184,36 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
// compressWithGzip compresses the data with gzip
func compressWithGzip(data []byte) ([]byte, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
defer gz.Close()
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isGZipAcceptable(request *http.Request) bool {
for _, encoding := range request.Header["Accept-Encoding"] {
if encoding == "gzip" {
return true
}
}
return false
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return

View File

@@ -23,8 +23,8 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ScanRequest struct {
Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"`
ImageId string `protobuf:"bytes,2,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
LayerIds []string `protobuf:"bytes,3,rep,name=layer_ids,json=layerIds,proto3" json:"layer_ids,omitempty"`
ArtifactId string `protobuf:"bytes,2,opt,name=artifact_id,json=artifactId,proto3" json:"artifact_id,omitempty"`
BlobIds []string `protobuf:"bytes,3,rep,name=blob_ids,json=blobIds,proto3" json:"blob_ids,omitempty"`
Options *ScanOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@@ -63,16 +63,16 @@ func (m *ScanRequest) GetTarget() string {
return ""
}
func (m *ScanRequest) GetImageId() string {
func (m *ScanRequest) GetArtifactId() string {
if m != nil {
return m.ImageId
return m.ArtifactId
}
return ""
}
func (m *ScanRequest) GetLayerIds() []string {
func (m *ScanRequest) GetBlobIds() []string {
if m != nil {
return m.LayerIds
return m.BlobIds
}
return nil
}
@@ -244,28 +244,29 @@ func init() {
func init() { proto.RegisterFile("rpc/scanner/service.proto", fileDescriptor_60d0e837512b18d4) }
var fileDescriptor_60d0e837512b18d4 = []byte{
// 367 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x4d, 0x6b, 0x83, 0x30,
0x18, 0x46, 0x5b, 0x6a, 0x8d, 0x83, 0x95, 0x1c, 0x86, 0x6d, 0xd9, 0x10, 0x4f, 0x65, 0x07, 0x65,
0x0e, 0xb6, 0xfb, 0xa0, 0x87, 0x9e, 0x3a, 0xd2, 0xb1, 0xc3, 0x2e, 0x25, 0xd5, 0x97, 0x2e, 0xa0,
0xc6, 0x26, 0x51, 0x26, 0xfb, 0x1f, 0xfb, 0xbd, 0xc3, 0xc4, 0xc2, 0xda, 0xd1, 0xdb, 0xfb, 0xf1,
0xf8, 0x7c, 0xbc, 0x06, 0x4d, 0x45, 0x95, 0xc6, 0x32, 0xa5, 0x65, 0x09, 0x22, 0x96, 0x20, 0x1a,
0x96, 0x42, 0x54, 0x09, 0xae, 0x38, 0x9e, 0x28, 0xc1, 0x9a, 0x36, 0xea, 0x97, 0x51, 0xf3, 0x30,
0x7b, 0xda, 0x33, 0xf5, 0x59, 0xef, 0xa2, 0x94, 0x17, 0x31, 0x3d, 0xd4, 0x54, 0x42, 0x5a, 0x0b,
0xa6, 0xda, 0x58, 0x23, 0xe3, 0x8e, 0x2a, 0xe5, 0x45, 0xc1, 0xcb, 0x53, 0xa6, 0xf0, 0xc7, 0x42,
0xde, 0x26, 0xa5, 0x25, 0x81, 0x43, 0x0d, 0x52, 0xe1, 0x1b, 0x34, 0x52, 0x54, 0xec, 0x41, 0xf9,
0x56, 0x60, 0x2d, 0x5c, 0xd2, 0x77, 0x78, 0x8a, 0xc6, 0xac, 0xa0, 0x7b, 0xd8, 0xb2, 0xcc, 0xb7,
0xf5, 0xc6, 0xd1, 0xfd, 0x2a, 0xc3, 0x73, 0xe4, 0xe6, 0xb4, 0x05, 0xb1, 0x65, 0x99, 0xf4, 0x07,
0xc1, 0x60, 0xe1, 0x92, 0xb1, 0x1e, 0xac, 0x32, 0x89, 0x9f, 0x91, 0xc3, 0x2b, 0xc5, 0x78, 0x29,
0xfd, 0x61, 0x60, 0x2d, 0xbc, 0xe4, 0x36, 0x3a, 0xf7, 0x1e, 0x75, 0xfa, 0x6b, 0x03, 0x22, 0x47,
0x74, 0x78, 0x6f, 0x7c, 0xf5, 0xf3, 0x4e, 0xa4, 0xa9, 0xf3, 0x72, 0xab, 0xda, 0x0a, 0x7c, 0xcb,
0x88, 0x74, 0x83, 0xb7, 0xb6, 0x82, 0xf0, 0x0b, 0x5d, 0x99, 0x0c, 0xb2, 0xe2, 0xa5, 0x04, 0x1c,
0x20, 0x9b, 0x4b, 0x1d, 0xc0, 0x4b, 0x26, 0xbd, 0x9e, 0x49, 0x1f, 0xad, 0x37, 0xc4, 0xe6, 0x12,
0x63, 0x34, 0x04, 0x2e, 0x73, 0x1d, 0x65, 0x4c, 0x74, 0x8d, 0x13, 0xe4, 0x08, 0x90, 0x75, 0xae,
0x4c, 0x0a, 0x2f, 0xf1, 0xff, 0x5b, 0x25, 0x1a, 0x40, 0x8e, 0xc0, 0xf0, 0x1b, 0x8d, 0xcc, 0xe8,
0xe2, 0xe1, 0x96, 0xe8, 0xba, 0xf3, 0x09, 0x82, 0xee, 0x58, 0xce, 0x14, 0x03, 0xe9, 0xdb, 0x9a,
0x7d, 0x7e, 0x6a, 0xec, 0xfd, 0x0f, 0xa8, 0x25, 0xe7, 0xdf, 0x74, 0x86, 0x75, 0xf4, 0x81, 0x26,
0xd7, 0x75, 0xf2, 0x8a, 0x9c, 0x8d, 0xb1, 0x86, 0x97, 0x68, 0xd8, 0x95, 0xf8, 0xc2, 0x75, 0xfb,
0xbf, 0x3b, 0xbb, 0xbb, 0xb4, 0x36, 0x87, 0x7b, 0x71, 0x3f, 0x9c, 0x7e, 0xb5, 0x1b, 0xe9, 0xf7,
0xf1, 0xf8, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xfb, 0xa8, 0xd9, 0x86, 0x02, 0x00, 0x00,
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
}

View File

@@ -10,10 +10,10 @@ service Scanner {
}
message ScanRequest {
string target = 1; // image name or tar file path
string image_id = 2;
repeated string layer_ids = 3;
ScanOptions options = 4;
string target = 1; // image name or tar file path
string artifact_id = 2;
repeated string blob_ids = 3;
ScanOptions options = 4;
}
message ScanOptions {

View File

@@ -17,6 +17,7 @@ import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
@@ -28,6 +29,9 @@ import io "io"
import json "encoding/json"
import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// =================
// Scanner Interface
// =================
@@ -320,6 +324,16 @@ func (s *scannerServer) serveScanProtobuf(ctx context.Context, resp http.Respons
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -478,6 +492,7 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -730,7 +745,15 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp)
}
respBodyBytes, err := ioutil.ReadAll(resp.Body)
r := resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
r, err = gzip.NewReader(r)
if err != nil {
return wrapInternal(err, "invalid gzip")
}
}
respBodyBytes, err := ioutil.ReadAll(r)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
@@ -835,6 +858,36 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
// compressWithGzip compresses the data with gzip
func compressWithGzip(data []byte) ([]byte, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
defer gz.Close()
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isGZipAcceptable(request *http.Request) bool {
for _, encoding := range request.Header["Accept-Encoding"] {
if encoding == "gzip" {
return true
}
}
return false
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return
@@ -857,28 +910,29 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
}
var twirpFileDescriptor0 = []byte{
// 367 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0x4d, 0x6b, 0x83, 0x30,
0x18, 0x46, 0x5b, 0x6a, 0x8d, 0x83, 0x95, 0x1c, 0x86, 0x6d, 0xd9, 0x10, 0x4f, 0x65, 0x07, 0x65,
0x0e, 0xb6, 0xfb, 0xa0, 0x87, 0x9e, 0x3a, 0xd2, 0xb1, 0xc3, 0x2e, 0x25, 0xd5, 0x97, 0x2e, 0xa0,
0xc6, 0x26, 0x51, 0x26, 0xfb, 0x1f, 0xfb, 0xbd, 0xc3, 0xc4, 0xc2, 0xda, 0xd1, 0xdb, 0xfb, 0xf1,
0xf8, 0x7c, 0xbc, 0x06, 0x4d, 0x45, 0x95, 0xc6, 0x32, 0xa5, 0x65, 0x09, 0x22, 0x96, 0x20, 0x1a,
0x96, 0x42, 0x54, 0x09, 0xae, 0x38, 0x9e, 0x28, 0xc1, 0x9a, 0x36, 0xea, 0x97, 0x51, 0xf3, 0x30,
0x7b, 0xda, 0x33, 0xf5, 0x59, 0xef, 0xa2, 0x94, 0x17, 0x31, 0x3d, 0xd4, 0x54, 0x42, 0x5a, 0x0b,
0xa6, 0xda, 0x58, 0x23, 0xe3, 0x8e, 0x2a, 0xe5, 0x45, 0xc1, 0xcb, 0x53, 0xa6, 0xf0, 0xc7, 0x42,
0xde, 0x26, 0xa5, 0x25, 0x81, 0x43, 0x0d, 0x52, 0xe1, 0x1b, 0x34, 0x52, 0x54, 0xec, 0x41, 0xf9,
0x56, 0x60, 0x2d, 0x5c, 0xd2, 0x77, 0x78, 0x8a, 0xc6, 0xac, 0xa0, 0x7b, 0xd8, 0xb2, 0xcc, 0xb7,
0xf5, 0xc6, 0xd1, 0xfd, 0x2a, 0xc3, 0x73, 0xe4, 0xe6, 0xb4, 0x05, 0xb1, 0x65, 0x99, 0xf4, 0x07,
0xc1, 0x60, 0xe1, 0x92, 0xb1, 0x1e, 0xac, 0x32, 0x89, 0x9f, 0x91, 0xc3, 0x2b, 0xc5, 0x78, 0x29,
0xfd, 0x61, 0x60, 0x2d, 0xbc, 0xe4, 0x36, 0x3a, 0xf7, 0x1e, 0x75, 0xfa, 0x6b, 0x03, 0x22, 0x47,
0x74, 0x78, 0x6f, 0x7c, 0xf5, 0xf3, 0x4e, 0xa4, 0xa9, 0xf3, 0x72, 0xab, 0xda, 0x0a, 0x7c, 0xcb,
0x88, 0x74, 0x83, 0xb7, 0xb6, 0x82, 0xf0, 0x0b, 0x5d, 0x99, 0x0c, 0xb2, 0xe2, 0xa5, 0x04, 0x1c,
0x20, 0x9b, 0x4b, 0x1d, 0xc0, 0x4b, 0x26, 0xbd, 0x9e, 0x49, 0x1f, 0xad, 0x37, 0xc4, 0xe6, 0x12,
0x63, 0x34, 0x04, 0x2e, 0x73, 0x1d, 0x65, 0x4c, 0x74, 0x8d, 0x13, 0xe4, 0x08, 0x90, 0x75, 0xae,
0x4c, 0x0a, 0x2f, 0xf1, 0xff, 0x5b, 0x25, 0x1a, 0x40, 0x8e, 0xc0, 0xf0, 0x1b, 0x8d, 0xcc, 0xe8,
0xe2, 0xe1, 0x96, 0xe8, 0xba, 0xf3, 0x09, 0x82, 0xee, 0x58, 0xce, 0x14, 0x03, 0xe9, 0xdb, 0x9a,
0x7d, 0x7e, 0x6a, 0xec, 0xfd, 0x0f, 0xa8, 0x25, 0xe7, 0xdf, 0x74, 0x86, 0x75, 0xf4, 0x81, 0x26,
0xd7, 0x75, 0xf2, 0x8a, 0x9c, 0x8d, 0xb1, 0x86, 0x97, 0x68, 0xd8, 0x95, 0xf8, 0xc2, 0x75, 0xfb,
0xbf, 0x3b, 0xbb, 0xbb, 0xb4, 0x36, 0x87, 0x7b, 0x71, 0x3f, 0x9c, 0x7e, 0xb5, 0x1b, 0xe9, 0xf7,
0xf1, 0xf8, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xfb, 0xa8, 0xd9, 0x86, 0x02, 0x00, 0x00,
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
}