mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 14:50:53 -08:00
BREAKING: Trivy DB from GHCR (#1539)
This commit is contained in:
@@ -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/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
@@ -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
4
go.mod
@@ -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
135
go.sum
@@ -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=
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
@@ -27,7 +25,7 @@ import (
|
||||
// VersionInfo holds the trivy DB version Info
|
||||
type VersionInfo struct {
|
||||
Version string `json:",omitempty"`
|
||||
VulnerabilityDB *db.Metadata `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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
263
pkg/db/db.go
263
pkg/db/db.go
@@ -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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the file metadata
|
||||
func (m Metadata) Get() (db.Metadata, error) {
|
||||
f, err := m.fs.Open(m.filePath)
|
||||
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 db.Metadata{}, xerrors.Errorf("unable to open a file: %w", err)
|
||||
return xerrors.Errorf("OCI artifact error: %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)
|
||||
c.artifact = art
|
||||
}
|
||||
return metadata, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
tests := []struct {
|
||||
name string
|
||||
light bool
|
||||
skip bool
|
||||
clock clock.Clock
|
||||
metadata db.Metadata
|
||||
expected bool
|
||||
expectedError error
|
||||
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,
|
||||
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{},
|
||||
metadata: metadata.Metadata{},
|
||||
skip: true,
|
||||
expectedError: xerrors.New("--skip-update cannot be specified on the first run"),
|
||||
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"),
|
||||
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 {
|
||||
timeDownloadedAt := clocktesting.NewFakeClock(time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
light bool
|
||||
downloadDB []github.DownloadDBExpectation
|
||||
expectedContent []byte
|
||||
expectedError error
|
||||
input string
|
||||
want metadata.Metadata
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
light: false,
|
||||
downloadDB: []github.DownloadDBExpectation{
|
||||
{
|
||||
Args: github.DownloadDBInput{FileName: fullDB},
|
||||
ReturnArgs: github.DownloadDBOutput{
|
||||
FileName: "testdata/test.db.gz",
|
||||
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: "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"),
|
||||
input: "testdata/trivy.db",
|
||||
wantErr: "unexpected EOF",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockConfig := new(mockDbOperation)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cacheDir := t.TempDir()
|
||||
|
||||
mockGitHubClient, err := github.NewMockClient(tc.downloadDB)
|
||||
require.NoError(t, err, tc.name)
|
||||
// Mock image
|
||||
img := new(fakei.FakeImage)
|
||||
img.LayersReturns([]v1.Layer{newFakeLayer(t, tt.input)}, nil)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
metadata := NewMetadata(fs, "/cache")
|
||||
// Mock OCI artifact
|
||||
art, err := oci.NewArtifact("db", mediaType, true, oci.WithImage(img))
|
||||
require.NoError(t, err)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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
|
||||
}{
|
||||
{
|
||||
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(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockConfig := new(mockDbOperation)
|
||||
mockConfig.ApplyGetMetadataExpectation(tc.getMetadataExpectation)
|
||||
mockConfig.ApplyStoreMetadataExpectation(tc.storeMetadataExpectation)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
meta := metadata.NewClient(cacheDir)
|
||||
got, err := meta.Get()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -16,10 +16,8 @@ type MockOperation struct {
|
||||
type OperationDownloadArgs struct {
|
||||
Ctx context.Context
|
||||
CtxAnything bool
|
||||
CacheDir string
|
||||
CacheDirAnything bool
|
||||
Light bool
|
||||
LightAnything 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
BIN
pkg/db/testdata/db.tar.gz
vendored
Normal file
Binary file not shown.
1
pkg/db/testdata/invalid.db.gz
vendored
1
pkg/db/testdata/invalid.db.gz
vendored
@@ -1 +0,0 @@
|
||||
foo
|
||||
BIN
pkg/db/testdata/test.db.gz
vendored
BIN
pkg/db/testdata/test.db.gz
vendored
Binary file not shown.
1
pkg/db/testdata/trivy.db
vendored
Normal file
1
pkg/db/testdata/trivy.db
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
135
pkg/oci/artifact.go
Normal 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
149
pkg/oci/artifact_test.go
Normal 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
BIN
pkg/oci/testdata/test.tar.gz
vendored
Normal file
Binary file not shown.
1
pkg/oci/testdata/test.txt
vendored
Normal file
1
pkg/oci/testdata/test.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, world
|
||||
@@ -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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,17 +49,18 @@ func WithClock(clock clock.Clock) Option {
|
||||
// Metadata holds default policy metadata
|
||||
type Metadata struct {
|
||||
Digest string
|
||||
LastDownloadedAt time.Time
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -113,7 +139,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
},
|
||||
metadata: policy.Metadata{
|
||||
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
|
||||
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
@@ -125,7 +151,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
},
|
||||
metadata: policy.Metadata{
|
||||
Digest: `sha256:01e033e78bd8a59fa4f4577215e7da06c05e1152526094d8d79d2aa06e98cb9d`,
|
||||
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
@@ -137,7 +163,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
},
|
||||
metadata: policy.Metadata{
|
||||
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
|
||||
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
DownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
@@ -149,7 +175,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
||||
},
|
||||
metadata: policy.Metadata{
|
||||
Digest: `sha256:922e50f14ab484f11ae65540c3d2d76009020213f1027d4331d31141575e5414`,
|
||||
LastDownloadedAt: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
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),
|
||||
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),
|
||||
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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
{
|
||||
|
||||
11
pkg/result/testdata/fixtures/light.yaml
vendored
11
pkg/result/testdata/fixtures/light.yaml
vendored
@@ -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
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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...")
|
||||
|
||||
@@ -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
1
pkg/rpc/server/testdata/metadata.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"Version":1,"NextUpdate":"3000-01-01T0:00:00.0Z","UpdatedAt":"3000-01-01T0:00:00.0Z"}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user