mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
020c4a3b14 | ||
|
|
2f2d1a908b | ||
|
|
03ad8a3cd0 | ||
|
|
1218e111ef | ||
|
|
78b7529172 | ||
|
|
e2bcb44687 | ||
|
|
a57c27eeec | ||
|
|
926f323a72 | ||
|
|
aa20adb22b | ||
|
|
3e0779a78d |
136
README.md
136
README.md
@@ -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 [34mINFO[0m Updating pipenv Security DB...
|
||||
2019-05-22T19:36:53.406+0200 [34mINFO[0m 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
10
go.mod
@@ -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
126
go.sum
@@ -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=
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
23
integration/data/auth_config/config.yml
Normal file
23
integration/data/auth_config/config.yml
Normal 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."
|
||||
18
integration/data/certs/cert.pem
Normal file
18
integration/data/certs/cert.pem
Normal 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-----
|
||||
28
integration/data/certs/key.pem
Normal file
28
integration/data/certs/key.pem
Normal 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-----
|
||||
112
integration/docker/docker.go
Normal file
112
integration/docker/docker.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +rbuild integration
|
||||
// +build integration
|
||||
|
||||
package integration
|
||||
|
||||
|
||||
346
integration/registry_test.go
Normal file
346
integration/registry_test.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
107
integration/testdata/alpine-310-registry.json.golden
vendored
Normal file
107
integration/testdata/alpine-310-registry.json.golden
vendored
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
467
internal/app.go
467
internal/app.go
@@ -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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
72
internal/artifact/config/config.go
Normal file
72
internal/artifact/config/config.go
Normal 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
|
||||
}
|
||||
249
internal/artifact/config/config_test.go
Normal file
249
internal/artifact/config/config_test.go
Normal 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
36
internal/artifact/fs.go
Normal 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)
|
||||
}
|
||||
50
internal/artifact/image.go
Normal file
50
internal/artifact/image.go
Normal 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)
|
||||
}
|
||||
@@ -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{}
|
||||
36
internal/artifact/repository.go
Normal file
36
internal/artifact/repository.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
96
internal/artifact/wire_gen.go
Normal file
96
internal/artifact/wire_gen.go
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
42
internal/config/artifact.go
Normal file
42
internal/config/artifact.go
Normal 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
|
||||
}
|
||||
80
internal/config/artifact_test.go
Normal file
80
internal/config/artifact_test.go
Normal 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
31
internal/config/db.go
Normal 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
|
||||
}
|
||||
88
internal/config/db_test.go
Normal file
88
internal/config/db_test.go
Normal 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
38
internal/config/global.go
Normal 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
|
||||
}
|
||||
46
internal/config/global_test.go
Normal file
46
internal/config/global_test.go
Normal 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
35
internal/config/image.go
Normal 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
|
||||
}
|
||||
72
internal/config/image_test.go
Normal file
72
internal/config/image_test.go
Normal 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
87
internal/config/report.go
Normal 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
|
||||
}
|
||||
162
internal/config/report_test.go
Normal file
162
internal/config/report_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
16
pkg/cache/remote.go
vendored
@@ -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
|
||||
}
|
||||
|
||||
32
pkg/cache/remote_test.go
vendored
32
pkg/cache/remote_test.go
vendored
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
50
pkg/detector/library/ghsa/advisory.go
Normal file
50
pkg/detector/library/ghsa/advisory.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
58
pkg/detector/library/python/advisory.go
Normal file
58
pkg/detector/library/python/advisory.go
Normal 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, ", ")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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"},
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"`)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
378
rpc/cache/service.pb.go
vendored
378
rpc/cache/service.pb.go
vendored
@@ -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,
|
||||
}
|
||||
|
||||
34
rpc/cache/service.proto
vendored
34
rpc/cache/service.proto
vendored
@@ -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;
|
||||
}
|
||||
|
||||
303
rpc/cache/service.twirp.go
vendored
303
rpc/cache/service.twirp.go
vendored
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user