BREAKING: Trivy DB from GHCR (#1539)

This commit is contained in:
Teppei Fukuda
2022-01-06 20:08:40 +02:00
committed by GitHub
parent db35450bbb
commit e65274e0ef
48 changed files with 849 additions and 1795 deletions

View File

@@ -1,24 +1,24 @@
# Air-Gapped Environment
Trivy can be used in air-gapped environments.
Trivy can be used in air-gapped environments. Note that an allowlist is [here][allowlist].
## Air-Gapped Environment for vulnerabilities
### Download the vulnerability database
At first, you need to download the vulnerability database for use in air-gapped environments.
Go to [trivy-db][trivy-db] and download `trivy-offline.db.tgz` in the latest release.
If you download `trivy-light-offline.db.tgz`, you have to run Trivy with `--light` option.
Please follow [oras installation instruction][oras].
Download `db.tar.gz`:
```
$ wget https://github.com/aquasecurity/trivy-db/releases/latest/download/trivy-offline.db.tgz
$ oras pull ghcr.io/aquasecurity/trivy-db:2 -a
```
### Transfer the DB file into the air-gapped environment
The way of transfer depends on the environment.
```
$ rsync -av -e ssh /path/to/trivy-offline.db.tgz [user]@[host]:dst
$ rsync -av -e ssh /path/to/db.tar.gz [user]@[host]:dst
```
### Put the DB file in Trivy's cache directory
@@ -35,17 +35,10 @@ Put the DB file in the cache directory + `/db`.
```
$ mkdir -p /home/myuser/.cache/trivy/db
$ cd /home/myuser/.cache/trivy/db
$ mv /path/to/trivy-offline.db.tgz .
```
Then, decompress it.
`trivy-offline.db.tgz` file includes two files, `trivy.db` and `metadata.json`.
```
$ tar xvf trivy-offline.db.tgz
$ tar xvf /path/to/db.tar.gz -C /home/myuser/.cache/trivy/db
x trivy.db
x metadata.json
$ rm trivy-offline.db.tgz
$ rm /path/to/db.tar.gz
```
In an air-gapped environment it is your responsibility to update the Trivy database on a regular basis, so that the scanner can detect recently-identified vulnerabilities.
@@ -62,7 +55,8 @@ $ trivy image --skip-update --offline-scan alpine:3.12
### Download misconfiguration policies
At first, you need to download misconfiguration policies for use in air-gapped environments.
Please follow [oras installation instruction][oras]. \
Please follow [oras installation instruction][oras].
Download `bundle.tar.gz`:
```
@@ -115,5 +109,5 @@ In an air-gapped environment, specify `--skip-policy-update` so that Trivy doesn
$ trivy conf --skip-policy-update /path/to/conf
```
[trivy-db]: https://github.com/aquasecurity/trivy-db/releases
[allowlist]: ../getting-started/troubleshooting.md
[oras]: https://oras.land/cli/

View File

@@ -1,4 +1,2 @@
# Integrations
Scan your image automatically as part of your CI workflow, failing the workflow if a vulnerability is found. When you don't want to fail the test, specify `--exit-code 0`.
Since in automated scenarios such as CI/CD you are only interested in the end result, and not the full report, use the `--light` flag to optimize for this scenario and get fast results.

View File

@@ -24,7 +24,6 @@ OPTIONS:
--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 timeout (default: 5m0s) [$TRIVY_TIMEOUT]
--light light mode: it's faster, but vulnerability descriptions and references are not displayed (default: false) [$TRIVY_LIGHT]
--ignore-policy value specify the Rego file to evaluate each vulnerability [$TRIVY_IGNORE_POLICY]
--list-all-pkgs enabling the option will output all packages regardless of vulnerability (default: false) [$TRIVY_LIST_ALL_PKGS]
--offline-scan do not issue API requests to identify dependencies (default: false) [$TRIVY_OFFLINE_SCAN]

View File

@@ -69,11 +69,17 @@ Reference : [boltdb: Opening a database][boltdb].
!!! error
FATAL failed to download vulnerability DB
If trivy is running behind corporate firewall try to whitelist urls below:
If trivy is running behind corporate firewall, you have to add the following urls to your allowlist.
- api.github.com
- github.com
- github-releases.githubusercontent.com
- ghcr.io
- pkg-containers.githubusercontent.com
### Old DB schema
!!! error
--skip-update cannot be specified with the old DB schema.
Trivy v0.23.0 or later requires Trivy DB v2. Please update your local database or follow [the instruction of air-gapped environment][air-gapped].
## Homebrew
### Scope error
@@ -123,3 +129,5 @@ Try again with `--reset` option:
```
$ trivy image --reset
```
[air-gapped]: ../advanced/air-gap.md

View File

@@ -36,39 +36,3 @@ This is useful to initialize workers in Continuous Integration systems.
```
$ trivy image --download-db-only
```
## Lightweight DB
The lightweight DB doesn't contain vulnerability detail such as descriptions and references. Because of that, the size of the DB is smaller and the download is faster.
This option is useful when you don't need vulnerability details and is suitable for CI/CD.
To find the additional information, you can search vulnerability details on the NVD website.
https://nvd.nist.gov/vuln/search
```
$ trivy image --light alpine:3.10
```
`--light` option doesn't display titles like the following example.
<details>
<summary>Result</summary>
```
2019-11-14T10:21:01.553+0200 INFO Reopening vulnerability DB
2019-11-14T10:21:02.574+0200 INFO Detecting Alpine vulnerabilities...
alpine:3.10 (alpine 3.10.2)
===========================
Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 2, HIGH: 0, CRITICAL: 0)
+---------+------------------+----------+-------------------+---------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
+---------+------------------+----------+-------------------+---------------+
| openssl | CVE-2019-1549 | MEDIUM | 1.1.1c-r0 | 1.1.1d-r0 |
+ +------------------+ + + +
| | CVE-2019-1563 | | | |
+ +------------------+----------+ + +
| | CVE-2019-1547 | LOW | | |
+---------+------------------+----------+-------------------+---------------+
```
</details>

4
go.mod
View File

@@ -13,7 +13,7 @@ require (
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b
github.com/aquasecurity/trivy-db v0.0.0-20220104200459-525690bf08ef
github.com/caarlos0/env/v6 v6.0.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cheggaaa/pb/v3 v3.0.3
@@ -24,7 +24,6 @@ require (
github.com/goccy/go-yaml v1.8.2 // indirect
github.com/golang/protobuf v1.5.2
github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475
github.com/google/go-github/v33 v33.0.0
github.com/google/wire v0.4.0
github.com/hashicorp/go-getter v1.5.2
github.com/hashicorp/go-hclog v0.15.0 // indirect
@@ -43,7 +42,6 @@ require (
github.com/twitchtv/twirp v8.1.0+incompatible
github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.19.1
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.27.1
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect

135
go.sum
View File

@@ -30,6 +30,7 @@ cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAV
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
@@ -42,7 +43,9 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/errorreporting v0.1.0/go.mod h1:cZSiBMvrnl0X13pD9DwKf9sQ8Eqy3EzHqkyKBZxiIrM=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -60,6 +63,7 @@ contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8=
@@ -122,6 +126,7 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64=
github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
@@ -174,8 +179,10 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
@@ -195,6 +202,7 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs=
github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
@@ -231,17 +239,17 @@ github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516 h1:moQmzbp
github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516/go.mod h1:gTd97VdQ0rg8Mkiic3rPgNOQdprZ7feTAhiD5mGQjgM=
github.com/aquasecurity/tfsec v0.63.1 h1:KH63HTcUoab7d3PKtqFO6T8K5AY7bzLw7Kiu+EY9U64=
github.com/aquasecurity/tfsec v0.63.1/go.mod h1:g5ZWmsfqW1FsCaPb9ux8Pzjcyss/WUB2XuRd5slqvnc=
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b h1:RaS93vlHzgreZk3CYqcNgoqukwbsBEYhAiE6qmhLwB0=
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b/go.mod h1:5h8GV7Qxp/SMJ4awWHs0KRxwVkKzcwOnRkORWOnCXRU=
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/aquasecurity/trivy-db v0.0.0-20220104200459-525690bf08ef h1:E8ihL2Rh5aceXzexoCgKQVFzWQUQ56TwtITEOu4IuNE=
github.com/aquasecurity/trivy-db v0.0.0-20220104200459-525690bf08ef/go.mod h1:nT/y6Nbo7KDfRhezh3uE8nLNV5R13mGw34j6E8htL+o=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
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/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
@@ -251,10 +259,12 @@ github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.42.0 h1:BMZws0t8NAhHFsfnT3B40IwD13jVDG5KerlRksctVIw=
github.com/aws/aws-sdk-go v1.42.0/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -294,6 +304,7 @@ github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGr
github.com/caarlos0/env/v6 v6.0.0 h1:NZt6FAoB8ieKO5lEwRdwCzYxWFx7ZYF2R7UcoyaWtyc=
github.com/caarlos0/env/v6 v6.0.0/go.mod h1:+wdyOmtjoZIW2GJOc2OYa5NoOFuWD/bIpWqm30NgtRk=
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
@@ -319,6 +330,7 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -330,6 +342,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@@ -529,14 +542,14 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
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.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -558,6 +571,8 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -590,6 +605,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -597,6 +613,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
@@ -657,6 +674,7 @@ github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQq
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
github.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
@@ -760,14 +778,11 @@ github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOt
github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw=
github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475 h1:da8EHPcyjqM4dHLhPqtY48YUj9ATT1ugRyi4g+MdITM=
github.com/google/go-containerregistry v0.7.1-0.20211214010025-a65b7844a475/go.mod h1:IwJblnDNiCs8sxubbfPNniYsUqr8m+nt7YbPzecsGuE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
github.com/google/go-github/v38 v38.1.0/go.mod h1:cStvrz/7nFr0FoENgG6GLbp53WaelXucT+BBz/3VKx4=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@@ -797,6 +812,7 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -813,8 +829,9 @@ github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
@@ -855,7 +872,9 @@ github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFi
github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY=
github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
@@ -910,6 +929,7 @@ github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09Uny
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@@ -921,11 +941,13 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea/go.mod h1:QMdK4dGB3YhEW2BmA1wgGpPYI3HZy/5gD705PXKUVSg=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jba/templatecheck v0.6.0/go.mod h1:/1k7EajoSErFI9GLHAsiIJEaNLt3ALKNw2TV7z2SYv4=
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/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@@ -1017,8 +1039,11 @@ github.com/liamg/tml v0.4.0/go.mod h1:0h4EAV/zBOsqI91EWONedjRpO8O0itjGJVd+wG5eC+
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -1052,7 +1077,6 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed/go.mod h1:SDJ4hurDYyQ9/7nc+eCYtXqdufgK4Cq9TJlwPklqEYA=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -1133,12 +1157,21 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@@ -1200,17 +1233,23 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/owenrumney/go-sarif v1.0.10/go.mod h1:sgJM0ZaZ28jT8t8Iq3/mUCFBW9cX09EobIBXYOhiYBc=
github.com/owenrumney/go-sarif v1.0.12/go.mod h1:Jk5smXU9QuCqTdh4N3PehnG+azzrf0XcQ267ZwAG8Ho=
github.com/owenrumney/squealer v0.2.28 h1:LYsqUHal+5QlANjbZ+h44SN5kIZSfHCWKUzBAS1KwB0=
github.com/owenrumney/squealer v0.2.28/go.mod h1:wwVPzhjiUBILIdDtnzGSEcapXczIj/tONP+ZJ49IhPY=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -1218,15 +1257,19 @@ github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bA
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
@@ -1242,6 +1285,7 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -1249,6 +1293,7 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
@@ -1257,6 +1302,7 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
@@ -1286,10 +1332,11 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -1300,6 +1347,7 @@ github.com/ryancurrah/gomodguard v1.0.4/go.mod h1:9T/Cfuxs5StfsocWr4WzDL36HqnX0f
github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
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/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
@@ -1319,7 +1367,6 @@ github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
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.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -1336,6 +1383,7 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:s
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sosedoff/gitkit v0.3.0 h1:TfINVRNUM+GcFa+LGhZ3RcWN86Im1M6i8qs0IsgMy90=
github.com/sosedoff/gitkit v0.3.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9KtkUYTKT5I=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
@@ -1368,6 +1416,9 @@ github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfD
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -1474,6 +1525,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg=
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
@@ -1505,6 +1557,8 @@ go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1517,20 +1571,26 @@ go.opentelemetry.io/contrib v0.21.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.21.0/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.21.0/go.mod h1:a9cocRplhIBkUAJmak+BPDx+LVL7cTmqUPB0uBcTA4k=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC1/go.mod h1:FXJnjGCoTQL6nQ8OpFJ0JI1DrdOvMoVx49ic0Hg4+D4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.0-RC1/go.mod h1:FliQjImlo7emZVjixV8nbDMAa4iAkcWTE9zzSEOiEPw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0-RC1/go.mod h1:cDwRc2Jrh5Gku1peGK8p9rRuX/Uq2OtVmLicjlw2WYU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.0.0-RC1/go.mod h1:OYKzEoxgXFvehW7X12WYT4/a2BlASJK9l7RtG4A91fg=
go.opentelemetry.io/otel/internal/metric v0.21.0/go.mod h1:iOfAaY2YycsXfYD4kaRSbLx2LKmfpKObWBEv9QK5zFo=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/metric v0.21.0/go.mod h1:JWCt1bjivC4iCrz/aCrM1GSw+ZcvY44KCbaeeRhzHnc=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk v1.0.0-RC1/go.mod h1:kj6yPn7Pgt5ByRuwesbaWcRLA+V7BSDg3Hf8xRvsvf8=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1539,11 +1599,13 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
@@ -1577,13 +1639,15 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -1591,6 +1655,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1609,17 +1674,20 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1685,6 +1753,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0=
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1708,6 +1777,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
@@ -1774,6 +1844,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1831,14 +1902,19 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
@@ -1913,6 +1989,8 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200102140908-9497f49d5709/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1953,6 +2031,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/vuln v0.0.0-20211215213114-5e054cb3e47e/go.mod h1:9qJmykHjqtHdZkMBTPU2esbdS/2fuYYj9Lsb5BLHlPE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1967,6 +2048,7 @@ google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
@@ -1999,8 +2081,11 @@ google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNe
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.60.0 h1:eq/zs5WPH4J9undYM9IP1O7dSr7Yh8Y0GtSCpzGzIUk=
google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2076,7 +2161,11 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -2084,9 +2173,11 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
google.golang.org/grpc v1.16.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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
@@ -2144,6 +2235,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gcfg.v1 v1.2.3/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.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
@@ -2153,7 +2245,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
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=
@@ -2194,6 +2285,7 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
k8s.io/api v0.0.0-20180904230853-4e7be11eab3f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
@@ -2252,11 +2344,11 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY=
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
@@ -2272,5 +2364,6 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4=

View File

@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/report"
)
@@ -45,9 +46,8 @@ func initDB(t *testing.T) string {
f, err := os.Create(metadataFile)
require.NoError(t, err)
err = json.NewEncoder(f).Encode(db.Metadata{
Version: 1,
Type: 1,
err = json.NewEncoder(f).Encode(metadata.Metadata{
Version: db.SchemaVersion,
NextUpdate: time.Now().Add(24 * time.Hour),
UpdatedAt: time.Now(),
})

View File

@@ -8,16 +8,14 @@ import (
"strings"
"time"
"github.com/spf13/afero"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/commands/artifact"
"github.com/aquasecurity/trivy/pkg/commands/client"
"github.com/aquasecurity/trivy/pkg/commands/plugin"
"github.com/aquasecurity/trivy/pkg/commands/server"
tdb "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/types"
@@ -26,8 +24,8 @@ import (
// VersionInfo holds the trivy DB version Info
type VersionInfo struct {
Version string `json:",omitempty"`
VulnerabilityDB *db.Metadata `json:",omitempty"`
Version string `json:",omitempty"`
VulnerabilityDB *metadata.Metadata `json:",omitempty"`
}
var (
@@ -183,9 +181,10 @@ var (
EnvVars: []string{"TRIVY_TIMEOUT"},
}
// TODO: remove this flag after a sufficient deprecation period.
lightFlag = cli.BoolFlag{
Name: "light",
Usage: "light mode: it's faster, but vulnerability descriptions and references are not displayed",
Usage: "deprecated",
EnvVars: []string{"TRIVY_LIGHT"},
}
@@ -405,16 +404,16 @@ func setHidden(flags []cli.Flag, hidden bool) []cli.Flag {
}
func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer) {
var dbMeta *db.Metadata
var dbMeta *metadata.Metadata
metadata, _ := tdb.NewMetadata(afero.NewOsFs(), cacheDir).Get() // nolint: errcheck
if !metadata.UpdatedAt.IsZero() && !metadata.NextUpdate.IsZero() && metadata.Version != 0 {
dbMeta = &db.Metadata{
Version: metadata.Version,
Type: metadata.Type,
NextUpdate: metadata.NextUpdate.UTC(),
UpdatedAt: metadata.UpdatedAt.UTC(),
DownloadedAt: metadata.DownloadedAt.UTC(),
mc := metadata.NewClient(cacheDir)
meta, _ := mc.Get() // nolint: errcheck
if !meta.UpdatedAt.IsZero() && !meta.NextUpdate.IsZero() && meta.Version != 0 {
dbMeta = &metadata.Metadata{
Version: meta.Version,
NextUpdate: meta.NextUpdate.UTC(),
UpdatedAt: meta.UpdatedAt.UTC(),
DownloadedAt: meta.DownloadedAt.UTC(),
}
}
@@ -428,20 +427,12 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
default:
output := fmt.Sprintf("Version: %s\n", version)
if dbMeta != nil {
var dbType string
switch dbMeta.Type {
case db.TypeFull:
dbType = "Full"
case db.TypeLight:
dbType = "Light"
}
output += fmt.Sprintf(`Vulnerability DB:
Type: %s
Version: %d
UpdatedAt: %s
NextUpdate: %s
DownloadedAt: %s
`, dbType, dbMeta.Version, dbMeta.UpdatedAt.UTC(), dbMeta.NextUpdate.UTC(), dbMeta.DownloadedAt.UTC())
`, dbMeta.Version, dbMeta.UpdatedAt.UTC(), dbMeta.NextUpdate.UTC(), dbMeta.DownloadedAt.UTC())
}
fmt.Fprintf(outputWriter, output)
}
@@ -587,6 +578,7 @@ func NewClientCommand() *cli.Command {
&securityChecksFlag,
&ignoreFileFlag,
&timeoutFlag,
&noProgressFlag,
&ignorePolicy,
stringSliceFlag(skipFiles),
stringSliceFlag(skipDirs),

View File

@@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
)
func Test_showVersion(t *testing.T) {
@@ -35,7 +35,6 @@ func Test_showVersion(t *testing.T) {
},
expectedOutput: `Version: v1.2.3
Vulnerability DB:
Type: Full
Version: 42
UpdatedAt: 2020-03-16 23:40:20 +0000 UTC
NextUpdate: 2020-03-16 23:57:00 +0000 UTC
@@ -49,7 +48,7 @@ Vulnerability DB:
outputFormat: "json",
version: "1.2.3",
},
expectedOutput: `{"Version":"1.2.3","VulnerabilityDB":{"Version":42,"Type":1,"NextUpdate":"2020-03-16T23:57:00Z","UpdatedAt":"2020-03-16T23:40:20Z","DownloadedAt":"2020-03-16T23:40:20Z"}}
expectedOutput: `{"Version":"1.2.3","VulnerabilityDB":{"Version":42,"NextUpdate":"2020-03-16T23:57:00Z","UpdatedAt":"2020-03-16T23:40:20Z","DownloadedAt":"2020-03-16T23:40:20Z"}}
`,
createDB: true,
},
@@ -90,9 +89,8 @@ Vulnerability DB:
require.NoError(t, err)
metadataFile := filepath.Join(cacheDir, "db", "metadata.json")
b, err := json.Marshal(db.Metadata{
b, err := json.Marshal(metadata.Metadata{
Version: 42,
Type: 1,
NextUpdate: time.Unix(1584403020, 0),
UpdatedAt: time.Unix(1584402020, 0),
DownloadedAt: time.Unix(1584402020, 0),

View File

@@ -81,7 +81,6 @@ func runWithTimeout(ctx context.Context, opt Option, initializeScanner Initializ
Output: opt.Output,
Severities: opt.Severities,
OutputTemplate: opt.Template,
Light: opt.Light,
IncludeNonFailures: opt.IncludeNonFailures,
Trace: opt.Trace,
}); err != nil {
@@ -121,7 +120,7 @@ func initFSCache(c Option) (cache.Cache, error) {
func initDB(c Option) error {
// download the database file
noProgress := c.Quiet || c.NoProgress
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.Light, c.SkipDBUpdate); err != nil {
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.SkipDBUpdate); err != nil {
return err
}
@@ -185,7 +184,8 @@ func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner,
// ScannerOptions is filled only when config scanning is enabled.
var configScannerOptions config.ScannerOption
if utils.StringInSlice(types.SecurityCheckConfig, opt.SecurityChecks) {
builtinPolicyPaths, err := operation.InitBuiltinPolicies(ctx, opt.SkipPolicyUpdate)
noProgress := opt.Quiet || opt.NoProgress
builtinPolicyPaths, err := operation.InitBuiltinPolicies(ctx, opt.CacheDir, noProgress, opt.SkipPolicyUpdate)
if err != nil {
return pkgReport.Report{}, xerrors.Errorf("failed to initialize built-in policies: %w", err)
}

View File

@@ -19,6 +19,9 @@ type Option struct {
option.ReportOption
option.ConfigOption
// For policy downloading
NoProgress bool
// We don't want to allow disabled analyzers to be passed by users,
// but it differs depending on scanning modes.
DisabledAnalyzers []analyzer.Type
@@ -44,6 +47,7 @@ func NewOption(c *cli.Context) (Option, error) {
ImageOption: option.NewImageOption(c),
ReportOption: option.NewReportOption(c),
ConfigOption: option.NewConfigOption(c),
NoProgress: c.Bool("no-progress"),
RemoteAddr: c.String("remote"),
token: c.String("token"),
tokenHeader: c.String("token-header"),

View File

@@ -89,7 +89,6 @@ func runWithTimeout(ctx context.Context, opt Option) error {
Output: opt.Output,
Severities: opt.Severities,
OutputTemplate: opt.Template,
Light: false,
IncludeNonFailures: opt.IncludeNonFailures,
Trace: opt.Trace,
}); err != nil {
@@ -143,7 +142,8 @@ func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func()
// ScannerOptions is filled only when config scanning is enabled.
var configScannerOptions config.ScannerOption
if utils.StringInSlice(types.SecurityCheckConfig, opt.SecurityChecks) {
builtinPolicyPaths, err := operation.InitBuiltinPolicies(ctx, opt.SkipPolicyUpdate)
noProgress := opt.Quiet || opt.NoProgress
builtinPolicyPaths, err := operation.InitBuiltinPolicies(ctx, opt.CacheDir, noProgress, opt.SkipPolicyUpdate)
if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("failed to initialize default policies: %w", err)
}

View File

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

View File

@@ -7,10 +7,10 @@ import (
"github.com/go-redis/redis/v8"
"github.com/google/wire"
"github.com/spf13/afero"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/policy"
@@ -77,10 +77,10 @@ func (c Cache) ClearArtifacts() error {
}
// DownloadDB downloads the DB
func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
client := initializeDBClient(cacheDir, quiet)
func DownloadDB(appVersion, cacheDir string, quiet, skipUpdate bool) error {
client := db.NewClient(cacheDir, quiet)
ctx := context.Background()
needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
needsUpdate, err := client.NeedsUpdate(appVersion, skipUpdate)
if err != nil {
return xerrors.Errorf("database error: %w", err)
}
@@ -88,12 +88,9 @@ func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
if needsUpdate {
log.Logger.Info("Need to update DB")
log.Logger.Info("Downloading DB...")
if err = client.Download(ctx, cacheDir, light); err != nil {
if err = client.Download(ctx, cacheDir); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
if err = client.UpdateMetadata(cacheDir); err != nil {
return xerrors.Errorf("unable to update database metadata: %w", err)
}
}
// for debug
@@ -104,8 +101,8 @@ func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
}
// InitBuiltinPolicies downloads the built-in policies and loads them
func InitBuiltinPolicies(ctx context.Context, skipUpdate bool) ([]string, error) {
client, err := policy.NewClient()
func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate bool) ([]string, error) {
client, err := policy.NewClient(cacheDir, quiet)
if err != nil {
return nil, xerrors.Errorf("policy client error: %w", err)
}
@@ -138,12 +135,12 @@ func InitBuiltinPolicies(ctx context.Context, skipUpdate bool) ([]string, error)
}
func showDBInfo(cacheDir string) error {
m := db.NewMetadata(afero.NewOsFs(), cacheDir)
metadata, err := m.Get()
m := metadata.NewClient(cacheDir)
meta, err := m.Get()
if err != nil {
return xerrors.Errorf("something wrong with DB: %w", err)
}
log.Logger.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s, DownloadedAt: %s",
metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate, metadata.DownloadedAt)
log.Logger.Debugf("DB Schema: %d, UpdatedAt: %s, NextUpdate: %s, DownloadedAt: %s",
meta.Version, meta.UpdatedAt, meta.NextUpdate, meta.DownloadedAt)
return nil
}

View File

@@ -1,29 +0,0 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//go:build !wireinject
// +build !wireinject
package operation
import (
db2 "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/spf13/afero"
"k8s.io/utils/clock"
)
// Injectors from inject.go:
func initializeDBClient(cacheDir string, quiet bool) db.Client {
config := db2.Config{}
client := github.NewClient()
progressBar := indicator.NewProgressBar(quiet)
realClock := clock.RealClock{}
fs := afero.NewOsFs()
metadata := db.NewMetadata(fs, cacheDir)
dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
return dbClient
}

View File

@@ -3,6 +3,8 @@ package option
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/log"
)
// DBOption holds the options for trivy DB
@@ -30,5 +32,8 @@ func (c *DBOption) Init() (err error) {
if c.SkipDBUpdate && c.DownloadDBOnly {
return xerrors.New("--skip-db-update and --download-db-only options can not be specified both")
}
if c.Light {
log.Logger.Warn("'--light' option is deprecated and will be removed")
}
return nil
}

View File

@@ -40,7 +40,7 @@ func run(c Config) (err error) {
}
// download the database file
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, true, false, c.SkipDBUpdate); err != nil {
if err = operation.DownloadDB(c.AppVersion, c.CacheDir, true, c.SkipDBUpdate); err != nil {
return err
}

View File

@@ -1,268 +1,181 @@
package db
import (
"compress/gzip"
"context"
"encoding/json"
"io"
"os"
"path/filepath"
"fmt"
"time"
"github.com/google/wire"
"github.com/spf13/afero"
"github.com/aquasecurity/trivy/pkg/oci"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
fullDB = "trivy.db.gz"
lightDB = "trivy-light.db.gz"
metadataFile = "metadata.json"
gb = 1024 * 1024 * 1024
)
// SuperSet binds the dependencies
var SuperSet = wire.NewSet(
// indicator.ProgressBar
indicator.NewProgressBar,
// clock.Clock
wire.Struct(new(clock.RealClock)),
wire.Bind(new(clock.Clock), new(clock.RealClock)),
// db.Config
wire.Struct(new(db.Config)),
wire.Bind(new(dbOperation), new(db.Config)),
// github.Client
github.NewClient,
wire.Bind(new(github.Operation), new(github.Client)),
// Metadata
afero.NewOsFs,
NewMetadata,
// db.Client
NewClient,
wire.Bind(new(Operation), new(Client)),
dbRepository = "ghcr.io/aquasecurity/trivy-db"
dbMediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip"
)
// Operation defines the DB operations
type Operation interface {
NeedsUpdate(cliVersion string, light, skip bool) (need bool, err error)
Download(ctx context.Context, cacheDir string, light bool) (err error)
UpdateMetadata(cacheDir string) (err error)
NeedsUpdate(cliVersion string, skip bool) (need bool, err error)
Download(ctx context.Context, dst string) (err error)
}
type dbOperation interface {
GetMetadata() (metadata db.Metadata, err error)
StoreMetadata(metadata db.Metadata, dir string) (err error)
type options struct {
artifact *oci.Artifact
clock clock.Clock
}
// Option is a functional option
type Option func(*options)
// WithOCIArtifact takes an OCI artifact
func WithOCIArtifact(art *oci.Artifact) Option {
return func(opts *options) {
opts.artifact = art
}
}
// WithClock takes a clock
func WithClock(clock clock.Clock) Option {
return func(opts *options) {
opts.clock = clock
}
}
// Client implements DB operations
type Client struct {
dbc dbOperation
githubClient github.Operation
pb indicator.ProgressBar
clock clock.Clock
metadata Metadata
*options
cacheDir string
metadata metadata.Client
quiet bool
}
// NewClient is the factory method for DB client
func NewClient(dbc dbOperation, githubClient github.Operation, pb indicator.ProgressBar, clock clock.Clock, metadata Metadata) Client {
return Client{
dbc: dbc,
githubClient: githubClient,
pb: pb,
clock: clock,
metadata: metadata,
func NewClient(cacheDir string, quiet bool, opts ...Option) *Client {
o := &options{
clock: clock.RealClock{},
}
for _, opt := range opts {
opt(o)
}
return &Client{
options: o,
cacheDir: cacheDir,
metadata: metadata.NewClient(cacheDir),
quiet: quiet,
}
}
// NeedsUpdate check is DB needs update
func (c Client) NeedsUpdate(cliVersion string, light, skip bool) (bool, error) {
dbType := db.TypeFull
if light {
dbType = db.TypeLight
}
metadata, err := c.metadata.Get()
func (c *Client) NeedsUpdate(cliVersion string, skip bool) (bool, error) {
meta, err := c.metadata.Get()
if err != nil {
log.Logger.Debugf("There is no valid metadata file: %s", err)
if skip {
log.Logger.Error("The first run cannot skip downloading DB")
return false, xerrors.New("--skip-update cannot be specified on the first run")
}
metadata = db.Metadata{} // suppress a warning
meta = metadata.Metadata{Version: db.SchemaVersion}
}
if db.SchemaVersion < metadata.Version {
if db.SchemaVersion < meta.Version {
log.Logger.Errorf("Trivy version (%s) is old. Update to the latest version.", cliVersion)
return false, xerrors.Errorf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
metadata.Version, db.SchemaVersion)
meta.Version, db.SchemaVersion)
}
if skip {
if err = c.validate(dbType, metadata); err != nil {
return false, err
if err = c.validate(meta); err != nil {
return false, xerrors.Errorf("validate error: %w", err)
}
return false, nil
}
if db.SchemaVersion != metadata.Version || metadata.Type != dbType {
if db.SchemaVersion != meta.Version {
return true, nil
}
return !c.isNewDB(metadata), nil
return !c.isNewDB(meta), nil
}
func (c Client) validate(dbType db.Type, metadata db.Metadata) error {
if db.SchemaVersion != metadata.Version {
log.Logger.Error("The local DB is old and needs to be updated")
return xerrors.New("--skip-update cannot be specified with the old DB")
} else if metadata.Type != dbType {
if dbType == db.TypeFull {
log.Logger.Error("The local DB is a lightweight DB. You have to download a full DB")
} else {
log.Logger.Error("The local DB is a full DB. You have to download a lightweight DB")
}
return xerrors.New("--skip-update cannot be specified with the different schema DB")
func (c *Client) validate(meta metadata.Metadata) error {
if db.SchemaVersion != meta.Version {
log.Logger.Error("The local DB has an old schema version which is not supported by the current version of Trivy CLI. It needs to be updated.")
return xerrors.New("--skip-update cannot be specified with the old DB schema")
}
return nil
}
func (c Client) isNewDB(metadata db.Metadata) bool {
if c.clock.Now().Before(metadata.NextUpdate) {
log.Logger.Debug("DB update was skipped because DB is the latest")
func (c *Client) isNewDB(meta metadata.Metadata) bool {
if c.clock.Now().Before(meta.NextUpdate) {
log.Logger.Debug("DB update was skipped because the local DB is the latest")
return true
}
if c.clock.Now().Before(metadata.DownloadedAt.Add(time.Hour)) {
log.Logger.Debug("DB update was skipped because DB was downloaded during the last hour")
if c.clock.Now().Before(meta.DownloadedAt.Add(time.Hour)) {
log.Logger.Debug("DB update was skipped because the local DB was downloaded during the last hour")
return true
}
return false
}
// Download downloads the DB file
func (c Client) Download(ctx context.Context, cacheDir string, light bool) error {
// Remove the metadata file before downloading DB
func (c *Client) Download(ctx context.Context, dst string) error {
// Remove the metadata file under the cache directory before downloading DB
if err := c.metadata.Delete(); err != nil {
log.Logger.Debug("no metadata file")
}
dbFile := fullDB
if light {
dbFile = lightDB
if err := c.populateOCIArtifact(); err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
rc, size, err := c.githubClient.DownloadDB(ctx, dbFile)
if err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
defer rc.Close()
bar := c.pb.Start(int64(size))
barReader := bar.NewProxyReader(rc)
defer bar.Finish()
gr, err := gzip.NewReader(barReader)
if err != nil {
return xerrors.Errorf("invalid gzip file: %w", err)
if err := c.artifact.Download(ctx, db.Dir(dst)); err != nil {
return xerrors.Errorf("database download error: %w", err)
}
dbPath := db.Path(cacheDir)
dbDir := filepath.Dir(dbPath)
if err = os.MkdirAll(dbDir, 0700); err != nil {
return xerrors.Errorf("failed to mkdir: %w", err)
if err := c.updateDownloadedAt(dst); err != nil {
return xerrors.Errorf("failed to update downloaded_at: %w", err)
}
file, err := os.Create(dbPath)
if err != nil {
return xerrors.Errorf("unable to open DB file: %w", err)
}
defer file.Close()
limited := io.LimitReader(gr, 2*gb)
if _, err = io.Copy(file, limited); err != nil {
return xerrors.Errorf("failed to save DB file: %w", err)
}
return nil
}
// UpdateMetadata updates the DB metadata
func (c Client) UpdateMetadata(cacheDir string) error {
func (c *Client) updateDownloadedAt(dst string) error {
log.Logger.Debug("Updating database metadata...")
// make sure the DB has been successfully downloaded
if err := db.Init(cacheDir); err != nil {
return xerrors.Errorf("DB error: %w", err)
}
defer db.Close()
metadata, err := c.dbc.GetMetadata()
// We have to initialize a metadata client here
// since the destination may be different from the cache directory.
client := metadata.NewClient(dst)
meta, err := client.Get()
if err != nil {
return xerrors.Errorf("unable to get metadata: %w", err)
}
metadata.DownloadedAt = c.clock.Now().UTC()
if err = c.dbc.StoreMetadata(metadata, filepath.Join(cacheDir, "db")); err != nil {
return xerrors.Errorf("failed to store metadata: %w", err)
meta.DownloadedAt = c.clock.Now().UTC()
if err = client.Update(meta); err != nil {
return xerrors.Errorf("failed to update metadata: %w", err)
}
return nil
}
// Metadata defines the file meta
type Metadata struct { // TODO: Move all Metadata things to trivy-db repo
fs afero.Fs
filePath string
}
// NewMetadata is the factory method for file Metadata
func NewMetadata(fs afero.Fs, cacheDir string) Metadata {
filePath := MetadataPath(cacheDir)
return Metadata{
fs: fs,
filePath: filePath,
}
}
// MetadataPath returns the metaData file path
func MetadataPath(cacheDir string) string {
dbPath := db.Path(cacheDir)
dbDir := filepath.Dir(dbPath)
return filepath.Join(dbDir, metadataFile)
}
// Delete deletes the file of database metadata
func (m Metadata) Delete() error {
if err := m.fs.Remove(m.filePath); err != nil {
return xerrors.Errorf("unable to remove the metadata file: %w", err)
func (c *Client) populateOCIArtifact() error {
if c.artifact == nil {
repo := fmt.Sprintf("%s:%d", dbRepository, db.SchemaVersion)
art, err := oci.NewArtifact(repo, dbMediaType, c.quiet)
if err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
c.artifact = art
}
return nil
}
// Get returns the file metadata
func (m Metadata) Get() (db.Metadata, error) {
f, err := m.fs.Open(m.filePath)
if err != nil {
return db.Metadata{}, xerrors.Errorf("unable to open a file: %w", err)
}
defer f.Close()
var metadata db.Metadata
if err = json.NewDecoder(f).Decode(&metadata); err != nil {
return db.Metadata{}, xerrors.Errorf("unable to decode metadata: %w", err)
}
return metadata, nil
}

View File

@@ -1,367 +1,224 @@
package db
package db_test
import (
"context"
"encoding/json"
"errors"
"os"
"fmt"
"testing"
"time"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/spf13/afero"
"github.com/google/go-containerregistry/pkg/v1"
fakei "github.com/google/go-containerregistry/pkg/v1/fake"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/stretchr/testify/assert"
tdb "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/oci"
)
const mediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip"
type fakeLayer struct {
v1.Layer
}
func (f fakeLayer) MediaType() (types.MediaType, error) {
return mediaType, nil
}
func newFakeLayer(t *testing.T, input string) v1.Layer {
layer, err := tarball.LayerFromFile(input)
require.NoError(t, err)
return fakeLayer{layer}
}
func TestClient_NeedsUpdate(t *testing.T) {
timeNextUpdateDay1 := time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC)
timeNextUpdateDay2 := time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC)
testCases := []struct {
name string
light bool
skip bool
clock clock.Clock
metadata db.Metadata
expected bool
expectedError error
tests := []struct {
name string
skip bool
clock clock.Clock
metadata metadata.Metadata
want bool
wantErr string
}{
{
name: "happy path",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
},
expected: true,
want: true,
},
{
name: "happy path for first run",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{},
expected: true,
},
{
name: "happy path with different type",
light: true,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: timeNextUpdateDay1,
},
expected: true,
metadata: metadata.Metadata{},
want: true,
},
{
name: "happy path with old schema version",
light: true,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
metadata: metadata.Metadata{
Version: 0,
Type: db.TypeFull,
NextUpdate: timeNextUpdateDay1,
},
expected: true,
want: true,
},
{
name: "happy path with --skip-update",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
},
skip: true,
expected: false,
skip: true,
want: false,
},
{
name: "skip downloading DB",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion,
NextUpdate: timeNextUpdateDay2,
},
expected: false,
want: false,
},
{
name: "newer schema version",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 2,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion + 1,
NextUpdate: timeNextUpdateDay2,
},
expectedError: xerrors.New("the version of DB schema doesn't match. Local DB: 2, Expected: 1"),
wantErr: fmt.Sprintf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
tdb.SchemaVersion+1, tdb.SchemaVersion),
},
{
name: "--skip-update on the first run",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{},
skip: true,
expectedError: xerrors.New("--skip-update cannot be specified on the first run"),
name: "--skip-update on the first run",
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: metadata.Metadata{},
skip: true,
wantErr: "--skip-update cannot be specified on the first run",
},
{
name: "--skip-update with different schema version",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
metadata: metadata.Metadata{
Version: 0,
Type: db.TypeFull,
NextUpdate: timeNextUpdateDay1,
},
skip: true,
expectedError: xerrors.New("--skip-update cannot be specified with the old DB"),
skip: true,
wantErr: "--skip-update cannot be specified with the old DB",
},
{
name: "happy with old DownloadedAt",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
DownloadedAt: time.Date(2019, 9, 30, 22, 30, 0, 0, time.UTC),
},
expected: true,
want: true,
},
{
name: "skip downloading DB with recent DownloadedAt",
light: false,
clock: clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC)),
metadata: db.Metadata{
Version: 1,
Type: db.TypeFull,
metadata: metadata.Metadata{
Version: tdb.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
DownloadedAt: time.Date(2019, 9, 30, 23, 30, 0, 0, time.UTC),
},
expected: false,
want: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fs := afero.NewMemMapFs()
metadata := NewMetadata(fs, "/cache")
if tc.metadata != (db.Metadata{}) {
b, err := json.Marshal(tc.metadata)
require.NoError(t, err)
err = afero.WriteFile(fs, metadata.filePath, b, 0600)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir := t.TempDir()
if tt.metadata != (metadata.Metadata{}) {
meta := metadata.NewClient(cacheDir)
err := meta.Update(tt.metadata)
require.NoError(t, err)
}
client := Client{
clock: tc.clock,
metadata: metadata,
}
needsUpdate, err := client.NeedsUpdate("test", tc.light, tc.skip)
client := db.NewClient(cacheDir, true, db.WithClock(tt.clock))
needsUpdate, err := client.NeedsUpdate("test", tt.skip)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
case tt.wantErr != "":
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
default:
assert.NoError(t, err, tc.name)
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tc.expected, needsUpdate)
assert.Equal(t, tt.want, needsUpdate)
})
}
}
func TestClient_Download(t *testing.T) {
testCases := []struct {
name string
light bool
downloadDB []github.DownloadDBExpectation
expectedContent []byte
expectedError error
}{
{
name: "happy path",
light: false,
downloadDB: []github.DownloadDBExpectation{
{
Args: github.DownloadDBInput{FileName: fullDB},
ReturnArgs: github.DownloadDBOutput{
FileName: "testdata/test.db.gz",
},
},
},
},
{
name: "DownloadDB returns an error",
light: false,
downloadDB: []github.DownloadDBExpectation{
{
Args: github.DownloadDBInput{FileName: fullDB},
ReturnArgs: github.DownloadDBOutput{
Err: xerrors.New("download failed"),
},
},
},
expectedError: xerrors.New("failed to download vulnerability DB: download failed"),
},
{
name: "invalid gzip",
light: false,
downloadDB: []github.DownloadDBExpectation{
{
Args: github.DownloadDBInput{FileName: fullDB},
ReturnArgs: github.DownloadDBOutput{
FileName: "testdata/invalid.db.gz",
},
},
},
expectedError: xerrors.New("invalid gzip file: unexpected EOF"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockConfig := new(mockDbOperation)
mockGitHubClient, err := github.NewMockClient(tc.downloadDB)
require.NoError(t, err, tc.name)
fs := afero.NewMemMapFs()
metadata := NewMetadata(fs, "/cache")
dir, err := os.MkdirTemp("", "db")
require.NoError(t, err, tc.name)
defer os.RemoveAll(dir)
pb := indicator.NewProgressBar(true)
client := NewClient(mockConfig, mockGitHubClient, pb, nil, metadata)
ctx := context.Background()
err = client.Download(ctx, dir, tc.light)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
default:
assert.NoError(t, err, tc.name)
}
mockGitHubClient.AssertExpectations(t)
})
}
}
func TestClient_UpdateMetadata(t *testing.T) {
timeDownloadedAt := clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC))
testCases := []struct {
name string
clock clock.Clock
getMetadataExpectation dbOperationGetMetadataExpectation
storeMetadataExpectation dbOperationStoreMetadataExpectation
expectedError error
tests := []struct {
name string
input string
want metadata.Metadata
wantErr string
}{
{
name: "happy path",
clock: timeDownloadedAt,
getMetadataExpectation: dbOperationGetMetadataExpectation{
Returns: dbOperationGetMetadataReturns{
Metadata: db.Metadata{
Version: 1,
Type: 1,
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
},
Err: nil,
},
},
storeMetadataExpectation: dbOperationStoreMetadataExpectation{
Metadata: db.Metadata{
Version: 1,
Type: 1,
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
DownloadedAt: timeDownloadedAt.Now(),
},
input: "testdata/db.tar.gz",
want: metadata.Metadata{
Version: 1,
NextUpdate: time.Date(3000, 1, 1, 18, 5, 43, 198355188, time.UTC),
UpdatedAt: time.Date(3000, 1, 1, 12, 5, 43, 198355588, time.UTC),
DownloadedAt: time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC),
},
},
{
name: "sad path, get metadata fails",
clock: timeDownloadedAt,
getMetadataExpectation: dbOperationGetMetadataExpectation{
Returns: dbOperationGetMetadataReturns{
Err: errors.New("get metadata failed"),
},
},
expectedError: errors.New("unable to get metadata: get metadata failed"),
},
{
name: "sad path, store metadata fails",
clock: timeDownloadedAt,
getMetadataExpectation: dbOperationGetMetadataExpectation{
Returns: dbOperationGetMetadataReturns{
Metadata: db.Metadata{
Version: 1,
Type: 1,
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
},
Err: nil,
},
},
storeMetadataExpectation: dbOperationStoreMetadataExpectation{
Metadata: db.Metadata{
Version: 1,
Type: 1,
NextUpdate: time.Date(2020, 4, 30, 23, 59, 59, 0, time.UTC),
UpdatedAt: time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC),
DownloadedAt: timeDownloadedAt.Now(),
},
Returns: dbOperationStoreMetadataReturns{
Err: errors.New("store metadata failed"),
},
},
expectedError: errors.New("failed to store metadata: store metadata failed"),
name: "invalid gzip",
input: "testdata/trivy.db",
wantErr: "unexpected EOF",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockConfig := new(mockDbOperation)
mockConfig.ApplyGetMetadataExpectation(tc.getMetadataExpectation)
mockConfig.ApplyStoreMetadataExpectation(tc.storeMetadataExpectation)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir := t.TempDir()
fs := afero.NewMemMapFs()
metadata := NewMetadata(fs, "/cache")
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns([]v1.Layer{newFakeLayer(t, tt.input)}, nil)
dir, err := os.MkdirTemp("", "db")
require.NoError(t, err, tc.name)
defer os.RemoveAll(dir)
// Mock OCI artifact
art, err := oci.NewArtifact("db", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
pb := indicator.NewProgressBar(true)
client := NewClient(mockConfig, nil, pb, tc.clock, metadata)
err = client.UpdateMetadata(dir)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
default:
assert.NoError(t, err, tc.name)
client := db.NewClient(cacheDir, true, db.WithOCIArtifact(art), db.WithClock(timeDownloadedAt))
err = client.Download(context.Background(), cacheDir)
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
assert.NoError(t, err)
meta := metadata.NewClient(cacheDir)
got, err := meta.Get()
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,88 +0,0 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package db
import (
"github.com/aquasecurity/trivy-db/pkg/db"
mock "github.com/stretchr/testify/mock"
)
// mockDbOperation is an autogenerated mock type for the dbOperation type
type mockDbOperation struct {
mock.Mock
}
type dbOperationGetMetadataReturns struct {
Metadata db.Metadata
Err error
}
type dbOperationGetMetadataExpectation struct {
Returns dbOperationGetMetadataReturns
}
func (_m *mockDbOperation) ApplyGetMetadataExpectation(e dbOperationGetMetadataExpectation) {
var args []interface{}
_m.On("GetMetadata", args...).Return(e.Returns.Metadata, e.Returns.Err)
}
func (_m *mockDbOperation) ApplyGetMetadataExpectations(expectations []dbOperationGetMetadataExpectation) {
for _, e := range expectations {
_m.ApplyGetMetadataExpectation(e)
}
}
// GetMetadata provides a mock function with given fields:
func (_m *mockDbOperation) GetMetadata() (db.Metadata, error) {
ret := _m.Called()
var r0 db.Metadata
if rf, ok := ret.Get(0).(func() db.Metadata); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(db.Metadata)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type dbOperationStoreMetadataReturns struct {
Err error
}
type dbOperationStoreMetadataExpectation struct {
Metadata db.Metadata
Dir string
Returns dbOperationStoreMetadataReturns
}
func (_m *mockDbOperation) ApplyStoreMetadataExpectation(e dbOperationStoreMetadataExpectation) {
_m.On("StoreMetadata", e.Metadata, mock.Anything).Return(e.Returns.Err)
}
func (_m *mockDbOperation) ApplyStoreMetadataExpectations(expectations []dbOperationStoreMetadataExpectation) {
for _, e := range expectations {
_m.ApplyStoreMetadataExpectation(e)
}
}
// StoreMetadata provides a mock function with given fields: metadata, dir
func (_m *mockDbOperation) StoreMetadata(metadata db.Metadata, dir string) error {
ret := _m.Called(metadata, dir)
var r0 error
if rf, ok := ret.Get(0).(func(db.Metadata, string) error); ok {
r0 = rf(metadata, dir)
} else {
r0 = ret.Error(0)
}
return r0
}

View File

@@ -14,12 +14,10 @@ type MockOperation struct {
}
type OperationDownloadArgs struct {
Ctx context.Context
CtxAnything bool
CacheDir string
CacheDirAnything bool
Light bool
LightAnything bool
Ctx context.Context
CtxAnything bool
Dst string
DstAnything bool
}
type OperationDownloadReturns struct {
@@ -38,15 +36,10 @@ func (_m *MockOperation) ApplyDownloadExpectation(e OperationDownloadExpectation
} else {
args = append(args, e.Args.Ctx)
}
if e.Args.CacheDirAnything {
if e.Args.DstAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.CacheDir)
}
if e.Args.LightAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Light)
args = append(args, e.Args.Dst)
}
_m.On("Download", args...).Return(e.Returns.Err)
}
@@ -57,13 +50,13 @@ func (_m *MockOperation) ApplyDownloadExpectations(expectations []OperationDownl
}
}
// Download provides a mock function with given fields: ctx, cacheDir, light
func (_m *MockOperation) Download(ctx context.Context, cacheDir string, light bool) error {
ret := _m.Called(ctx, cacheDir, light)
// Download provides a mock function with given fields: ctx, dst
func (_m *MockOperation) Download(ctx context.Context, dst string) error {
ret := _m.Called(ctx, dst)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, bool) error); ok {
r0 = rf(ctx, cacheDir, light)
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, dst)
} else {
r0 = ret.Error(0)
}
@@ -76,8 +69,6 @@ type OperationNeedsUpdateArgs struct {
CliVersionAnything bool
Skip bool
SkipAnything bool
Light bool
LightAnything bool
}
type OperationNeedsUpdateReturns struct {
@@ -102,11 +93,6 @@ func (_m *MockOperation) ApplyNeedsUpdateExpectation(e OperationNeedsUpdateExpec
} else {
args = append(args, e.Args.Skip)
}
if e.Args.LightAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Light)
}
_m.On("NeedsUpdate", args...).Return(e.Returns.Need, e.Returns.Err)
}
@@ -116,67 +102,23 @@ func (_m *MockOperation) ApplyNeedsUpdateExpectations(expectations []OperationNe
}
}
// NeedsUpdate provides a mock function with given fields: cliVersion, skip, light
func (_m *MockOperation) NeedsUpdate(cliVersion string, skip bool, light bool) (bool, error) {
ret := _m.Called(cliVersion, skip, light)
// NeedsUpdate provides a mock function with given fields: cliVersion, skip
func (_m *MockOperation) NeedsUpdate(cliVersion string, skip bool) (bool, error) {
ret := _m.Called(cliVersion, skip)
var r0 bool
if rf, ok := ret.Get(0).(func(string, bool, bool) bool); ok {
r0 = rf(cliVersion, skip, light)
if rf, ok := ret.Get(0).(func(string, bool) bool); ok {
r0 = rf(cliVersion, skip)
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, bool, bool) error); ok {
r1 = rf(cliVersion, skip, light)
if rf, ok := ret.Get(1).(func(string, bool) error); ok {
r1 = rf(cliVersion, skip)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type OperationUpdateMetadataArgs struct {
CacheDir string
CacheDirAnything bool
}
type OperationUpdateMetadataReturns struct {
Err error
}
type OperationUpdateMetadataExpectation struct {
Args OperationUpdateMetadataArgs
Returns OperationUpdateMetadataReturns
}
func (_m *MockOperation) ApplyUpdateMetadataExpectation(e OperationUpdateMetadataExpectation) {
var args []interface{}
if e.Args.CacheDirAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.CacheDir)
}
_m.On("UpdateMetadata", args...).Return(e.Returns.Err)
}
func (_m *MockOperation) ApplyUpdateMetadataExpectations(expectations []OperationUpdateMetadataExpectation) {
for _, e := range expectations {
_m.ApplyUpdateMetadataExpectation(e)
}
}
// UpdateMetadata provides a mock function with given fields: cacheDir
func (_m *MockOperation) UpdateMetadata(cacheDir string) error {
ret := _m.Called(cacheDir)
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(cacheDir)
} else {
r0 = ret.Error(0)
}
return r0
}

BIN
pkg/db/testdata/db.tar.gz vendored Normal file

Binary file not shown.

View File

@@ -1 +0,0 @@
foo

Binary file not shown.

1
pkg/db/testdata/trivy.db vendored Normal file
View File

@@ -0,0 +1 @@
test

View File

@@ -1,142 +0,0 @@
package github
import (
"context"
"fmt"
"io"
"net/http"
"os"
"sort"
"strconv"
"strings"
"github.com/google/go-github/v33/github"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
owner = "aquasecurity"
repo = "trivy-db"
)
// RepositoryInterface defines the operations on repository
type RepositoryInterface interface {
ListReleases(ctx context.Context, opt *github.ListOptions) ([]*github.RepositoryRelease, *github.Response, error)
DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error)
}
// Repository implements RepositoryInterface
type Repository struct {
repository *github.RepositoriesService
git *github.GitService
owner string
repoName string
}
// ListReleases returns all github releases on repository
func (r Repository) ListReleases(ctx context.Context, opt *github.ListOptions) ([]*github.RepositoryRelease, *github.Response, error) {
return r.repository.ListReleases(ctx, r.owner, r.repoName, opt)
}
// DownloadAsset returns reader object of downloaded object
func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id, nil)
}
// Operation defines the file operations
type Operation interface {
DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error)
}
// Client implements RepositoryInterface
type Client struct {
Repository RepositoryInterface
}
// NewClient is the factory method to return Client for RepositoryInterface operations
func NewClient() Client {
var client *http.Client
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken != "" {
log.Logger.Info("Using your github token")
ctx := context.Background()
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
client = oauth2.NewClient(ctx, ts)
}
gc := github.NewClient(client)
repo := Repository{
repository: gc.Repositories,
git: gc.Git,
owner: owner,
repoName: repo,
}
return Client{
Repository: repo,
}
}
// DownloadDB returns reader object of file content
func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error) {
options := github.ListOptions{}
releases, _, err := c.Repository.ListReleases(ctx, &options)
if err != nil {
return nil, 0, xerrors.Errorf("failed to list releases: %w", err)
}
sort.Slice(releases, func(i, j int) bool {
return releases[i].GetPublishedAt().After(releases[j].GetPublishedAt().Time)
})
prefix := fmt.Sprintf("v%d", db.SchemaVersion)
for _, release := range releases {
log.Logger.Debugf("release name: %s", release.GetName())
if !strings.HasPrefix(release.GetName(), prefix) {
continue
}
for _, asset := range release.Assets {
rc, size, err := c.downloadAsset(ctx, asset, fileName)
if err != nil {
log.Logger.Debug(err)
continue
}
return rc, size, nil
}
}
return nil, 0, xerrors.New("DB file not found")
}
func (c Client) downloadAsset(ctx context.Context, asset *github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
log.Logger.Debugf("asset name: %s", asset.GetName())
if asset.GetName() != fileName {
return nil, 0, xerrors.New("file name doesn't match")
}
rc, url, err := c.Repository.DownloadAsset(ctx, asset.GetID())
if err != nil {
return nil, 0, xerrors.Errorf("unable to download the asset: %w", err)
}
if rc != nil {
return rc, asset.GetSize(), nil
}
log.Logger.Debugf("asset URL: %s", url)
resp, err := http.Get(url) // nolint: gosec
if err != nil || resp.StatusCode != http.StatusOK {
return nil, 0, xerrors.Errorf("unable to download the asset via URL: %w", err)
}
size, err := strconv.Atoi(resp.Header.Get("Content-Length"))
if err != nil {
return nil, 0, xerrors.Errorf("invalid size: %w", err)
}
return resp.Body, size, nil
}

View File

@@ -1,57 +0,0 @@
package github
import (
"context"
"io"
"os"
"github.com/stretchr/testify/mock"
)
type MockClient struct {
mock.Mock
}
type DownloadDBInput struct {
FileName string
}
type DownloadDBOutput struct {
FileName string
Size int
Err error
}
type DownloadDBExpectation struct {
Args DownloadDBInput
ReturnArgs DownloadDBOutput
}
func NewMockClient(downloadDBExpectations []DownloadDBExpectation) (*MockClient, error) {
mockDetector := new(MockClient)
for _, e := range downloadDBExpectations {
var rc io.ReadCloser
if e.ReturnArgs.FileName != "" {
f, err := os.Open(e.ReturnArgs.FileName)
if err != nil {
return nil, err
}
rc = f
}
mockDetector.On("DownloadDB", mock.Anything, e.Args.FileName).Return(
rc, e.ReturnArgs.Size, e.ReturnArgs.Err)
}
return mockDetector, nil
}
func (_m *MockClient) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error) {
ret := _m.Called(ctx, fileName)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Int(1), ret.Error(2)
}
rc, ok := ret0.(io.ReadCloser)
if !ok {
return nil, ret.Int(1), ret.Error(2)
}
return rc, ret.Int(1), ret.Error(2)
}

View File

@@ -1,458 +0,0 @@
package github
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"path"
"strings"
"testing"
"time"
"github.com/google/go-github/v33/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"golang.org/x/xerrors"
)
type MockRepository struct {
mock.Mock
}
func (_m *MockRepository) ListReleases(ctx context.Context, opt *github.ListOptions) (
[]*github.RepositoryRelease, *github.Response, error) {
ret := _m.Called(ctx, opt)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, nil, ret.Error(2)
}
releases, ok := ret0.([]*github.RepositoryRelease)
if !ok {
return nil, nil, ret.Error(2)
}
return releases, nil, ret.Error(2)
}
func (_m *MockRepository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
ret := _m.Called(ctx, id)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.String(1), ret.Error(2)
}
rc, ok := ret0.(io.ReadCloser)
if !ok {
return nil, ret.String(1), ret.Error(2)
}
return rc, ret.String(1), ret.Error(2)
}
func TestClient_DownloadDB(t *testing.T) {
type listReleasesOutput struct {
releases []*github.RepositoryRelease
response *github.Response
err error
}
type listReleases struct {
input string
output listReleasesOutput
}
type downloadAssetOutput struct {
rc io.ReadCloser
redirectPath string
err error
}
type downloadAsset struct {
input int64
output downloadAssetOutput
}
testCases := []struct {
name string
fileName string
filePaths []string
listReleases []listReleases
downloadAsset []downloadAsset
expectedError error
}{
{
name: "happy path",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
// this release should be skipped due to the wrong prefix of the tag
ID: github.Int64(2),
Name: github.String("v2-2020010101"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 1, 1, 1, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
{
ID: github.Int64(1),
Name: github.String("v1-2020123123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
rc: io.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "happy path with redirect URL",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
redirectPath: "/happy",
},
},
},
},
{
name: "happy path with inorder releases",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2019100123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
{
// this release should be used because this is the latest
ID: github.Int64(3),
Name: github.String("v1-2019100200"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(300),
Name: github.String("trivy.db.gz"),
},
},
},
{
ID: github.Int64(2),
Name: github.String("v1-2019100122"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 300,
output: downloadAssetOutput{
rc: io.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "happy path with no asset",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
// this release should be skipped due to no asset
ID: github.Int64(1),
Name: github.String("v1-2019100123"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
},
},
{
// this release should be skipped due to no asset
ID: github.Int64(3),
Name: github.String("v1-2019100200"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
},
},
{
// this release should be used because this release has assets
ID: github.Int64(2),
Name: github.String("v1-2019100122"),
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 200,
output: downloadAssetOutput{
rc: io.NopCloser(strings.NewReader("foo")),
},
},
},
},
{
name: "no asset",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
},
},
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "the file name doesn't match",
fileName: "trivy-light.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "ListReleases returns error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
err: xerrors.New("something wrong"),
},
},
},
expectedError: xerrors.New("failed to list releases: something wrong"),
},
{
name: "DownloadAsset returns error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
err: xerrors.New("something wrong"),
},
},
},
expectedError: xerrors.New("DB file not found"),
},
{
name: "404 error",
fileName: "trivy.db.gz",
listReleases: []listReleases{
{
input: mock.Anything,
output: listReleasesOutput{
releases: []*github.RepositoryRelease{
{
ID: github.Int64(1),
Name: github.String("v1-2020123000"),
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
},
},
},
},
},
},
},
downloadAsset: []downloadAsset{
{
input: 100,
output: downloadAssetOutput{
redirectPath: "/not_found",
},
},
},
expectedError: xerrors.New("DB file not found"),
},
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/happy":
_, _ = fmt.Fprintf(w, "happy")
case "/not_found":
http.NotFound(w, r)
}
return
},
))
defer ts.Close()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockRepo := new(MockRepository)
for _, lr := range tc.listReleases {
mockRepo.On("ListReleases", mock.Anything, lr.input).Return(
lr.output.releases, lr.output.response, lr.output.err,
)
}
for _, da := range tc.downloadAsset {
var redirectURL string
if da.output.redirectPath != "" {
u, _ := url.Parse(ts.URL)
u.Path = path.Join(u.Path, da.output.redirectPath)
redirectURL = u.String()
}
mockRepo.On("DownloadAsset", mock.Anything, da.input).Return(
da.output.rc, redirectURL, da.output.err,
)
}
client := Client{
Repository: mockRepo,
}
ctx := context.Background()
rc, _, err := client.DownloadDB(ctx, tc.fileName)
switch {
case tc.expectedError != nil:
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
default:
assert.NoError(t, err, tc.name)
assert.NotNil(t, rc, tc.name)
}
mockRepo.AssertExpectations(t)
})
}
}

View File

@@ -1,47 +0,0 @@
package indicator
import (
"io"
"github.com/cheggaaa/pb/v3"
)
// ProgressBar exports method to track the progress of jobs
type ProgressBar struct {
quiet bool
}
// NewProgressBar is the factory method to return progressBar object
func NewProgressBar(quiet bool) ProgressBar {
return ProgressBar{quiet: quiet}
}
// Start starts the progress tracking
func (p ProgressBar) Start(total int64) Bar {
if p.quiet {
return Bar{}
}
bar := pb.Full.Start64(total)
return Bar{bar: bar}
}
// Bar is the proxy progress bar
type Bar struct {
bar *pb.ProgressBar
}
// NewProxyReader is the factory method to track the progress
func (b Bar) NewProxyReader(r io.Reader) io.Reader {
if b.bar == nil {
return r
}
return b.bar.NewProxyReader(r)
}
// Finish finishes the progress tracking
func (b Bar) Finish() {
if b.bar == nil {
return
}
b.bar.Finish()
}

135
pkg/oci/artifact.go Normal file
View File

@@ -0,0 +1,135 @@
package oci
import (
"context"
"io"
"os"
"github.com/cheggaaa/pb/v3"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/downloader"
)
type options struct {
img v1.Image
}
// Option is a functional option
type Option func(*options)
// WithImage takes an OCI v1 Image
func WithImage(img v1.Image) Option {
return func(opts *options) {
opts.img = img
}
}
// Artifact is used to download artifacts such as vulnerability database and policies from OCI registries.
type Artifact struct {
image v1.Image
layer v1.Layer // Take the first layer as OCI artifact
quiet bool
}
// NewArtifact returns a new artifact
func NewArtifact(repo, mediaType string, quiet bool, opts ...Option) (*Artifact, error) {
o := &options{}
for _, opt := range opts {
opt(o)
}
if o.img == nil {
ref, err := name.ParseReference(repo)
if err != nil {
return nil, xerrors.Errorf("repository name error (%s): %w", repo, err)
}
o.img, err = remote.Image(ref)
if err != nil {
return nil, xerrors.Errorf("OCI repository error: %w", err)
}
}
layers, err := o.img.Layers()
if err != nil {
return nil, xerrors.Errorf("OCI layer error: %w", err)
}
// A single layer is only supported now.
if len(layers) != 1 {
return nil, xerrors.Errorf("OCI artifact must be a single layer")
}
// Take the first layer
layer := layers[0]
layerMediaType, err := layer.MediaType()
if err != nil {
return nil, xerrors.Errorf("media type error: %w", err)
} else if mediaType != string(layerMediaType) {
return nil, xerrors.Errorf("unacceptable media type: %s", string(layerMediaType))
}
return &Artifact{
image: o.img,
layer: layer,
quiet: quiet,
}, nil
}
func (a Artifact) Download(ctx context.Context, dir string) error {
size, err := a.layer.Size()
if err != nil {
return xerrors.Errorf("size error: %w", err)
}
rc, err := a.layer.Compressed()
if err != nil {
return xerrors.Errorf("failed to fetch the layer: %w", err)
}
defer rc.Close()
// Show progress bar
bar := pb.Full.Start64(size)
if a.quiet {
bar.SetWriter(io.Discard)
}
pr := bar.NewProxyReader(rc)
defer bar.Finish()
// https://github.com/hashicorp/go-getter/issues/326
f, err := os.CreateTemp("", "artifact-*.tar.gz")
if err != nil {
return xerrors.Errorf("failed to create a temp file: %w", err)
}
defer func() {
_ = f.Close()
_ = os.Remove(f.Name())
}()
// Download the layer content into a temporal file
if _, err = io.Copy(f, pr); err != nil {
return xerrors.Errorf("copy error: %w", err)
}
// Decompress artifact-xxx.tar.gz and copy it into the cache dir
if err = downloader.Download(ctx, f.Name(), dir, dir); err != nil {
return xerrors.Errorf("download error: %w", err)
}
return nil
}
func (a Artifact) Digest() (string, error) {
digest, err := a.image.Digest()
if err != nil {
return "", xerrors.Errorf("digest error: %w", err)
}
return digest.String(), nil
}

149
pkg/oci/artifact_test.go Normal file
View File

@@ -0,0 +1,149 @@
package oci_test
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/aquasecurity/trivy/pkg/utils"
v1 "github.com/google/go-containerregistry/pkg/v1"
fakei "github.com/google/go-containerregistry/pkg/v1/fake"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type fakeLayer struct {
v1.Layer
}
func (f fakeLayer) MediaType() (types.MediaType, error) {
return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil
}
func TestNewArtifact(t *testing.T) {
layer, err := tarball.LayerFromFile("testdata/test.tar.gz")
require.NoError(t, err)
flayer := fakeLayer{layer}
type layersReturns struct {
layers []v1.Layer
err error
}
tests := []struct {
name string
mediaType string
layersReturns layersReturns
wantErr string
}{
{
name: "happy path",
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
layersReturns: layersReturns{
layers: []v1.Layer{flayer},
},
},
{
name: "sad: two layers",
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
layersReturns: layersReturns{
layers: []v1.Layer{layer, layer},
},
wantErr: "OCI artifact must be a single layer",
},
{
name: "sad: Layers returns an error",
mediaType: "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip",
layersReturns: layersReturns{
err: fmt.Errorf("error"),
},
wantErr: "OCI layer error",
},
{
name: "sad: media type doesn't match",
mediaType: "unknown",
layersReturns: layersReturns{
layers: []v1.Layer{layer},
},
wantErr: "unacceptable media type",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
utils.SetCacheDir(tempDir)
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns(tt.layersReturns.layers, tt.layersReturns.err)
_, err = oci.NewArtifact("repo", tt.mediaType, true, oci.WithImage(img))
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
require.NoError(t, err)
})
}
}
func TestArtifact_Download(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr string
}{
{
name: "happy path",
input: "testdata/test.tar.gz",
want: "Hello, world",
},
{
name: "invalid gzip",
input: "testdata/test.txt",
wantErr: "unexpected EOF",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
utils.SetCacheDir(tempDir)
// Mock layer
layer, err := tarball.LayerFromFile(tt.input)
require.NoError(t, err)
flayer := fakeLayer{layer}
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns([]v1.Layer{flayer}, nil)
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
artifact, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
err = artifact.Download(context.Background(), tempDir)
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}
require.NoError(t, err)
// Assert
got, err := os.ReadFile(filepath.Join(tempDir, "test.txt"))
require.NoError(t, err)
assert.Equal(t, tt.want, string(got))
})
}
}

BIN
pkg/oci/testdata/test.tar.gz vendored Normal file

Binary file not shown.

1
pkg/oci/testdata/test.txt vendored Normal file
View File

@@ -0,0 +1 @@
Hello, world

View File

@@ -4,42 +4,38 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/open-policy-agent/opa/bundle"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
"github.com/aquasecurity/trivy/pkg/downloader"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
)
const (
bundleVersion = 1
bundleRepository = "ghcr.io/aquasecurity/appshield"
layerMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
policyMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
updateInterval = 24 * time.Hour
)
type options struct {
img v1.Image
clock clock.Clock
artifact *oci.Artifact
clock clock.Clock
}
// Option is a functional option
type Option func(*options)
// WithImage takes an OCI v1 Image
func WithImage(img v1.Image) Option {
// WithOCIArtifact takes an OCI artifact
func WithOCIArtifact(art *oci.Artifact) Option {
return func(opts *options) {
opts.img = img
opts.artifact = art
}
}
@@ -52,18 +48,19 @@ func WithClock(clock clock.Clock) Option {
// Metadata holds default policy metadata
type Metadata struct {
Digest string
LastDownloadedAt time.Time
Digest string
DownloadedAt time.Time
}
// Client implements policy operations
type Client struct {
img v1.Image
clock clock.Clock
*options
policyDir string
quiet bool
}
// NewClient is the factory method for policy client
func NewClient(opts ...Option) (*Client, error) {
func NewClient(cacheDir string, quiet bool, opts ...Option) (*Client, error) {
o := &options{
clock: clock.RealClock{},
}
@@ -73,32 +70,33 @@ func NewClient(opts ...Option) (*Client, error) {
}
return &Client{
img: o.img,
clock: o.clock,
options: o,
policyDir: filepath.Join(cacheDir, "policy"),
quiet: quiet,
}, nil
}
// LoadBuiltinPolicies loads default policies
func (c *Client) LoadBuiltinPolicies() ([]string, error) {
f, err := os.Open(manifestPath())
f, err := os.Open(c.manifestPath())
if err != nil {
return nil, xerrors.Errorf("manifest file open error (%s): %w", manifestPath(), err)
return nil, xerrors.Errorf("manifest file open error (%s): %w", c.manifestPath(), err)
}
var manifest bundle.Manifest
if err = json.NewDecoder(f).Decode(&manifest); err != nil {
return nil, xerrors.Errorf("json decode error (%s): %w", manifestPath(), err)
return nil, xerrors.Errorf("json decode error (%s): %w", c.manifestPath(), err)
}
// If the "roots" field is not included in the manifest it defaults to [""]
// which means that ALL data and policy must come from the bundle.
if manifest.Roots == nil || len(*manifest.Roots) == 0 {
return []string{contentDir()}, nil
return []string{c.contentDir()}, nil
}
var policyPaths []string
for _, root := range *manifest.Roots {
policyPaths = append(policyPaths, filepath.Join(contentDir(), root))
policyPaths = append(policyPaths, filepath.Join(c.contentDir(), root))
}
return policyPaths, nil
@@ -106,7 +104,7 @@ func (c *Client) LoadBuiltinPolicies() ([]string, error) {
// NeedsUpdate returns if the default policy should be updated
func (c *Client) NeedsUpdate() (bool, error) {
f, err := os.Open(metadataPath())
f, err := os.Open(c.metadataPath())
if err != nil {
log.Logger.Debugf("Failed to open the policy metadata: %s", err)
return true, nil
@@ -119,20 +117,20 @@ func (c *Client) NeedsUpdate() (bool, error) {
}
// No need to update if it's been within a day since the last update.
if c.clock.Now().Before(meta.LastDownloadedAt.Add(updateInterval)) {
if c.clock.Now().Before(meta.DownloadedAt.Add(updateInterval)) {
return false, nil
}
if err = c.populateImage(); err != nil {
if err = c.populateOCIArtifact(); err != nil {
return false, xerrors.Errorf("OPA bundle error: %w", err)
}
digest, err := c.img.Digest()
digest, err := c.artifact.Digest()
if err != nil {
return false, xerrors.Errorf("digest error: %w", err)
}
if meta.Digest != digest.String() {
if meta.Digest != digest {
return true, nil
}
@@ -144,112 +142,57 @@ func (c *Client) NeedsUpdate() (bool, error) {
}
return false, nil
}
func (c *Client) populateImage() error {
if c.img == nil {
func (c *Client) populateOCIArtifact() error {
if c.artifact == nil {
repo := fmt.Sprintf("%s:%d", bundleRepository, bundleVersion)
ref, err := name.ParseReference(repo)
art, err := oci.NewArtifact(repo, policyMediaType, c.quiet)
if err != nil {
return xerrors.Errorf("repository name error (%s): %w", repo, err)
}
c.img, err = remote.Image(ref)
if err != nil {
return xerrors.Errorf("OCI repository error: %w", err)
return xerrors.Errorf("OCI artifact error: %w", err)
}
c.artifact = art
}
return nil
}
// DownloadBuiltinPolicies download default policies from GitHub Pages
func (c *Client) DownloadBuiltinPolicies(ctx context.Context) error {
if err := c.populateImage(); err != nil {
if err := c.populateOCIArtifact(); err != nil {
return xerrors.Errorf("OPA bundle error: %w", err)
}
layers, err := c.img.Layers()
if err != nil {
return xerrors.Errorf("OCI layer error: %w", err)
}
if len(layers) != 1 {
return xerrors.Errorf("OPA bundle must be a single layer: %w", err)
}
bundleLayer := layers[0]
mediaType, err := bundleLayer.MediaType()
if err != nil {
return xerrors.Errorf("media type error: %w", err)
}
if mediaType != layerMediaType {
return xerrors.Errorf("unacceptable media type: %s", mediaType)
}
if err = c.downloadBuiltinPolicies(ctx, bundleLayer); err != nil {
dst := c.contentDir()
if err := c.artifact.Download(ctx, dst); err != nil {
return xerrors.Errorf("download error: %w", err)
}
digest, err := c.img.Digest()
digest, err := c.artifact.Digest()
if err != nil {
return xerrors.Errorf("digest error: %w", err)
}
log.Logger.Debugf("Digest of the built-in policies: %s", digest)
// Update metadata.json with the new digest and the current date
if err = c.updateMetadata(digest.String(), c.clock.Now()); err != nil {
if err = c.updateMetadata(digest, c.clock.Now()); err != nil {
return xerrors.Errorf("unable to update the policy metadata: %w", err)
}
return nil
}
func (c *Client) downloadBuiltinPolicies(ctx context.Context, bundleLayer v1.Layer) error {
// Take the first layer as OPA bundle
rc, err := bundleLayer.Compressed()
if err != nil {
return xerrors.Errorf("failed to fetch a layer: %w", err)
}
defer rc.Close()
// https://github.com/hashicorp/go-getter/issues/326
f, err := os.CreateTemp("", "bundle-*.tar.gz")
if err != nil {
return xerrors.Errorf("failed to create a temp dir: %w", err)
}
defer func() {
_ = f.Close()
_ = os.Remove(f.Name())
}()
// Download bundle.tar.gz into a temporal file
if _, err = io.Copy(f, rc); err != nil {
return xerrors.Errorf("copy error: %w", err)
}
// Decompress bundle.tar.gz and copy into the cache dir
dst := contentDir()
if err = downloader.Download(ctx, f.Name(), dst, dst); err != nil {
return xerrors.Errorf("policy download error: %w", err)
}
return nil
}
func (c *Client) updateMetadata(digest string, now time.Time) error {
meta := Metadata{
Digest: digest,
LastDownloadedAt: now,
}
f, err := os.Create(metadataPath())
f, err := os.Create(c.metadataPath())
if err != nil {
return xerrors.Errorf("failed to open a policy manifest: %w", err)
}
defer f.Close()
meta := Metadata{
Digest: digest,
DownloadedAt: now,
}
if err = json.NewEncoder(f).Encode(meta); err != nil {
return xerrors.Errorf("json encode error: %w", err)
}
@@ -257,18 +200,14 @@ func (c *Client) updateMetadata(digest string, now time.Time) error {
return nil
}
func policyDir() string {
return filepath.Join(utils.CacheDir(), "policy")
func (c *Client) contentDir() string {
return filepath.Join(c.policyDir, "content")
}
func contentDir() string {
return filepath.Join(policyDir(), "content")
func (c *Client) metadataPath() string {
return filepath.Join(c.policyDir, "metadata.json")
}
func metadataPath() string {
return filepath.Join(policyDir(), "metadata.json")
}
func manifestPath() string {
return filepath.Join(contentDir(), bundle.ManifestExt)
func (c *Client) manifestPath() string {
return filepath.Join(c.contentDir(), bundle.ManifestExt)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"testing"
@@ -18,8 +19,8 @@ import (
"k8s.io/utils/clock"
fake "k8s.io/utils/clock/testing"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/utils"
)
type fakeLayer struct {
@@ -37,6 +38,25 @@ func newFakeLayer(t *testing.T) v1.Layer {
return fakeLayer{layer}
}
type brokenLayer struct {
v1.Layer
}
func (b brokenLayer) MediaType() (types.MediaType, error) {
return "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", nil
}
func (b brokenLayer) Compressed() (io.ReadCloser, error) {
return nil, fmt.Errorf("compressed error")
}
func newBrokenLayer(t *testing.T) v1.Layer {
layer, err := tarball.LayerFromFile("testdata/bundle.tar.gz")
require.NoError(t, err)
return brokenLayer{layer}
}
func TestClient_LoadBuiltinPolicies(t *testing.T) {
tests := []struct {
name string
@@ -74,10 +94,16 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
utils.SetCacheDir(tt.cacheDir)
// Mock image
img := new(fakei.FakeImage)
c, err := policy.NewClient(policy.WithImage(img))
img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil)
// Mock OCI artifact
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tt.cacheDir, true, policy.WithOCIArtifact(art))
require.NoError(t, err)
got, err := c.LoadBuiltinPolicies()
@@ -112,8 +138,8 @@ func TestClient_NeedsUpdate(t *testing.T) {
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
},
@@ -124,8 +150,8 @@ func TestClient_NeedsUpdate(t *testing.T) {
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d`,
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Digest: `sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
},
@@ -136,8 +162,8 @@ func TestClient_NeedsUpdate(t *testing.T) {
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: true,
},
@@ -148,8 +174,8 @@ func TestClient_NeedsUpdate(t *testing.T) {
err: fmt.Errorf("error"),
},
metadata: policy.Metadata{
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
},
want: false,
wantErr: true,
@@ -171,10 +197,10 @@ func TestClient_NeedsUpdate(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// Set up a temporary directory
tmpDir := t.TempDir()
utils.SetCacheDir(tmpDir)
// Mock image
img := new(fakei.FakeImage)
img.LayersReturns([]v1.Layer{newFakeLayer(t)}, nil)
img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err)
// Create a policy directory
@@ -191,10 +217,14 @@ func TestClient_NeedsUpdate(t *testing.T) {
require.NoError(t, err)
}
// Assert results
c, err := policy.NewClient(policy.WithImage(img), policy.WithClock(tt.clock))
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tmpDir, true, policy.WithOCIArtifact(art), policy.WithClock(tt.clock))
require.NoError(t, err)
// Assert results
got, err := c.NeedsUpdate()
assert.Equal(t, tt.wantErr, err != nil)
assert.Equal(t, tt.want, got)
@@ -203,8 +233,6 @@ func TestClient_NeedsUpdate(t *testing.T) {
}
func TestClient_DownloadBuiltinPolicies(t *testing.T) {
layer := newFakeLayer(t)
type digestReturns struct {
h v1.Hash
err error
@@ -225,55 +253,39 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) {
name: "happy path",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{layer},
layers: []v1.Layer{newFakeLayer(t)},
},
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
LastDownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
},
{
name: "sad: two layers",
name: "sad: broken layer",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{layer, layer},
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
LastDownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
wantErr: "OPA bundle must be a single layer",
},
{
name: "sad: Layers returns an error",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
err: fmt.Errorf("error"),
layers: []v1.Layer{newBrokenLayer(t)},
},
digestReturns: digestReturns{
h: v1.Hash{Algorithm: "sha256", Hex: "01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d"},
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
LastDownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
wantErr: "OCI layer error",
wantErr: "compressed error",
},
{
name: "sad: Digest returns an error",
clock: fake.NewFakeClock(time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC)),
layersReturns: layersReturns{
layers: []v1.Layer{layer},
layers: []v1.Layer{newFakeLayer(t)},
},
digestReturns: digestReturns{
err: fmt.Errorf("error"),
},
want: &policy.Metadata{
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
LastDownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
Digest: "sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d",
DownloadedAt: time.Date(2021, 1, 1, 1, 0, 0, 0, time.UTC),
},
wantErr: "digest error",
},
@@ -282,14 +294,18 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
utils.SetCacheDir(tempDir)
// Mock image
img := new(fakei.FakeImage)
img.DigestReturns(tt.digestReturns.h, tt.digestReturns.err)
img.LayersReturns(tt.layersReturns.layers, tt.layersReturns.err)
c, err := policy.NewClient(policy.WithClock(tt.clock), policy.WithImage(img))
// Mock OCI artifact
mediaType := "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip"
art, err := oci.NewArtifact("repo", mediaType, true, oci.WithImage(img))
require.NoError(t, err)
c, err := policy.NewClient(tempDir, true, policy.WithClock(tt.clock), policy.WithOCIArtifact(art))
require.NoError(t, err)
err = c.DownloadBuiltinPolicies(context.Background())

View File

@@ -19,9 +19,6 @@ type TableWriter struct {
Severities []dbTypes.Severity
Output io.Writer
// For vulnerabilities
Light bool
// For misconfigurations
IncludeNonFailures bool
Trace bool
@@ -103,10 +100,7 @@ func (tw TableWriter) summary(severityCount map[string]int) (int, []string) {
}
func (tw TableWriter) writeVulnerabilities(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
if !tw.Light {
header = append(header, "Title")
}
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version", "Title"}
table.SetHeader(header)
severityCount := tw.setVulnerabilityRows(table, vulns)
@@ -156,14 +150,11 @@ func (tw TableWriter) setVulnerabilityRows(table *tablewriter.Table, vulns []typ
var row []string
if tw.Output == os.Stdout {
row = []string{v.PkgName, v.VulnerabilityID, dbTypes.ColorizeSeverity(v.Severity),
v.InstalledVersion, v.FixedVersion}
v.InstalledVersion, v.FixedVersion, strings.TrimSpace(title)}
} else {
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion}
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion, strings.TrimSpace(title)}
}
if !tw.Light {
row = append(row, strings.TrimSpace(title))
}
table.Append(row)
}
return severityCount

View File

@@ -16,7 +16,6 @@ func TestReportWriter_Table(t *testing.T) {
name string
results report.Results
expectedOutput string
light bool
includeNonFailures bool
}{
{
@@ -46,33 +45,6 @@ func TestReportWriter_Table(t *testing.T) {
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
`,
},
{
name: "happy path light",
light: true,
results: report.Results{
{
Target: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
+---------+------------------+----------+-------------------+---------------+
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 |
+---------+------------------+----------+-------------------+---------------+
`,
},
{
@@ -218,7 +190,6 @@ func TestReportWriter_Table(t *testing.T) {
err := report.Write(report.Report{Results: tc.results}, report.Option{
Format: "table",
Output: &tableWritten,
Light: tc.light,
IncludeNonFailures: tc.includeNonFailures,
})
assert.NoError(t, err)

View File

@@ -93,7 +93,6 @@ type Option struct {
Output io.Writer
Severities []dbTypes.Severity
OutputTemplate string
Light bool
// For misconfigurations
IncludeNonFailures bool
@@ -108,7 +107,6 @@ func Write(report Report, option Option) error {
writer = &TableWriter{
Output: option.Output,
Severities: option.Severities,
Light: option.Light,
IncludeNonFailures: option.IncludeNonFailures,
Trace: option.Trace,
}

View File

@@ -30,7 +30,7 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
}{
{
name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
@@ -54,7 +54,7 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
{
name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0002"},
@@ -79,7 +79,7 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
{
name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0003"},
@@ -101,7 +101,7 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
{
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0004"},
@@ -137,50 +137,9 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
},
},
{
name: "happy path light db, with only OS vulnerability, yes vendor severity",
fixtures: []string{"testdata/fixtures/light.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-0001"},
},
reportType: vulnerability.Ubuntu,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityLow.String(),
},
SeveritySource: vulnerability.Ubuntu,
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
},
},
},
{
name: "happy path light db, with only OS vulnerability, no vendor severity",
fixtures: []string{"testdata/fixtures/light.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-0002"},
},
reportType: vulnerability.Alpine,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0002",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityUnknown.String(),
},
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0002",
},
},
},
{
name: "happy path, with only library vulnerability",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0005"},
@@ -203,7 +162,7 @@ func TestClient_FillVulnerabilityInfo(t *testing.T) {
},
{
name: "happy path, with package-specific severity",
fixtures: []string{"testdata/fixtures/full.yaml"},
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{

View File

@@ -1,11 +0,0 @@
- bucket: vulnerability
pairs:
- key: CVE-2020-0001
value:
Title: dos
Severity: MEDIUM
VendorSeverity:
ubuntu: 1
- key: CVE-2020-0002
value:
Title: dos

View File

@@ -12,8 +12,3 @@ func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServ
wire.Build(ScanSuperSet)
return &ScanServer{}
}
func initializeDBWorker(cacheDir string, quiet bool) dbWorker {
wire.Build(DBWorkerSuperSet)
return dbWorker{}
}

View File

@@ -8,24 +8,21 @@ import (
"time"
"github.com/NYTimes/gziphandler"
"github.com/google/wire"
"github.com/twitchtv/twirp"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
dbFile "github.com/aquasecurity/trivy/pkg/db"
dbc "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
// DBWorkerSuperSet binds the dependencies for Trivy DB worker
var DBWorkerSuperSet = wire.NewSet(
dbFile.SuperSet,
newDBWorker,
)
const updateInterval = 1 * time.Hour
// Server represents Trivy server
type Server struct {
@@ -53,10 +50,10 @@ func (s Server) ListenAndServe(serverCache cache.Cache) error {
dbUpdateWg := &sync.WaitGroup{}
go func() {
worker := initializeDBWorker(s.cacheDir, true)
worker := newDBWorker(dbc.NewClient(s.cacheDir, true))
ctx := context.Background()
for {
time.Sleep(1 * time.Hour)
time.Sleep(updateInterval)
if err := worker.update(ctx, s.appVersion, s.cacheDir, dbUpdateWg, requestWg); err != nil {
log.Logger.Errorf("%+v\n", err)
}
@@ -124,7 +121,7 @@ func newDBWorker(dbClient dbFile.Operation) dbWorker {
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
dbUpdateWg, requestWg *sync.WaitGroup) error {
log.Logger.Debug("Check for DB update...")
needsUpdate, err := w.dbClient.NeedsUpdate(appVersion, false, false)
needsUpdate, err := w.dbClient.NeedsUpdate(appVersion, false)
if err != nil {
return xerrors.Errorf("failed to check if db needs an update")
} else if !needsUpdate {
@@ -145,7 +142,7 @@ func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, re
}
defer os.RemoveAll(tmpDir)
if err = w.dbClient.Download(ctx, tmpDir, false); err != nil {
if err = w.dbClient.Download(ctx, tmpDir); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
@@ -160,12 +157,14 @@ func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, re
return xerrors.Errorf("failed to close DB: %w", err)
}
// Copy trivy.db
if _, err = utils.CopyFile(db.Path(tmpDir), db.Path(cacheDir)); err != nil {
return xerrors.Errorf("failed to copy the database file: %w", err)
}
if err = w.dbClient.UpdateMetadata(cacheDir); err != nil {
return xerrors.Errorf("unable to update database metadata: %w", err)
// Copy metadata.json
if _, err = utils.CopyFile(metadata.Path(tmpDir), metadata.Path(cacheDir)); err != nil {
return xerrors.Errorf("failed to copy the metadata file: %w", err)
}
log.Logger.Info("Reopening DB...")

View File

@@ -6,7 +6,6 @@ import (
"net/http/httptest"
"os"
"path"
"path/filepath"
"sync"
"testing"
"time"
@@ -18,7 +17,9 @@ import (
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/utils"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
)
@@ -52,7 +53,7 @@ func Test_dbWorker_update(t *testing.T) {
needsUpdate needsUpdate
download download
args args
want db.Metadata
want metadata.Metadata
wantErr string
}{
{
@@ -65,9 +66,8 @@ func Test_dbWorker_update(t *testing.T) {
call: true,
},
args: args{appVersion: "1"},
want: db.Metadata{
want: metadata.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: timeNextUpdate,
UpdatedAt: timeUpdateAt,
},
@@ -105,51 +105,50 @@ func Test_dbWorker_update(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir, err := os.MkdirTemp("", "server-test")
require.NoError(t, err, tt.name)
cacheDir := t.TempDir()
require.NoError(t, db.Init(cacheDir), tt.name)
mockDBClient := new(dbFile.MockOperation)
mockDBClient.On("NeedsUpdate",
tt.needsUpdate.input.appVersion, false, tt.needsUpdate.input.skip).Return(
tt.needsUpdate.input.appVersion, tt.needsUpdate.input.skip).Return(
tt.needsUpdate.output.needsUpdate, tt.needsUpdate.output.err)
mockDBClient.On("UpdateMetadata", mock.Anything).Return(nil)
if tt.download.call {
mockDBClient.On("Download", mock.Anything, mock.Anything, false).Run(
mockDBClient.On("Download", mock.Anything, mock.Anything).Run(
func(args mock.Arguments) {
// fake download: copy testdata/new.db to tmpDir/db/trivy.db
content, err := os.ReadFile("testdata/new.db")
require.NoError(t, err, tt.name)
tmpDir := args.String(1)
dbPath := db.Path(tmpDir)
require.NoError(t, os.MkdirAll(filepath.Dir(dbPath), 0777), tt.name)
err = os.WriteFile(dbPath, content, 0444)
require.NoError(t, err, tt.name)
err := os.MkdirAll(db.Dir(tmpDir), 0744)
require.NoError(t, err)
_, err = utils.CopyFile("testdata/new.db", db.Path(tmpDir))
require.NoError(t, err)
// fake download: copy testdata/metadata.json to tmpDir/db/metadata.json
_, err = utils.CopyFile("testdata/metadata.json", metadata.Path(tmpDir))
require.NoError(t, err)
}).Return(tt.download.err)
}
w := newDBWorker(mockDBClient)
var dbUpdateWg, requestWg sync.WaitGroup
err = w.update(context.Background(), tt.args.appVersion, cacheDir,
err := w.update(context.Background(), tt.args.appVersion, cacheDir,
&dbUpdateWg, &requestWg)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
require.NoError(t, err, tt.name)
if !tt.download.call {
return
}
dbc := db.Config{}
got, err := dbc.GetMetadata()
mc := metadata.NewClient(cacheDir)
got, err := mc.Get()
assert.NoError(t, err, tt.name)
assert.Equal(t, tt.want, got, tt.name)

1
pkg/rpc/server/testdata/metadata.json vendored Normal file
View File

@@ -0,0 +1 @@
{"Version":1,"NextUpdate":"3000-01-01T0:00:00.0Z","UpdatedAt":"3000-01-01T0:00:00.0Z"}

View File

@@ -10,14 +10,9 @@ import (
"github.com/aquasecurity/fanal/applier"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
db2 "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/spf13/afero"
"k8s.io/utils/clock"
)
// Injectors from inject.go:
@@ -31,15 +26,3 @@ func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServ
scanServer := NewScanServer(scanner, client)
return scanServer
}
func initializeDBWorker(cacheDir string, quiet bool) dbWorker {
config := db.Config{}
client := github.NewClient()
progressBar := indicator.NewProgressBar(quiet)
realClock := clock.RealClock{}
fs := afero.NewOsFs()
metadata := db2.NewMetadata(fs, cacheDir)
dbClient := db2.NewClient(config, client, progressBar, realClock, metadata)
serverDbWorker := newDBWorker(dbClient)
return serverDbWorker
}

View File

@@ -5,6 +5,8 @@ import (
"io"
"os"
"path/filepath"
"golang.org/x/xerrors"
)
var cacheDir string
@@ -42,7 +44,7 @@ func StringInSlice(a string, list []string) bool {
func CopyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
return 0, xerrors.Errorf("file (%s) stat error: %w", src, err)
}
if !sourceFileStat.Mode().IsRegular() {