feat(cache): based on JSON (#398)

* refactor(docker_conf): rename and remove unnecessary options

* feat(rpc): define new API

* fix(cli): change default timeout

* fix(import): fix package names

* refactor(vulnerability): remove old mock

* refactor(utils): remove un-needed functions

* feat(cache): implement cache communicating with a server

* refactor(scan): separate scan function as local scanner

* test(scanner): add tests for ScanImage

* refactor(scan): remove unused options

* test(vulnerability): generate mock

* refactor(server): split a file

* feat(server): implement new RPC server

* feat(client): implement new RPC client

* fix(cache): use new cache interface

* fix(standalone): use new scanner

* fix(client): use new scanner

* fix(server): pass cache

* test(integration): make sure an error is not nil before calling the method

* fix(mod): update dependencies

* test(integration): ensure the image load finishes

* feat(docker): support DOCKER_HOST and DOCKER_CERT_PATH

* chore(mod): update dependencies

* refactor(rpc): remove old client

* feat(server): support old API for backward compatibility

* fix(server): check a schema version of JSON cache

* fix(rpc): add a version to packages

* feat(rpc): add PutImage

* test: rename expectations

* refactor(cache): rename LayerCache to ImageCache

* refactor: rename ImageInfo to ImageReference

* fix(applier): pass image_id to ApplyLayer

* feat(cache): handle image cache

* chore(mod): update dependencies

* refactor(server): pass only config

* feat(cli): add -removed-pkgs option

* refactor(err): wrap errors
This commit is contained in:
Teppei Fukuda
2020-02-27 23:17:55 +02:00
committed by GitHub
parent b83174f32d
commit 18b80e3781
81 changed files with 7592 additions and 2382 deletions

View File

@@ -47,7 +47,7 @@ build:
.PHONY: protoc
protoc:
protoc --proto_path=$(GOSRC):. --twirp_out=. --go_out=. ./rpc/detector/service.proto
find ./rpc/ -name "*.proto" -type f -exec protoc --proto_path=$(GOSRC):. --twirp_out=. --go_out=. {} \;
.PHONY: install
install:

12
go.mod
View File

@@ -3,8 +3,7 @@ module github.com/aquasecurity/trivy
go 1.13
require (
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793
github.com/aquasecurity/fanal v0.0.0-20200221125056-947c2a5bb130
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1
github.com/caarlos0/env/v6 v6.0.0
@@ -13,7 +12,7 @@ require (
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f
github.com/genuinetools/reg v0.16.0
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect
github.com/golang/protobuf v1.3.2
github.com/golang/protobuf v1.3.3
github.com/google/go-github/v28 v28.1.1
github.com/google/wire v0.3.0
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
@@ -21,19 +20,16 @@ require (
github.com/knqyf263/go-version v1.1.1
github.com/kylelemons/godebug v1.1.0
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
github.com/prometheus/procfs v0.0.5 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/stretchr/testify v1.4.0
github.com/twitchtv/twirp v5.10.1+incompatible
github.com/urfave/cli v1.20.0
github.com/urfave/cli v1.22.1
go.uber.org/atomic v1.5.1 // indirect
go.uber.org/multierr v1.4.0 // indirect
go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328 // indirect
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
gopkg.in/yaml.v2 v2.2.4 // indirect
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4
)
replace github.com/genuinetools/reg => github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00

246
go.sum
View File

@@ -2,16 +2,20 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
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/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -19,18 +23,14 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6/go.mod h1:enEz4FFetw4XAbkffaYgyCVq1556R9Ry+noqT4rq9BE=
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793 h1:wPGD5cu66RvwRkNULAqOAMbNWFjmYSCJ/mqHJ5rYBN0=
github.com/aquasecurity/fanal v0.0.0-20200112144021-9a35ce3bd793/go.mod h1:S2D937GMywJzh6KiLQEyt/0yqmfAngUFvuQ9UmkIZSw=
github.com/aquasecurity/fanal v0.0.0-20200221125056-947c2a5bb130 h1:OAwolcJ+dj0hDbPh/jSQlOMXxDti0LoOKvgR7e+hzVA=
github.com/aquasecurity/fanal v0.0.0-20200221125056-947c2a5bb130/go.mod h1:yPZqe/vMN0QDXBIl3kE9s793zU9NSQuEHGWLlL85bG8=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ulc/gvfWm4ylhVaR7MxOwujRjA6et7KhmUbSgUFf4=
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ=
github.com/aquasecurity/trivy v0.1.6/go.mod h1:5hobyhxLzDtxruHzPxpND2PUKOssvGUdE9BocpJUwo4=
@@ -38,16 +38,14 @@ github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1 h1:IVXoVH8ej
github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1/go.mod h1:Uf9bXd50zTHtWTP7+7u5+OFCPtUVrmsS4v0RXd7E5lw=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 h1:xbdUfr2KE4THsFx9CFWtWpU91lF+YhgP46moV94nYTA=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83 h1:ukTLOeMC0aVxbJWVg6hOsVJ0VPIo8w++PbNsze/pqF8=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
github.com/aws/aws-sdk-go v1.19.11 h1:tqaTGER6Byw3QvsjGW0p018U2UOqaJPeJuzoaF7jjoQ=
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.31 h1:14mdh3HsTgRekePPkYcCbAaEXJknc3mN7f4XfsiMMDA=
github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 h1:GMmnK0dvr0Sf0gx3DvTbln0c8DE07B7sPVD9dgHOqo4=
github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91/go.mod h1:hw/JEQBIE+c/BLI4aKM8UU8v+ZqrD3h7HC27kKt8JQU=
github.com/caarlos0/env/v6 v6.0.0 h1:NZt6FAoB8ieKO5lEwRdwCzYxWFx7ZYF2R7UcoyaWtyc=
@@ -58,8 +56,32 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/cheggaaa/pb/v3 v3.0.3 h1:8WApbyUmgMOz7WIxJVNK0IRDcRfAmTxcEdi0TuxjdP4=
github.com/cheggaaa/pb/v3 v3.0.3/go.mod h1:Pp35CDuiEpHa/ZLGCtBbM6CBwMstv1bJlG884V+73Yc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo=
github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
github.com/containers/storage v1.15.3/go.mod h1:v0lq/3f+cXH3Y/HiDaFYRR0zilwDve7I4W7U5xQxvF8=
github.com/containers/storage v1.15.5 h1:dBZx9yRFHod9c8FVaXlVtRqr2cmlAhpl+9rt87cE7J4=
github.com/containers/storage v1.15.5/go.mod h1:v0lq/3f+cXH3Y/HiDaFYRR0zilwDve7I4W7U5xQxvF8=
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -68,19 +90,27 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c h1:QlAVcyoF7QQVN7zV+xYBjgwtRVlRU3WCTCpb2mcqQrM=
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.0.0-20171019062838-86f080cff091/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.0.0-20180522102801-da99009bbb11/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f h1:W4fbqg0JUwy6lLesoJaV/rE0fwAmtdtinMa64X1CEh0=
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43 h1:gZ4lWixV821UVbYtr+oz1ZPCHkbtE+ivfmHyZRgyl2Y=
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
github.com/docker/docker-credential-helpers v0.6.2 h1:CrW9H1VMf3a4GrtyAi7IUJjkJVpwBBpX0+mvkvYJaus=
github.com/docker/docker-credential-helpers v0.6.2/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.0.0-20180212134524-7beb39f0b969/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA=
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
@@ -93,45 +123,48 @@ github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkg
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
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/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
github.com/genuinetools/reg v0.16.0 h1:ZhLZPT+aUGHLfy45Ub5FLWik+3Dij1iwaj8A/GyAZBw=
github.com/genuinetools/reg v0.16.0/go.mod h1:12Fe9EIvK3dG/qWhNk5e9O96I8SGmCKLsJ8GsXUbk+Y=
github.com/ghodss/yaml v0.0.0-20161207003320-04f313413ffd/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.1.3 h1:cBU46h1lYQk5f2Z+jZbewFKy+1zzE2aUX/ilcPDAm9M=
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/protobuf v0.0.0-20170815085658-fcdc5011193f/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -148,17 +181,23 @@ github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs167
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v0.0.0-20170217192616-94e7d24fd285/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
@@ -167,11 +206,16 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.4 h1:xhvAeUPQ2drNUhKtrGdTGNvV9nNafHMUkRyLkzxJoB4=
github.com/klauspost/compress v1.9.4/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knqyf263/berkeleydb v0.0.0-20190501065933-fafe01fb9662/go.mod h1:bu1CcN4tUtoRcI/B/RFHhxMNKFHVq/c3SV+UTyduoXg=
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
@@ -182,7 +226,6 @@ github.com/knqyf263/go-version v1.1.1 h1:+MpcBC9b7rk5ihag8Y/FLG8get1H2GjniwKQ+9D
github.com/knqyf263/go-version v1.1.1/go.mod h1:0tBvHvOBSf5TqGNcY+/ih9o8qo3R16iZCpB9rP0D3VM=
github.com/knqyf263/nested v0.0.1 h1:Sv26CegUMhjt19zqbBKntjwESdxe5hxVPSk0+AKjdUc=
github.com/knqyf263/nested v0.0.1/go.mod h1:zwhsIhMkBg90DTOJQvxPkKIypEHPYkgWHs4gybdlUmk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -192,32 +235,32 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed h1:fCWISZq4YN4ulCJx7x0KB15rqxLEe3mtNJL8cSOGKZU=
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed/go.mod h1:SDJ4hurDYyQ9/7nc+eCYtXqdufgK4Cq9TJlwPklqEYA=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8=
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -225,17 +268,27 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a h1:0LD5FJ
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2 h1:QhPf3A2AZW3tTGvHPg0TA+CR3oHbVLlXUhlghqISp1I=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU=
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g=
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/peterhellberg/link v1.0.0 h1:mUWkiegowUXEcmlb+ybF75Q/8D2Y0BjZtR8cxoKhaQo=
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
@@ -245,25 +298,26 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9 h1:kyf9snWXHvQc+yxE9imhdI8YAm4oKeZISlaAR+x73zs=
github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
@@ -271,67 +325,86 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/httpfs v0.0.0-20181222201310-74dc9339e414/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00 h1:0e4vRd9YqnQBIAIAE39jLKDWffRfJWxloyWwcaMAQho=
github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00/go.mod h1:RQE7h2jyIxekQZ24/wad0c9RGP+KSq4XzHh7h83ALi8=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/twitchtv/twirp v5.10.1+incompatible h1:35js8ID9rYPKkZ0qWnuZw+q+OuCWM1GIibu1F1YImjA=
github.com/twitchtv/twirp v5.10.1+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbauerster/mpb/v4 v4.11.1/go.mod h1:vMLa1J/ZKC83G2lB/52XpqT+ZZtFG4aZOdKhmpRL1uM=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v0.0.0-20190816131739-be0936907f66/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -342,15 +415,16 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c h1:SRpq/kuj/xNci/RdvEs+RSvpfxqvLAzTKuKGlzoGdZQ=
golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -360,12 +434,12 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -374,19 +448,20 @@ golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190506115046-ca7f33d4116e h1:bq5BY1tGuaK8HxuwN6pT6kWgTVLeJ5KwuyBpsl1CZL4=
golang.org/x/sys v0.0.0-20190506115046-ca7f33d4116e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -400,16 +475,13 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190503185657-3b6f9c0030f7/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328 h1:t3X42h9e6xdbrCD/gPyWqAXr2BEpdJqRd1brThaaxII=
golang.org/x/tools v0.0.0-20191121040551-947d4aa89328/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373 h1:PPwnA7z1Pjf7XYaBP9GL1VAMZmcIWyFz7QCMSIIa3Bg=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -418,50 +490,56 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0 h1:KFpaNTUcLHLoP/OkdcRXR+MA5p55MhA41YVb7Wd8EfM=
gopkg.in/src-d/go-git-fixtures.v3 v3.4.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw=
gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
k8s.io/client-go v0.0.0-20170217214107-bcde30fb7eae/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA=
k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=

View File

@@ -4,6 +4,7 @@ package integration
import (
"context"
"io"
"io/ioutil"
"os"
"strings"
@@ -229,7 +230,7 @@ func TestRun_WithDockerEngine(t *testing.T) {
name: "sad path, invalid image",
invalidImage: true,
testfile: "badimage:latest",
expectedError: "error in image scan: failed to analyze image: failed to extract files: failed to get the v2 manifest: Get https://registry-1.docker.io/v2/library/badimage/manifests/latest: http: non-successful response (status=401 body=\"{\\\"errors\\\":[{\\\"code\\\":\\\"UNAUTHORIZED\\\",\\\"message\\\":\\\"authentication required\\\",\\\"detail\\\":[{\\\"Type\\\":\\\"repository\\\",\\\"Class\\\":\\\"\\\",\\\"Name\\\":\\\"library/badimage\\\",\\\"Action\\\":\\\"pull\\\"}]}]}\\n\")",
expectedError: "unable to initialize a image struct: Error reading manifest latest in docker.io/library/badimage",
},
}
@@ -256,8 +257,9 @@ func TestRun_WithDockerEngine(t *testing.T) {
})
// load image into docker engine
_, err = cli.ImageLoad(ctx, testfile, true)
res, err := cli.ImageLoad(ctx, testfile, true)
require.NoError(t, err, tc.name)
io.Copy(ioutil.Discard, res.Body)
// tag our image to something unique
err = cli.ImageTag(ctx, tc.imageTag, tc.testfile)
@@ -293,7 +295,8 @@ func TestRun_WithDockerEngine(t *testing.T) {
err = app.Run(trivyArgs)
switch {
case tc.expectedError != "":
assert.Equal(t, tc.expectedError, err.Error(), tc.name)
require.NotNil(t, err)
assert.Contains(t, err.Error(), tc.expectedError, tc.name)
default:
assert.NoError(t, err, tc.name)
}
@@ -311,6 +314,10 @@ func TestRun_WithDockerEngine(t *testing.T) {
Force: true,
PruneChildren: true,
})
_, err = cli.ImageRemove(ctx, tc.imageTag, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
assert.NoError(t, err, tc.name)
}
})

View File

@@ -104,6 +104,12 @@ var (
EnvVar: "TRIVY_DEBUG",
}
removedPkgsFlag = cli.BoolFlag{
Name: "removed-pkgs",
Usage: "detect vulnerabilities of removed packages (only for Alpine)",
EnvVar: "TRIVY_REMOVED_PKGS",
}
vulnTypeFlag = cli.StringFlag{
Name: "vuln-type",
Value: "os,library",
@@ -127,7 +133,7 @@ var (
timeoutFlag = cli.DurationFlag{
Name: "timeout",
Value: time.Second * 60,
Value: time.Second * 120,
Usage: "docker timeout",
EnvVar: "TRIVY_TIMEOUT",
}
@@ -192,6 +198,7 @@ OPTIONS:
noProgressFlag,
ignoreUnfixedFlag,
debugFlag,
removedPkgsFlag,
vulnTypeFlag,
cacheDirFlag,
ignoreFileFlag,
@@ -242,6 +249,7 @@ func NewClientCommand() cli.Command {
quietFlag,
ignoreUnfixedFlag,
debugFlag,
removedPkgsFlag,
vulnTypeFlag,
ignoreFileFlag,
cacheDirFlag,

View File

@@ -30,12 +30,13 @@ type Config struct {
Format string
Template string
Timeout time.Duration
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
Timeout time.Duration
ScanRemovedPkgs bool
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
RemoteAddr string
token string
@@ -73,12 +74,13 @@ func New(c *cli.Context) (Config, error) {
Format: c.String("format"),
Template: c.String("template"),
Timeout: c.Duration("timeout"),
vulnType: c.String("vuln-type"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
Timeout: c.Duration("timeout"),
ScanRemovedPkgs: c.Bool("removed-pkgs"),
vulnType: c.String("vuln-type"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
RemoteAddr: c.String("remote"),
token: c.String("token"),

View File

@@ -3,18 +3,26 @@
package client
import (
"context"
"time"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/google/wire"
)
func initializeScanner(cacheClient cache.Cache, ospkgCustomHeaders ospkg.CustomHeaders, libraryCustomHeaders library.CustomHeaders,
ospkgURL ospkg.RemoteURL, libURL library.RemoteURL) scanner.Scanner {
wire.Build(scanner.ClientSet)
return scanner.Scanner{}
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil
}
func initializeVulnerabilityClient() vulnerability.Client {

View File

@@ -1,20 +1,19 @@
package client
import (
"context"
"os"
"github.com/urfave/cli"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/client/config"
"github.com/aquasecurity/trivy/internal/operation"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
"golang.org/x/xerrors"
)
func Run(cliCtx *cli.Context) error {
@@ -37,31 +36,40 @@ func run(c config.Config) (err error) {
// configure cache dir
utils.SetCacheDir(c.CacheDir)
cacheClient := cache.Initialize(c.CacheDir)
cacheOperation := operation.NewCache(cacheClient)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
if c.ClearCache {
return cacheOperation.ClearImages()
log.Logger.Warn("A client doesn't have image cache")
return nil
}
var scanner scanner.Scanner
ctx := context.Background()
remoteCache := cache.NewRemoteCache(cache.RemoteURL(c.RemoteAddr), c.CustomHeaders)
if c.Input != "" {
// scan tar file
scanner, err = initializeArchiveScanner(ctx, c.Input, remoteCache,
client.CustomHeaders(c.CustomHeaders), client.RemoteURL(c.RemoteAddr), c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
} else {
// scan an image in Docker Engine or Docker Registry
scanner, err = initializeDockerScanner(ctx, c.ImageName, remoteCache,
client.CustomHeaders(c.CustomHeaders), client.RemoteURL(c.RemoteAddr), c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the docker scanner: %w", err)
}
}
scanOptions := types.ScanOptions{
VulnType: c.VulnType,
RemoteURL: c.RemoteAddr,
VulnType: c.VulnType,
ScanRemovedPackages: c.ScanRemovedPkgs,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
dockerOption, err := types.GetDockerOption()
if err != nil {
return xerrors.Errorf("failed to get docker option: %w", err)
}
dockerOption.Timeout = c.Timeout
scanner := initializeScanner(cacheClient,
ospkg.CustomHeaders(c.CustomHeaders), library.CustomHeaders(c.CustomHeaders),
ospkg.RemoteURL(c.RemoteAddr), library.RemoteURL(c.RemoteAddr))
results, err := scanner.ScanImage(c.ImageName, c.Input, scanOptions, dockerOption)
results, err := scanner.ScanImage(scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}

View File

@@ -6,31 +6,54 @@
package client
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/rpc/client/library"
"github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
library2 "github.com/aquasecurity/trivy/pkg/scanner/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"time"
)
// Injectors from inject.go:
func initializeScanner(cacheClient cache.Cache, ospkgCustomHeaders ospkg.CustomHeaders, libraryCustomHeaders library.CustomHeaders, ospkgURL ospkg.RemoteURL, libURL library.RemoteURL) scanner.Scanner {
osDetector := ospkg.NewProtobufClient(ospkgURL)
detector := ospkg.NewDetector(ospkgCustomHeaders, osDetector)
ospkgScanner := ospkg2.NewScanner(detector)
libDetector := library.NewProtobufClient(libURL)
libraryDetector := library.NewDetector(libraryCustomHeaders, libDetector)
libraryScanner := library2.NewScanner(libraryDetector)
scannerScanner := scanner.NewScanner(cacheClient, ospkgScanner, libraryScanner)
return scannerScanner
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, err
}
extractor, err := docker.NewDockerExtractor(ctx, imageName, dockerOption)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scanner2 := scanner.NewScanner(clientScanner, config)
return scanner2, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration) (scanner.Scanner, error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, err
}
extractor, err := docker.NewDockerArchiveExtractor(ctx, filePath, dockerOption)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scanner2 := scanner.NewScanner(clientScanner, config)
return scanner2, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
return client
vulnerabilityClient := vulnerability.NewClient(config)
return vulnerabilityClient
}

View File

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

View File

@@ -1,6 +1,7 @@
package server
import (
"github.com/aquasecurity/fanal/cache"
"github.com/urfave/cli"
"golang.org/x/xerrors"
@@ -30,8 +31,13 @@ func run(c config.Config) (err error) {
utils.SetCacheDir(c.CacheDir)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
fsCache, err := cache.NewFSCache(utils.CacheDir())
if err != nil {
return xerrors.Errorf("unable to initialize cache: %w", err)
}
// server doesn't have image cache
cacheOperation := operation.NewCache(nil)
cacheOperation := operation.NewCache(fsCache)
if c.Reset {
return cacheOperation.ClearDB()
}
@@ -49,5 +55,5 @@ func run(c config.Config) (err error) {
return nil
}
return server.ListenAndServe(c.Listen, c)
return server.ListenAndServe(c, fsCache)
}

View File

@@ -33,13 +33,14 @@ type Config struct {
Format string
Template string
Timeout time.Duration
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
Timeout time.Duration
ScanRemovedPkgs bool
vulnType string
Light bool
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
// these variables are generated by Init()
ImageName string
@@ -82,13 +83,14 @@ func New(c *cli.Context) (Config, error) {
Format: c.String("format"),
Template: c.String("template"),
Timeout: c.Duration("timeout"),
vulnType: c.String("vuln-type"),
Light: c.Bool("light"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
Timeout: c.Duration("timeout"),
ScanRemovedPkgs: c.Bool("removed-pkgs"),
vulnType: c.String("vuln-type"),
Light: c.Bool("light"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),

View File

@@ -3,21 +3,25 @@
package standalone
import (
"context"
"time"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/operation"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/google/wire"
)
func initializeCacheClient(cacheDir string) (operation.Cache, error) {
wire.Build(operation.SuperSet)
return operation.Cache{}, nil
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil
}
func initializeScanner(c cache.Cache) scanner.Scanner {
wire.Build(scanner.StandaloneSet)
return scanner.Scanner{}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil
}
func initializeVulnerabilityClient() vulnerability.Client {

View File

@@ -1,6 +1,7 @@
package standalone
import (
"context"
"io/ioutil"
l "log"
"os"
@@ -12,6 +13,7 @@ import (
"github.com/aquasecurity/trivy/internal/standalone/config"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/urfave/cli"
@@ -38,7 +40,11 @@ func run(c config.Config) (err error) {
// configure cache dir
utils.SetCacheDir(c.CacheDir)
cacheClient := cache.Initialize(c.CacheDir)
cacheClient, err := cache.NewFSCache(c.CacheDir)
if err != nil {
return xerrors.Errorf("unable to initialize the cache: %w", err)
}
cacheOperation := operation.NewCache(cacheClient)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
@@ -63,19 +69,30 @@ func run(c config.Config) (err error) {
return nil
}
var scanner scanner.Scanner
ctx := context.Background()
if c.Input != "" {
// scan tar file
scanner, err = initializeArchiveScanner(ctx, c.Input, cacheClient, cacheClient, c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
} else {
// scan an image in Docker Engine or Docker Registry
scanner, err = initializeDockerScanner(ctx, c.ImageName, cacheClient, cacheClient, c.Timeout)
if err != nil {
return xerrors.Errorf("unable to initialize the docker scanner: %w", err)
}
}
scanOptions := types.ScanOptions{
VulnType: c.VulnType,
VulnType: c.VulnType,
ScanRemovedPackages: c.ScanRemovedPkgs,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
dockerOption, err := types.GetDockerOption()
if err != nil {
return xerrors.Errorf("failed to get docker option: %w", err)
}
dockerOption.Timeout = c.Timeout
scanner := initializeScanner(cacheClient)
results, err := scanner.ScanImage(c.ImageName, c.Input, scanOptions, dockerOption)
results, err := scanner.ScanImage(scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}

View File

@@ -6,33 +6,58 @@
package standalone
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor/docker"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/operation"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
library2 "github.com/aquasecurity/trivy/pkg/scanner/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"time"
)
// Injectors from inject.go:
func initializeCacheClient(cacheDir string) (operation.Cache, error) {
cacheCache := cache.Initialize(cacheDir)
operationCache := operation.NewCache(cacheCache)
return operationCache, nil
}
func initializeScanner(c cache.Cache) scanner.Scanner {
func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache, timeout time.Duration) (scanner.Scanner, error) {
applier := analyzer.NewApplier(localImageCache)
detector := ospkg.Detector{}
ospkgScanner := ospkg2.NewScanner(detector)
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
libraryScanner := library2.NewScanner(libraryDetector)
scannerScanner := scanner.NewScanner(c, ospkgScanner, libraryScanner)
return scannerScanner
localScanner := local.NewScanner(applier, detector, libraryDetector)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, err
}
extractor, err := docker.NewDockerExtractor(ctx, imageName, dockerOption)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scannerScanner := scanner.NewScanner(localScanner, config)
return scannerScanner, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache, timeout time.Duration) (scanner.Scanner, error) {
applier := analyzer.NewApplier(localImageCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
localScanner := local.NewScanner(applier, detector, libraryDetector)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, err
}
extractor, err := docker.NewDockerArchiveExtractor(ctx, filePath, dockerOption)
if err != nil {
return scanner.Scanner{}, err
}
config := analyzer.New(extractor, layerCache)
scannerScanner := scanner.NewScanner(localScanner, config)
return scannerScanner, nil
}
func initializeVulnerabilityClient() vulnerability.Client {

51
pkg/cache/remote.go vendored Normal file
View File

@@ -0,0 +1,51 @@
package cache
import (
"context"
"net/http"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/rpc/client"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
)
type RemoteCache struct {
ctx context.Context // for custom header
client rpcCache.Cache
}
type RemoteURL string
func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ImageCache {
ctx := client.WithCustomHeaders(context.Background(), customHeaders)
c := rpcCache.NewCacheProtobufClient(string(url), &http.Client{})
return &RemoteCache{ctx: ctx, client: c}
}
func (c RemoteCache) PutImage(imageID string, imageInfo types.ImageInfo) error {
_, err := c.client.PutImage(c.ctx, rpc.ConvertToRpcImageInfo(imageID, imageInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
func (c RemoteCache) PutLayer(layerID, decompressedLayerID string, layerInfo types.LayerInfo) error {
_, err := c.client.PutLayer(c.ctx, rpc.ConvertToRpcLayerInfo(layerID, decompressedLayerID, layerInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
func (c RemoteCache) MissingLayers(imageID string, layerIDs []string) (bool, []string, error) {
layers, err := c.client.MissingLayers(c.ctx, rpc.ConvertToMissingLayersRequest(imageID, layerIDs))
if err != nil {
return false, nil, xerrors.Errorf("unable to fetch missing layers: %w", err)
}
return layers.MissingImage, layers.MissingLayerIds, nil
}

294
pkg/cache/remote_test.go vendored Normal file
View File

@@ -0,0 +1,294 @@
package cache_test
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/twitchtv/twirp"
"golang.org/x/xerrors"
fcache "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/cache"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
"github.com/aquasecurity/trivy/rpc/detector"
)
type mockCacheServer struct {
cache fcache.Cache
}
func (s *mockCacheServer) PutImage(_ context.Context, in *rpcCache.PutImageRequest) (*google_protobuf.Empty, error) {
if strings.Contains(in.ImageId, "invalid") {
return &google_protobuf.Empty{}, xerrors.New("invalid image ID")
}
return &google_protobuf.Empty{}, nil
}
func (s *mockCacheServer) PutLayer(_ context.Context, in *rpcCache.PutLayerRequest) (*google_protobuf.Empty, error) {
if strings.Contains(in.LayerId, "invalid") {
return &google_protobuf.Empty{}, xerrors.New("invalid layer ID")
}
return &google_protobuf.Empty{}, nil
}
func (s *mockCacheServer) MissingLayers(_ context.Context, in *rpcCache.MissingLayersRequest) (*rpcCache.MissingLayersResponse, error) {
var layerIDs []string
for _, layerID := range in.LayerIds[:len(in.LayerIds)-1] {
if strings.Contains(layerID, "invalid") {
return nil, xerrors.New("invalid layer ID")
}
layerIDs = append(layerIDs, layerID)
}
return &rpcCache.MissingLayersResponse{MissingImage: true, MissingLayerIds: layerIDs}, nil
}
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get(tokenHeader) {
detector.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
})
}
func TestRemoteCache_PutImage(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
ts := httptest.NewServer(mux)
type args struct {
imageID string
imageInfo types.ImageInfo
customHeaders http.Header
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "happy path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
imageInfo: types.ImageInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Time{},
DockerVersion: "18.06",
OS: "linux",
HistoryPackages: []types.Package{
{
Name: "musl",
Version: "1.2.3",
},
},
},
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
},
{
name: "sad path",
args: args{
imageID: "sha256:invalid",
imageInfo: types.ImageInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Time{},
DockerVersion: "18.06",
OS: "linux",
HistoryPackages: []types.Package{
{
Name: "musl",
Version: "1.2.3",
},
},
},
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
wantErr: "twirp error internal",
},
{
name: "sad path: invalid token",
args: args{
imageID: "sha256:invalid",
customHeaders: http.Header{
"Trivy-Token": []string{"invalid-token"},
},
},
wantErr: "twirp error unauthenticated",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
err := c.PutImage(tt.args.imageID, tt.args.imageInfo)
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)
}
})
}
}
func TestRemoteCache_PutLayer(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
ts := httptest.NewServer(mux)
type args struct {
layerID string
decompressedLayerID string
layerInfo types.LayerInfo
customHeaders http.Header
}
tests := []struct {
name string
args args
wantErr string
}{
{
name: "happy path",
args: args{
layerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
decompressedLayerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
},
{
name: "sad path",
args: args{
layerID: "sha256:invalid",
decompressedLayerID: "sha256:invalid",
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
wantErr: "twirp error internal",
},
{
name: "sad path: invalid token",
args: args{
layerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
decompressedLayerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
customHeaders: http.Header{
"Trivy-Token": []string{"invalid-token"},
},
},
wantErr: "twirp error unauthenticated",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
err := c.PutLayer(tt.args.layerID, tt.args.decompressedLayerID, tt.args.layerInfo)
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)
}
})
}
}
func TestRemoteCache_MissingLayers(t *testing.T) {
mux := http.NewServeMux()
layerHandler := rpcCache.NewCacheServer(new(mockCacheServer), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(layerHandler, "valid-token", "Trivy-Token"))
ts := httptest.NewServer(mux)
type args struct {
imageID string
layerIDs []string
customHeaders http.Header
}
tests := []struct {
name string
args args
wantMissingImage bool
wantMissingLayerIDs []string
wantErr string
}{
{
name: "happy path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
layerIDs: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
wantMissingImage: true,
wantMissingLayerIDs: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
},
{
name: "sad path",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
layerIDs: []string{
"sha256:invalid",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
customHeaders: http.Header{
"Trivy-Token": []string{"valid-token"},
},
},
wantErr: "twirp error internal",
},
{
name: "sad path with invalid token",
args: args{
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
layerIDs: []string{
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
customHeaders: http.Header{
"Trivy-Token": []string{"invalid-token"},
},
},
wantErr: "twirp error unauthenticated",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders)
gotMissingImage, gotMissingLayerIDs, err := c.MissingLayers(tt.args.imageID, tt.args.layerIDs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
require.NoError(t, err, tt.name)
}
assert.Equal(t, tt.wantMissingImage, gotMissingImage)
assert.Equal(t, tt.wantMissingLayerIDs, gotMissingLayerIDs)
})
}
}

View File

@@ -7,7 +7,7 @@ import (
version "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/alpine"
"github.com/aquasecurity/trivy/pkg/log"
@@ -50,7 +50,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Alpine vulnerabilities...")
if strings.Count(osVer, ".") > 1 {
osVer = osVer[:strings.LastIndex(osVer, ".")]

View File

@@ -6,9 +6,10 @@ import (
"testing"
"time"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
@@ -23,7 +24,7 @@ func TestMain(m *testing.M) {
func TestScanner_Detect(t *testing.T) {
type args struct {
osVer string
pkgs []analyzer.Package
pkgs []ftypes.Package
}
type getInput struct {
osVer string
@@ -52,7 +53,7 @@ func TestScanner_Detect(t *testing.T) {
name: "happy path",
args: args{
osVer: "3.10.2",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "ansible",
Version: "2.6.4",
@@ -109,7 +110,7 @@ func TestScanner_Detect(t *testing.T) {
name: "contain rc",
args: args{
osVer: "3.9",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "jq",
Version: "1.6-r0",
@@ -150,7 +151,7 @@ func TestScanner_Detect(t *testing.T) {
name: "Get returns an error",
args: args{
osVer: "3.8.1",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "jq",
Version: "1.6-r0",

View File

@@ -3,12 +3,11 @@ package amazon
import (
"strings"
"go.uber.org/zap"
version "github.com/knqyf263/go-deb-version"
"go.uber.org/zap"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/amazon"
"github.com/aquasecurity/trivy/pkg/log"
@@ -28,7 +27,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Amazon Linux vulnerabilities...")
osVer = strings.Fields(osVer)[0]

View File

@@ -9,7 +9,7 @@ import (
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
@@ -52,7 +52,7 @@ func TestScanner_Detect(t *testing.T) {
},
}
vuls, err := s.Detect("3.1.0", []analyzer.Package{
vuls, err := s.Detect("3.1.0", []ftypes.Package{
{
Name: "testpkg",
Version: "2.1.0",
@@ -89,7 +89,7 @@ func TestScanner_Detect(t *testing.T) {
},
},
}
vuls, err := s.Detect("foo", []analyzer.Package{
vuls, err := s.Detect("foo", []ftypes.Package{
{
Name: "testpkg",
},
@@ -115,7 +115,7 @@ func TestScanner_Detect(t *testing.T) {
},
}
vuls, err := s.Detect("3.1.0", []analyzer.Package{
vuls, err := s.Detect("3.1.0", []ftypes.Package{
{
Name: "testpkg",
Version: "badsourceversion",
@@ -144,7 +144,7 @@ func TestScanner_Detect(t *testing.T) {
},
}
vuls, err := s.Detect("3.1.0", []analyzer.Package{
vuls, err := s.Detect("3.1.0", []ftypes.Package{
{
Name: "testpkg",
Version: "3.1.0",

View File

@@ -10,7 +10,7 @@ import (
version "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
@@ -52,7 +52,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Debian vulnerabilities...")
if strings.Count(osVer, ".") > 0 {

View File

@@ -3,6 +3,11 @@ package ospkg
import (
"time"
"github.com/google/wire"
"golang.org/x/xerrors"
fos "github.com/aquasecurity/fanal/analyzer/os"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/amazon"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
@@ -12,11 +17,6 @@ import (
"github.com/aquasecurity/trivy/pkg/detector/ospkg/suse"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
fos "github.com/aquasecurity/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/types"
)
@@ -30,17 +30,17 @@ var (
)
type Operation interface {
Detect(string, string, string, time.Time, []analyzer.Package) ([]types.DetectedVulnerability, bool, error)
Detect(string, string, string, time.Time, []ftypes.Package) ([]types.DetectedVulnerability, bool, error)
}
type Driver interface {
Detect(string, []analyzer.Package) ([]types.DetectedVulnerability, error)
Detect(string, []ftypes.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(string, string) bool
}
type Detector struct{}
func (d Detector) Detect(_, osFamily, osName string, _ time.Time, pkgs []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
func (d Detector) Detect(_, osFamily, osName string, _ time.Time, pkgs []ftypes.Package) ([]types.DetectedVulnerability, bool, error) {
driver := newDriver(osFamily, osName)
if driver == nil {
return nil, false, ErrUnsupportedOS

View File

@@ -3,7 +3,7 @@ package ospkg
import (
"time"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/mock"
)
@@ -17,7 +17,7 @@ type DetectInput struct {
OSFamily string
OSName string
Created time.Time
Pkgs []analyzer.Package
Pkgs []ftypes.Package
}
type DetectOutput struct {
Vulns []types.DetectedVulnerability
@@ -38,7 +38,7 @@ func NewMockDetector(detectExpectations []DetectExpectation) *MockDetector {
return mockDetector
}
func (_m *MockDetector) Detect(a string, b string, c string, d time.Time, e []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
func (_m *MockDetector) Detect(a string, b string, c string, d time.Time, e []ftypes.Package) ([]types.DetectedVulnerability, bool, error) {
ret := _m.Called(a, b, c, d, e)
ret0 := ret.Get(0)
if ret0 == nil {

View File

@@ -9,7 +9,7 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
@@ -44,7 +44,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Oracle Linux vulnerabilities...")
if strings.Count(osVer, ".") > 0 {

View File

@@ -8,7 +8,7 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
@@ -33,7 +33,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Photon Linux vulnerabilities...")
log.Logger.Debugf("Photon Linux: os version: %s", osVer)
log.Logger.Debugf("Photon Linux: the number of packages: %d", len(pkgs))

View File

@@ -9,8 +9,8 @@ import (
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/os"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
@@ -47,7 +47,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting RHEL/CentOS vulnerabilities...")
if strings.Count(osVer, ".") > 0 {
osVer = osVer[:strings.Index(osVer, ".")]

View File

@@ -5,11 +5,12 @@ import (
"testing"
"time"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
@@ -23,7 +24,7 @@ func TestMain(m *testing.M) {
func TestScanner_Detect(t *testing.T) {
type args struct {
osVer string
pkgs []analyzer.Package
pkgs []ftypes.Package
}
tests := []struct {
name string
@@ -36,7 +37,7 @@ func TestScanner_Detect(t *testing.T) {
name: "happy path: src pkg name is different from bin pkg name",
args: args{
osVer: "7.6",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "vim-minimal",
Version: "7.4.160",
@@ -107,7 +108,7 @@ func TestScanner_Detect(t *testing.T) {
name: "happy path: src pkg name is the same as bin pkg name",
args: args{
osVer: "6.5",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "nss",
Version: "3.36.0",
@@ -168,7 +169,7 @@ func TestScanner_Detect(t *testing.T) {
name: "sad path: Get returns an error",
args: args{
osVer: "5",
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "nss",
Version: "3.36.0",

View File

@@ -3,16 +3,17 @@ package suse
import (
"time"
"github.com/aquasecurity/fanal/analyzer"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
fos "github.com/aquasecurity/fanal/analyzer/os"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
susecvrf "github.com/aquasecurity/trivy-db/pkg/vulnsrc/suse-cvrf"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
)
var (
@@ -79,7 +80,7 @@ func NewScanner(t SUSEType) *Scanner {
return nil
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting SUSE vulnerabilities...")
log.Logger.Debugf("SUSE: os version: %s", osVer)
log.Logger.Debugf("SUSE: the number of packages: %d", len(pkgs))

View File

@@ -3,13 +3,12 @@ package ubuntu
import (
"time"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/ubuntu"
version "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/ubuntu"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
@@ -61,7 +60,7 @@ func NewScanner() *Scanner {
}
}
func (s *Scanner) Detect(osVer string, pkgs []analyzer.Package) ([]types.DetectedVulnerability, error) {
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Ubuntu vulnerabilities...")
log.Logger.Debugf("ubuntu: os version: %s", osVer)
log.Logger.Debugf("ubuntu: the number of packages: %d", len(pkgs))

62
pkg/rpc/client/client.go Normal file
View File

@@ -0,0 +1,62 @@
package client
import (
"context"
"net/http"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/google/wire"
digest "github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/report"
r "github.com/aquasecurity/trivy/pkg/rpc"
rpc "github.com/aquasecurity/trivy/rpc/scanner"
)
var SuperSet = wire.NewSet(
NewProtobufClient,
NewScanner,
)
type RemoteURL string
func NewProtobufClient(remoteURL RemoteURL) rpc.Scanner {
return rpc.NewScannerProtobufClient(string(remoteURL), &http.Client{})
}
type CustomHeaders http.Header
type Scanner struct {
customHeaders CustomHeaders
client rpc.Scanner
}
func NewScanner(customHeaders CustomHeaders, s rpc.Scanner) Scanner {
return Scanner{customHeaders: customHeaders, client: s}
}
func (s Scanner) Scan(target string, imageID digest.Digest, layerIDs []string, options types.ScanOptions) (report.Results, *ftypes.OS, bool, error) {
ctx := WithCustomHeaders(context.Background(), http.Header(s.customHeaders))
var res *rpc.ScanResponse
err := r.Retry(func() error {
var err error
res, err = s.client.Scan(ctx, &rpc.ScanRequest{
Target: target,
ImageId: string(imageID),
LayerIds: layerIDs,
Options: &rpc.ScanOptions{
VulnType: options.VulnType,
},
})
return err
})
if err != nil {
return nil, nil, false, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcResults(res.Results), r.ConvertFromRpcOS(res.Os), res.Eosl, nil
}

View File

@@ -0,0 +1,243 @@
package client
import (
"context"
"errors"
"testing"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/rpc/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/mock"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/scanner"
digest "github.com/opencontainers/go-digest"
)
type mockScanner struct {
mock.Mock
}
type scanArgs struct {
Ctx context.Context
CtxAnything bool
Request *scanner.ScanRequest
RequestAnything bool
}
type scanReturns struct {
Res *scanner.ScanResponse
Err error
}
type scanExpectation struct {
Args scanArgs
Returns scanReturns
}
func (_m *mockScanner) ApplyScanExpectation(e scanExpectation) {
var args []interface{}
if e.Args.CtxAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Ctx)
}
if e.Args.RequestAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Request)
}
_m.On("Scan", args...).Return(e.Returns.Res, e.Returns.Err)
}
func (_m *mockScanner) ApplyScanExpectations(expectations []scanExpectation) {
for _, e := range expectations {
_m.ApplyScanExpectation(e)
}
}
// Scan provides a mock function with given fields: Ctx, Request
func (_m *mockScanner) Scan(Ctx context.Context, Request *scanner.ScanRequest) (*scanner.ScanResponse, error) {
ret := _m.Called(Ctx, Request)
var r0 *scanner.ScanResponse
if rf, ok := ret.Get(0).(func(context.Context, *scanner.ScanRequest) *scanner.ScanResponse); ok {
r0 = rf(Ctx, Request)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*scanner.ScanResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *scanner.ScanRequest) error); ok {
r1 = rf(Ctx, Request)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
func TestScanner_Scan(t *testing.T) {
type fields struct {
customHeaders CustomHeaders
}
type args struct {
target string
imageID digest.Digest
layerIDs []string
options types.ScanOptions
}
tests := []struct {
name string
fields fields
args args
scanExpectation scanExpectation
wantResults report.Results
wantOS *ftypes.OS
wantEosl bool
wantErr string
}{
{
name: "happy path",
fields: fields{
customHeaders: CustomHeaders{
"Trivy-Token": []string{"foo"},
},
},
args: args{
target: "alpine:3.11",
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{
VulnType: []string{"os"},
},
},
scanExpectation: scanExpectation{
Args: scanArgs{
CtxAnything: true,
Request: &scanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &scanner.ScanOptions{
VulnType: []string{"os"},
},
},
},
Returns: scanReturns{
Res: &scanner.ScanResponse{
Os: &common.OS{
Family: "alpine",
Name: "3.11",
},
Eosl: true,
Results: []*scanner.Result{
{
Target: "alpine:3.11",
Vulnerabilities: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2020-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial os Service",
Severity: common.Severity_CRITICAL,
References: []string{"http://exammple.com"},
},
},
},
},
},
},
},
wantResults: report.Results{
{
Target: "alpine:3.11",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "DoS",
Description: "Denial os Service",
Severity: "CRITICAL",
References: []string{"http://exammple.com"},
},
},
},
},
},
wantOS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
wantEosl: true,
},
{
name: "sad path: Scan returns an error",
fields: fields{
customHeaders: CustomHeaders{
"Trivy-Token": []string{"foo"},
},
},
args: args{
target: "alpine:3.11",
imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{
VulnType: []string{"os"},
},
},
scanExpectation: scanExpectation{
Args: scanArgs{
CtxAnything: true,
Request: &scanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &scanner.ScanOptions{
VulnType: []string{"os"},
},
},
},
Returns: scanReturns{
Err: errors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities via RPC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := new(mockScanner)
mockClient.ApplyScanExpectation(tt.scanExpectation)
s := NewScanner(tt.fields.customHeaders, mockClient)
gotResults, gotOS, gotEosl, err := s.Scan(tt.args.target, tt.args.imageID, tt.args.layerIDs, tt.args.options)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
require.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
require.NoError(t, err, tt.name)
}
assert.Equal(t, tt.wantResults, gotResults)
assert.Equal(t, tt.wantOS, gotOS)
assert.Equal(t, tt.wantEosl, gotEosl)
})
}
}

View File

@@ -1,70 +0,0 @@
package library
import (
"context"
"net/http"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/google/wire"
"golang.org/x/xerrors"
depptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
detector "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/log"
r "github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/types"
rpc "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
NewProtobufClient,
NewDetector,
wire.Bind(new(detector.Operation), new(Detector)),
)
type RemoteURL string
func NewProtobufClient(remoteURL RemoteURL) rpc.LibDetector {
return rpc.NewLibDetectorProtobufClient(string(remoteURL), &http.Client{})
}
type CustomHeaders http.Header
type Detector struct {
customHeaders CustomHeaders
client rpc.LibDetector
}
func NewDetector(customHeaders CustomHeaders, detector rpc.LibDetector) Detector {
return Detector{customHeaders: customHeaders, client: detector}
}
func (d Detector) Detect(imageName, filePath string, created time.Time, libs []depptypes.Library) ([]types.DetectedVulnerability, error) {
ctx := client.WithCustomHeaders(context.Background(), http.Header(d.customHeaders))
var res *rpc.DetectResponse
err := r.Retry(func() error {
var err error
res, err = d.client.Detect(ctx, &rpc.LibDetectRequest{
ImageName: imageName,
FilePath: filePath,
Libraries: r.ConvertToRpcLibraries(libs),
Created: func() *timestamp.Timestamp {
t, err := ptypes.TimestampProto(created)
if err != nil {
log.Logger.Warnf("invalid timestamp: %s", err)
}
return t
}(),
})
return err
})
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcVulns(res.Vulnerabilities), nil
}

View File

@@ -1,182 +0,0 @@
package library
import (
"context"
"testing"
"time"
"golang.org/x/xerrors"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/stretchr/testify/mock"
)
type mockDetector struct {
mock.Mock
}
func (_m *mockDetector) Detect(a context.Context, b *detector.LibDetectRequest) (*detector.DetectResponse, error) {
ret := _m.Called(a, b)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
res, ok := ret0.(*detector.DetectResponse)
if !ok {
return nil, ret.Error(1)
}
return res, ret.Error(1)
}
func TestDetectClient_Detect(t *testing.T) {
type detectInput struct {
req *detector.LibDetectRequest
}
type detectOutput struct {
res *detector.DetectResponse
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
customHeaders CustomHeaders
}
type args struct {
imageName string
filePath string
created time.Time
libs []deptypes.Library
}
tests := []struct {
name string
fields fields
args args
detect detect
want []types.DetectedVulnerability
wantErr string
}{
{
name: "happy path",
fields: fields{
customHeaders: CustomHeaders{
"Trivy-Token": []string{"token"},
},
},
args: args{
imageName: "/tmp/alpine.tar",
filePath: "app/Pipfile.lock",
created: time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
libs: []deptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
detect: detect{
input: detectInput{req: &detector.LibDetectRequest{
ImageName: "/tmp/alpine.tar",
FilePath: "app/Pipfile.lock",
Created: func() *timestamp.Timestamp {
d := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
t, _ := ptypes.TimestampProto(d)
return t
}(),
Libraries: []*detector.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
output: detectOutput{
res: &detector.DetectResponse{
Vulnerabilities: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "django",
InstalledVersion: "3.0.0",
FixedVersion: "3.0.1",
Title: "RCE",
Description: "Remote Code Execution",
Severity: detector.Severity_CRITICAL,
},
},
},
},
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "django",
InstalledVersion: "3.0.0",
FixedVersion: "3.0.1",
Vulnerability: dbTypes.Vulnerability{
Title: "RCE",
Description: "Remote Code Execution",
Severity: "CRITICAL",
},
},
},
},
{
name: "Detect returns an error",
fields: fields{},
args: args{
imageName: "/tmp/alpine.tar",
filePath: "app/Pipfile.lock",
created: time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC),
libs: []deptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
detect: detect{
input: detectInput{req: &detector.LibDetectRequest{
ImageName: "/tmp/alpine.tar",
FilePath: "app/Pipfile.lock",
Libraries: []*detector.Library{
{Name: "django", Version: "3.0.0"},
},
Created: func() *timestamp.Timestamp {
d := time.Date(2019, 2, 1, 0, 0, 0, 0, time.UTC)
t, _ := ptypes.TimestampProto(d)
return t
}(),
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities via RPC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(mockDetector)
mockDetector.On("Detect", mock.Anything, tt.detect.input.req).Return(
tt.detect.output.res, tt.detect.output.err)
d := NewDetector(tt.fields.customHeaders, mockDetector)
got, err := d.Detect(tt.args.imageName, tt.args.filePath, tt.args.created, tt.args.libs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

View File

@@ -1,73 +0,0 @@
package ospkg
import (
"context"
"net/http"
"time"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
r "github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/types"
rpc "github.com/aquasecurity/trivy/rpc/detector"
)
var SuperSet = wire.NewSet(
NewProtobufClient,
NewDetector,
wire.Bind(new(detector.Operation), new(Detector)),
)
type RemoteURL string
func NewProtobufClient(remoteURL RemoteURL) rpc.OSDetector {
return rpc.NewOSDetectorProtobufClient(string(remoteURL), &http.Client{})
}
type CustomHeaders http.Header
type Detector struct {
customHeaders CustomHeaders
client rpc.OSDetector
}
func NewDetector(customHeaders CustomHeaders, detector rpc.OSDetector) Detector {
return Detector{customHeaders: customHeaders, client: detector}
}
func (d Detector) Detect(imageName, osFamily, osName string, created time.Time, pkgs []analyzer.Package) ([]types.DetectedVulnerability, bool, error) {
ctx := client.WithCustomHeaders(context.Background(), http.Header(d.customHeaders))
var res *rpc.DetectResponse
err := r.Retry(func() error {
var err error
res, err = d.client.Detect(ctx, &rpc.OSDetectRequest{
ImageName: imageName,
OsFamily: osFamily,
OsName: osName,
Created: func() *timestamp.Timestamp {
t, err := ptypes.TimestampProto(created)
if err != nil {
log.Logger.Warnf("invalid timestamp: %s", err)
}
return t
}(),
Packages: r.ConvertToRpcPkgs(pkgs),
})
return err
})
if err != nil {
return nil, false, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcVulns(res.Vulnerabilities), res.Eosl, nil
}

View File

@@ -1,202 +0,0 @@
package ospkg
import (
"context"
"testing"
"time"
"github.com/aquasecurity/fanal/analyzer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
)
type mockDetector struct {
mock.Mock
}
func (_m *mockDetector) Detect(a context.Context, b *detector.OSDetectRequest) (*detector.DetectResponse, error) {
ret := _m.Called(a, b)
ret0 := ret.Get(0)
if ret0 == nil {
return nil, ret.Error(1)
}
res, ok := ret0.(*detector.DetectResponse)
if !ok {
return nil, ret.Error(1)
}
return res, ret.Error(1)
}
func TestDetectClient_Detect(t *testing.T) {
type detectInput struct {
req *detector.OSDetectRequest
}
type detectOutput struct {
res *detector.DetectResponse
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
customHeaders CustomHeaders
}
type args struct {
imageName string
osFamily string
osName string
created time.Time
pkgs []analyzer.Package
}
tests := []struct {
name string
fields fields
args args
detect detect
want []types.DetectedVulnerability
wantErr string
}{
{
name: "happy path",
fields: fields{
customHeaders: CustomHeaders{
"Trivy-Token": []string{"token"},
},
},
args: args{
imageName: "alpine:3.10.2",
osFamily: "alpine",
osName: "3.10.2",
created: time.Unix(1581498560, 0),
pkgs: []analyzer.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
detect: detect{
input: detectInput{
req: &detector.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
ImageName: "alpine:3.10.2",
Created: func() *timestamp.Timestamp {
t, _ := ptypes.TimestampProto(time.Unix(1581498560, 0))
return t
}(),
Packages: []*detector.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
},
output: detectOutput{
res: &detector.DetectResponse{
Vulnerabilities: []*detector.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "bash",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Title: "RCE",
Description: "Remote Code Execution",
Severity: detector.Severity_HIGH,
},
},
},
},
},
want: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "bash",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
Title: "RCE",
Description: "Remote Code Execution",
Severity: "HIGH",
},
},
},
},
{
name: "Detect returns an error",
fields: fields{},
args: args{
imageName: "alpine:3.10.2",
osFamily: "alpine",
osName: "3.10.2",
created: time.Unix(1581498560, 0),
pkgs: []analyzer.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
detect: detect{
input: detectInput{
req: &detector.OSDetectRequest{
ImageName: "alpine:3.10.2",
OsFamily: "alpine",
OsName: "3.10.2",
Created: func() *timestamp.Timestamp {
t, _ := ptypes.TimestampProto(time.Unix(1581498560, 0))
return t
}(),
Packages: []*detector.Package{
{
Name: "openssl",
Version: "1.0.1e",
Release: "1",
Epoch: 0,
},
},
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
wantErr: "failed to detect vulnerabilities via RPC",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(mockDetector)
mockDetector.On("Detect", mock.Anything, tt.detect.input.req).Return(
tt.detect.output.res, tt.detect.output.err)
d := NewDetector(tt.fields.customHeaders, mockDetector)
got, _, err := d.Detect(tt.args.imageName, tt.args.osFamily, tt.args.osName, tt.args.created, tt.args.pkgs)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

View File

@@ -1,18 +1,23 @@
package rpc
import (
"github.com/aquasecurity/fanal/analyzer"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/golang/protobuf/ptypes"
ftypes "github.com/aquasecurity/fanal/types"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/aquasecurity/trivy/rpc/cache"
"github.com/aquasecurity/trivy/rpc/common"
"github.com/aquasecurity/trivy/rpc/scanner"
)
func ConvertToRpcPkgs(pkgs []analyzer.Package) []*detector.Package {
var rpcPkgs []*detector.Package
func ConvertToRpcPkgs(pkgs []ftypes.Package) []*common.Package {
var rpcPkgs []*common.Package
for _, pkg := range pkgs {
rpcPkgs = append(rpcPkgs, &detector.Package{
rpcPkgs = append(rpcPkgs, &common.Package{
Name: pkg.Name,
Version: pkg.Version,
Release: pkg.Release,
@@ -27,10 +32,10 @@ func ConvertToRpcPkgs(pkgs []analyzer.Package) []*detector.Package {
return rpcPkgs
}
func ConvertFromRpcPkgs(rpcPkgs []*detector.Package) []analyzer.Package {
var pkgs []analyzer.Package
func ConvertFromRpcPkgs(rpcPkgs []*common.Package) []ftypes.Package {
var pkgs []ftypes.Package
for _, pkg := range rpcPkgs {
pkgs = append(pkgs, analyzer.Package{
pkgs = append(pkgs, ftypes.Package{
Name: pkg.Name,
Version: pkg.Version,
Release: pkg.Release,
@@ -45,10 +50,10 @@ func ConvertFromRpcPkgs(rpcPkgs []*detector.Package) []analyzer.Package {
return pkgs
}
func ConvertFromRpcLibraries(rpcLibs []*detector.Library) []ptypes.Library {
var libs []ptypes.Library
func ConvertFromRpcLibraries(rpcLibs []*common.Library) []deptypes.Library {
var libs []deptypes.Library
for _, l := range rpcLibs {
libs = append(libs, ptypes.Library{
libs = append(libs, deptypes.Library{
Name: l.Name,
Version: l.Version,
})
@@ -56,10 +61,10 @@ func ConvertFromRpcLibraries(rpcLibs []*detector.Library) []ptypes.Library {
return libs
}
func ConvertToRpcLibraries(libs []ptypes.Library) []*detector.Library {
var rpcLibs []*detector.Library
func ConvertToRpcLibraries(libs []deptypes.Library) []*common.Library {
var rpcLibs []*common.Library
for _, l := range libs {
rpcLibs = append(rpcLibs, &detector.Library{
rpcLibs = append(rpcLibs, &common.Library{
Name: l.Name,
Version: l.Version,
})
@@ -67,7 +72,7 @@ func ConvertToRpcLibraries(libs []ptypes.Library) []*detector.Library {
return rpcLibs
}
func ConvertFromRpcVulns(rpcVulns []*detector.Vulnerability) []types.DetectedVulnerability {
func ConvertFromRpcVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
var vulns []types.DetectedVulnerability
for _, vuln := range rpcVulns {
severity := dbTypes.Severity(vuln.Severity)
@@ -87,24 +92,205 @@ func ConvertFromRpcVulns(rpcVulns []*detector.Vulnerability) []types.DetectedVul
return vulns
}
func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*detector.Vulnerability {
var rpcVulns []*detector.Vulnerability
func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerability {
var rpcVulns []*common.Vulnerability
for _, vuln := range vulns {
severity, err := dbTypes.NewSeverity(vuln.Severity)
if err != nil {
log.Logger.Warn(err)
}
rpcVulns = append(rpcVulns, &detector.Vulnerability{
rpcVulns = append(rpcVulns, &common.Vulnerability{
VulnerabilityId: vuln.VulnerabilityID,
PkgName: vuln.PkgName,
InstalledVersion: vuln.InstalledVersion,
FixedVersion: vuln.FixedVersion,
Title: vuln.Title,
Description: vuln.Description,
Severity: detector.Severity(severity),
Severity: common.Severity(severity),
References: vuln.References,
})
}
return rpcVulns
}
func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
var results []report.Result
for _, result := range rpcResults {
var vulns []types.DetectedVulnerability
for _, vuln := range result.Vulnerabilities {
severity := dbTypes.Severity(vuln.Severity)
vulns = append(vulns, types.DetectedVulnerability{
VulnerabilityID: vuln.VulnerabilityId,
PkgName: vuln.PkgName,
InstalledVersion: vuln.InstalledVersion,
FixedVersion: vuln.FixedVersion,
Vulnerability: dbTypes.Vulnerability{
Title: vuln.Title,
Description: vuln.Description,
Severity: severity.String(),
References: vuln.References,
},
})
}
results = append(results, report.Result{
Target: result.Target,
Vulnerabilities: vulns,
})
}
return results
}
func ConvertFromRpcOS(rpcOS *common.OS) *ftypes.OS {
if rpcOS == nil {
return nil
}
return &ftypes.OS{
Family: rpcOS.Family,
Name: rpcOS.Name,
}
}
func ConvertFromRpcPackageInfos(rpcPkgInfos []*common.PackageInfo) []ftypes.PackageInfo {
var pkgInfos []ftypes.PackageInfo
for _, rpcPkgInfo := range rpcPkgInfos {
pkgInfos = append(pkgInfos, ftypes.PackageInfo{
FilePath: rpcPkgInfo.FilePath,
Packages: ConvertFromRpcPkgs(rpcPkgInfo.Packages),
})
}
return pkgInfos
}
func ConvertFromRpcApplications(rpcApps []*common.Application) []ftypes.Application {
var apps []ftypes.Application
for _, rpcApp := range rpcApps {
apps = append(apps, ftypes.Application{
Type: rpcApp.Type,
FilePath: rpcApp.FilePath,
Libraries: ConvertFromRpcLibraries(rpcApp.Libraries),
})
}
return apps
}
func ConvertFromRpcPutImageRequest(req *cache.PutImageRequest) ftypes.ImageInfo {
created, _ := ptypes.Timestamp(req.ImageInfo.Created)
return ftypes.ImageInfo{
SchemaVersion: int(req.ImageInfo.SchemaVersion),
Architecture: req.ImageInfo.Architecture,
Created: created,
DockerVersion: req.ImageInfo.DockerVersion,
OS: req.ImageInfo.Os,
HistoryPackages: ConvertFromRpcPkgs(req.ImageInfo.HistoryPackages),
}
}
func ConvertFromRpcPutLayerRequest(req *cache.PutLayerRequest) ftypes.LayerInfo {
return ftypes.LayerInfo{
SchemaVersion: int(req.LayerInfo.SchemaVersion),
OS: ConvertFromRpcOS(req.LayerInfo.Os),
PackageInfos: ConvertFromRpcPackageInfos(req.LayerInfo.PackageInfos),
Applications: ConvertFromRpcApplications(req.LayerInfo.Applications),
OpaqueDirs: req.LayerInfo.OpaqueDirs,
WhiteoutFiles: req.LayerInfo.WhiteoutFiles,
}
}
func ConvertToRpcOS(fos *ftypes.OS) *common.OS {
if fos == nil {
return nil
}
return &common.OS{
Family: fos.Family,
Name: fos.Name,
}
}
func ConvertToRpcImageInfo(imageID string, imageInfo ftypes.ImageInfo) *cache.PutImageRequest {
t, err := ptypes.TimestampProto(imageInfo.Created)
if err != nil {
log.Logger.Warnf("invalid timestamp: %s", err)
}
return &cache.PutImageRequest{
ImageId: imageID,
ImageInfo: &cache.ImageInfo{
SchemaVersion: int32(imageInfo.SchemaVersion),
Architecture: imageInfo.Architecture,
Created: t,
DockerVersion: imageInfo.DockerVersion,
Os: imageInfo.OS,
HistoryPackages: ConvertToRpcPkgs(imageInfo.HistoryPackages),
},
}
}
func ConvertToRpcLayerInfo(layerID, decompressedLayerID string, layerInfo ftypes.LayerInfo) *cache.PutLayerRequest {
var packageInfos []*common.PackageInfo
for _, pkgInfo := range layerInfo.PackageInfos {
packageInfos = append(packageInfos, &common.PackageInfo{
FilePath: pkgInfo.FilePath,
Packages: ConvertToRpcPkgs(pkgInfo.Packages),
})
}
var applications []*common.Application
for _, app := range layerInfo.Applications {
var libs []*common.Library
for _, lib := range app.Libraries {
libs = append(libs, &common.Library{
Name: lib.Name,
Version: lib.Version,
})
}
applications = append(applications, &common.Application{
Type: app.Type,
FilePath: app.FilePath,
Libraries: libs,
})
}
return &cache.PutLayerRequest{
LayerId: layerID,
DecompressedLayerId: decompressedLayerID,
LayerInfo: &cache.LayerInfo{
SchemaVersion: ftypes.LayerJSONSchemaVersion,
Os: ConvertToRpcOS(layerInfo.OS),
PackageInfos: packageInfos,
Applications: applications,
OpaqueDirs: layerInfo.OpaqueDirs,
WhiteoutFiles: layerInfo.WhiteoutFiles,
},
}
}
func ConvertToMissingLayersRequest(imageID string, layerIDs []string) *cache.MissingLayersRequest {
return &cache.MissingLayersRequest{
ImageId: imageID,
LayerIds: layerIDs,
}
}
func ConvertToRpcScanResponse(results report.Results, os *ftypes.OS, eosl bool) *scanner.ScanResponse {
rpcOS := &common.OS{}
if os != nil {
rpcOS.Family = os.Family
rpcOS.Name = os.Name
}
var rpcResults []*scanner.Result
for _, result := range results {
rpcResults = append(rpcResults, &scanner.Result{
Target: result.Target,
Vulnerabilities: ConvertToRpcVulns(result.Vulnerabilities),
})
}
return &scanner.ScanResponse{
Os: rpcOS,
Eosl: eosl,
Results: rpcResults,
}
}

View File

@@ -4,13 +4,15 @@ import (
"os"
"testing"
"github.com/aquasecurity/trivy/rpc/common"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/fanal/analyzer"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/rpc/detector"
"github.com/stretchr/testify/assert"
)
@@ -22,17 +24,17 @@ func TestMain(m *testing.M) {
func TestConvertToRpcPkgs(t *testing.T) {
type args struct {
pkgs []analyzer.Package
pkgs []ftypes.Package
}
tests := []struct {
name string
args args
want []*detector.Package
want []*common.Package
}{
{
name: "happy path",
args: args{
pkgs: []analyzer.Package{
pkgs: []ftypes.Package{
{
Name: "binary",
Version: "1.2.3",
@@ -46,7 +48,7 @@ func TestConvertToRpcPkgs(t *testing.T) {
},
},
},
want: []*detector.Package{
want: []*common.Package{
{
Name: "binary",
Version: "1.2.3",
@@ -71,16 +73,16 @@ func TestConvertToRpcPkgs(t *testing.T) {
func TestConvertFromRpcPkgs(t *testing.T) {
type args struct {
rpcPkgs []*detector.Package
rpcPkgs []*common.Package
}
tests := []struct {
name string
args args
want []analyzer.Package
want []ftypes.Package
}{
{
args: args{
rpcPkgs: []*detector.Package{
rpcPkgs: []*common.Package{
{
Name: "binary",
Version: "1.2.3",
@@ -94,7 +96,7 @@ func TestConvertFromRpcPkgs(t *testing.T) {
},
},
},
want: []analyzer.Package{
want: []ftypes.Package{
{
Name: "binary",
Version: "1.2.3",
@@ -119,7 +121,7 @@ func TestConvertFromRpcPkgs(t *testing.T) {
func TestConvertFromRpcLibraries(t *testing.T) {
type args struct {
rpcLibs []*detector.Library
rpcLibs []*common.Library
}
tests := []struct {
name string
@@ -129,7 +131,7 @@ func TestConvertFromRpcLibraries(t *testing.T) {
{
name: "happy path",
args: args{
rpcLibs: []*detector.Library{
rpcLibs: []*common.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
@@ -155,7 +157,7 @@ func TestConvertToRpcLibraries(t *testing.T) {
tests := []struct {
name string
args args
want []*detector.Library
want []*common.Library
}{
{
name: "happy path",
@@ -165,7 +167,7 @@ func TestConvertToRpcLibraries(t *testing.T) {
{Name: "bar", Version: "4.5.6"},
},
},
want: []*detector.Library{
want: []*common.Library{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "4.5.6"},
},
@@ -181,7 +183,7 @@ func TestConvertToRpcLibraries(t *testing.T) {
func TestConvertFromRpcVulns(t *testing.T) {
type args struct {
rpcVulns []*detector.Vulnerability
rpcVulns []*common.Vulnerability
}
tests := []struct {
name string
@@ -191,7 +193,7 @@ func TestConvertFromRpcVulns(t *testing.T) {
{
name: "happy path",
args: args{
rpcVulns: []*detector.Vulnerability{
rpcVulns: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "foo",
@@ -199,7 +201,7 @@ func TestConvertFromRpcVulns(t *testing.T) {
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_CRITICAL,
Severity: common.Severity_CRITICAL,
References: []string{"http://example.com"},
},
},
@@ -235,7 +237,7 @@ func TestConvertToRpcVulns(t *testing.T) {
tests := []struct {
name string
args args
want []*detector.Vulnerability
want []*common.Vulnerability
}{
{
name: "happy path",
@@ -255,7 +257,7 @@ func TestConvertToRpcVulns(t *testing.T) {
},
},
},
want: []*detector.Vulnerability{
want: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "foo",
@@ -263,7 +265,7 @@ func TestConvertToRpcVulns(t *testing.T) {
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_MEDIUM,
Severity: common.Severity_MEDIUM,
References: []string{"http://example.com"},
},
},
@@ -286,7 +288,7 @@ func TestConvertToRpcVulns(t *testing.T) {
},
},
},
want: []*detector.Vulnerability{
want: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0002",
PkgName: "bar",
@@ -294,7 +296,7 @@ func TestConvertToRpcVulns(t *testing.T) {
FixedVersion: "1.2.4",
Title: "DoS",
Description: "Denial of Service",
Severity: detector.Severity_UNKNOWN,
Severity: common.Severity_UNKNOWN,
References: []string{"http://example.com"},
},
},

View File

@@ -3,12 +3,17 @@
package server
import (
"github.com/google/wire"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/rpc/server/library"
"github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
"github.com/google/wire"
)
func initializeScanServer(localLayerCache cache.LocalImageCache) *ScanServer {
wire.Build(ScanSuperSet)
return &ScanServer{}
}
func initializeOspkgServer() *ospkg.Server {
wire.Build(ospkg.SuperSet)
return &ospkg.Server{}
@@ -20,6 +25,6 @@ func initializeLibServer() *library.Server {
}
func initializeDBWorker(quiet bool) dbWorker {
wire.Build(SuperSet)
wire.Build(DBWorkerSuperSet)
return dbWorker{}
}

View File

@@ -20,6 +20,7 @@ var SuperSet = wire.NewSet(
NewServer,
)
// Server is for backward compatibility
type Server struct {
detector detector.Operation
vulnClient vulnerability.Operation
@@ -29,6 +30,7 @@ func NewServer(detector detector.Operation, vulnClient vulnerability.Operation)
return &Server{detector: detector, vulnClient: vulnClient}
}
// Detect is for backward compatibility
func (s *Server) Detect(_ context.Context, req *proto.LibDetectRequest) (res *proto.DetectResponse, err error) {
vulns, err := s.detector.Detect("", req.FilePath, time.Time{}, rpc.ConvertFromRpcLibraries(req.Libraries))
if err != nil {

View File

@@ -5,19 +5,17 @@ import (
"os"
"testing"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/log"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/aquasecurity/trivy/rpc/common"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
@@ -32,11 +30,12 @@ func TestServer_Detect(t *testing.T) {
req *proto.LibDetectRequest
}
tests := []struct {
name string
args args
detect library.DetectExpectation
wantRes *proto.DetectResponse
wantErr string
name string
args args
detectExpectation library.DetectExpectation
fillInfoExpectation vulnerability.FillInfoExpectation
wantRes *proto.DetectResponse
wantErr string
}{
{
name: "happy path",
@@ -44,12 +43,12 @@ func TestServer_Detect(t *testing.T) {
req: &proto.LibDetectRequest{
ImageName: "alpine:3.10",
FilePath: "app/Pipfile.lock",
Libraries: []*proto.Library{
Libraries: []*common.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
detect: library.DetectExpectation{
detectExpectation: library.DetectExpectation{
Args: library.DetectInput{
FilePath: "app/Pipfile.lock",
Libs: []ptypes.Library{
@@ -73,8 +72,27 @@ func TestServer_Detect(t *testing.T) {
},
},
},
fillInfoExpectation: vulnerability.FillInfoExpectation{
Args: vulnerability.FillInfoArgs{
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "test",
InstalledVersion: "1",
FixedVersion: "2",
Vulnerability: dbTypes.Vulnerability{
Title: "title",
Description: "description",
Severity: "MEDIUM",
References: []string{"http://example.com"},
},
},
},
Light: false,
},
},
wantRes: &proto.DetectResponse{
Vulnerabilities: []*proto.Vulnerability{
Vulnerabilities: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "test",
@@ -82,7 +100,7 @@ func TestServer_Detect(t *testing.T) {
FixedVersion: "2",
Title: "title",
Description: "description",
Severity: proto.Severity_MEDIUM,
Severity: common.Severity_MEDIUM,
References: []string{"http://example.com"},
},
},
@@ -94,12 +112,12 @@ func TestServer_Detect(t *testing.T) {
req: &proto.LibDetectRequest{
ImageName: "alpine:3.10",
FilePath: "app/Pipfile.lock",
Libraries: []*proto.Library{
Libraries: []*common.Library{
{Name: "django", Version: "3.0.0"},
},
},
},
detect: library.DetectExpectation{
detectExpectation: library.DetectExpectation{
Args: library.DetectInput{
FilePath: "app/Pipfile.lock",
Libs: []ptypes.Library{
@@ -115,8 +133,9 @@ func TestServer_Detect(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := library.NewMockDetector([]library.DetectExpectation{tt.detect})
mockVulnClient := vulnerability.NewMockVulnClient()
mockDetector := library.NewMockDetector([]library.DetectExpectation{tt.detectExpectation})
mockVulnClient := new(vulnerability.MockOperation)
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
s := NewServer(mockDetector, mockVulnClient)
ctx := context.TODO()

148
pkg/rpc/server/listen.go Normal file
View File

@@ -0,0 +1,148 @@
package server
import (
"context"
"io/ioutil"
"net/http"
"os"
"sync"
"time"
"github.com/google/wire"
"github.com/twitchtv/twirp"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/server/config"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
"github.com/aquasecurity/trivy/rpc/detector"
rpcDetector "github.com/aquasecurity/trivy/rpc/detector"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
var DBWorkerSuperSet = wire.NewSet(
dbFile.SuperSet,
newDBWorker,
)
func ListenAndServe(c config.Config, fsCache cache.FSCache) error {
requestWg := &sync.WaitGroup{}
dbUpdateWg := &sync.WaitGroup{}
withWaitGroup := func(base http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Stop processing requests during DB update
dbUpdateWg.Wait()
// Wait for all requests to be processed before DB update
requestWg.Add(1)
defer requestWg.Done()
base.ServeHTTP(w, r)
})
}
go func() {
worker := initializeDBWorker(true)
ctx := context.Background()
for {
time.Sleep(1 * time.Hour)
if err := worker.update(ctx, c.AppVersion, c.CacheDir, dbUpdateWg, requestWg); err != nil {
log.Logger.Errorf("%+v\n", err)
}
}
}()
mux := http.NewServeMux()
scanHandler := rpcScanner.NewScannerServer(initializeScanServer(fsCache), nil)
mux.Handle(rpcScanner.ScannerPathPrefix, withToken(withWaitGroup(scanHandler), c.Token, c.TokenHeader))
layerHandler := rpcCache.NewCacheServer(NewCacheServer(fsCache), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(withWaitGroup(layerHandler), c.Token, c.TokenHeader))
// osHandler is for backward compatibility
osHandler := rpcDetector.NewOSDetectorServer(initializeOspkgServer(), nil)
mux.Handle(rpcDetector.OSDetectorPathPrefix, withToken(withWaitGroup(osHandler), c.Token, c.TokenHeader))
// libHandler is for backward compatibility
libHandler := rpcDetector.NewLibDetectorServer(initializeLibServer(), nil)
mux.Handle(rpcDetector.LibDetectorPathPrefix, withToken(withWaitGroup(libHandler), c.Token, c.TokenHeader))
log.Logger.Infof("Listening %s...", c.Listen)
return http.ListenAndServe(c.Listen, mux)
}
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get(tokenHeader) {
detector.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
})
}
type dbWorker struct {
dbClient dbFile.Operation
}
func newDBWorker(dbClient dbFile.Operation) dbWorker {
return dbWorker{dbClient: dbClient}
}
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
dbUpdateWg, requestWg *sync.WaitGroup) error {
needsUpdate, err := w.dbClient.NeedsUpdate(ctx, appVersion, false, false)
if err != nil {
return xerrors.Errorf("failed to check if db needs an update")
} else if !needsUpdate {
return nil
}
log.Logger.Info("Updating DB...")
if err = w.hotUpdate(ctx, cacheDir, dbUpdateWg, requestWg); err != nil {
return xerrors.Errorf("failed DB hot update")
}
return nil
}
func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, requestWg *sync.WaitGroup) error {
tmpDir, err := ioutil.TempDir("", "db")
if err != nil {
return xerrors.Errorf("failed to create a temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
if err := w.dbClient.Download(ctx, tmpDir, false); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
log.Logger.Info("Suspending all requests during DB update")
dbUpdateWg.Add(1)
defer dbUpdateWg.Done()
log.Logger.Info("Waiting for all requests to be processed before DB update...")
requestWg.Wait()
if err = db.Close(); err != nil {
return xerrors.Errorf("failed to close DB: %w", err)
}
if _, err = utils.CopyFile(db.Path(tmpDir), db.Path(cacheDir)); err != nil {
return xerrors.Errorf("failed to copy the database file: %w", err)
}
log.Logger.Info("Reopening DB...")
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("failed to open DB: %w", err)
}
return nil
}

View File

@@ -0,0 +1,157 @@
package server
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
}
func Test_dbWorker_update(t *testing.T) {
type needsUpdateInput struct {
appVersion string
skip bool
}
type needsUpdateOutput struct {
needsUpdate bool
err error
}
type needsUpdate struct {
input needsUpdateInput
output needsUpdateOutput
}
type download struct {
call bool
err error
}
type args struct {
appVersion string
}
tests := []struct {
name string
needsUpdate needsUpdate
download download
args args
want db.Metadata
wantErr string
}{
{
name: "happy path",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
},
download: download{
call: true,
},
args: args{appVersion: "1"},
want: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
{
name: "not update",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: false},
},
args: args{appVersion: "1"},
},
{
name: "NeedsUpdate returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{err: xerrors.New("fail")},
},
args: args{appVersion: "1"},
wantErr: "failed to check if db needs an update",
},
{
name: "Download returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
},
download: download{
call: true,
err: xerrors.New("fail"),
},
args: args{appVersion: "1"},
wantErr: "failed DB hot update",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir, err := ioutil.TempDir("", "server-test")
require.NoError(t, err, tt.name)
require.NoError(t, db.Init(cacheDir), tt.name)
mockDBClient := new(dbFile.MockClient)
mockDBClient.On("NeedsUpdate", mock.Anything,
tt.needsUpdate.input.appVersion, false, tt.needsUpdate.input.skip).Return(
tt.needsUpdate.output.needsUpdate, tt.needsUpdate.output.err)
if tt.download.call {
mockDBClient.On("Download", mock.Anything, mock.Anything, false).Run(
func(args mock.Arguments) {
// fake download: copy testdata/new.db to tmpDir/db/trivy.db
content, err := ioutil.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 = ioutil.WriteFile(dbPath, content, 0444)
require.NoError(t, err, tt.name)
}).Return(tt.download.err)
}
w := newDBWorker(mockDBClient)
var dbUpdateWg, requestWg sync.WaitGroup
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)
}
if !tt.download.call {
return
}
dbc := db.Config{}
got, err := dbc.GetMetadata()
assert.NoError(t, err, tt.name)
assert.Equal(t, tt.want, got, tt.name)
mockDBClient.AssertExpectations(t)
})
}
}

View File

@@ -20,6 +20,7 @@ var SuperSet = wire.NewSet(
NewServer,
)
// Server is for backward compatibility
type Server struct {
detector detector.Operation
vulnClient vulnerability.Operation
@@ -29,7 +30,8 @@ func NewServer(detector detector.Operation, vulnClient vulnerability.Operation)
return &Server{detector: detector, vulnClient: vulnClient}
}
func (s *Server) Detect(ctx context.Context, req *proto.OSDetectRequest) (res *proto.DetectResponse, err error) {
// Detect is for backward compatibility
func (s *Server) Detect(_ context.Context, req *proto.OSDetectRequest) (res *proto.DetectResponse, err error) {
vulns, eosl, err := s.detector.Detect("", req.OsFamily, req.OsName, time.Time{}, rpc.ConvertFromRpcPkgs(req.Packages))
if err != nil {
err = xerrors.Errorf("failed to detect vulnerabilities of OS packages: %w", err)

View File

@@ -9,12 +9,13 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/aquasecurity/trivy/rpc/common"
proto "github.com/aquasecurity/trivy/rpc/detector"
)
@@ -29,11 +30,12 @@ func TestServer_Detect(t *testing.T) {
req *proto.OSDetectRequest
}
tests := []struct {
name string
args args
detect ospkg.DetectExpectation
wantRes *proto.DetectResponse
wantErr string
name string
args args
detectExpectation ospkg.DetectExpectation
fillInfoExpectation vulnerability.FillInfoExpectation
wantRes *proto.DetectResponse
wantErr string
}{
{
name: "happy path",
@@ -41,16 +43,16 @@ func TestServer_Detect(t *testing.T) {
req: &proto.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*proto.Package{
Packages: []*common.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
},
detect: ospkg.DetectExpectation{
detectExpectation: ospkg.DetectExpectation{
Args: ospkg.DetectInput{
OSFamily: "alpine",
OSName: "3.10.2",
Pkgs: []analyzer.Package{
Pkgs: []ftypes.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
@@ -66,12 +68,25 @@ func TestServer_Detect(t *testing.T) {
},
},
},
fillInfoExpectation: vulnerability.FillInfoExpectation{
Args: vulnerability.FillInfoArgs{
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "musl",
Vulnerability: dbTypes.Vulnerability{
Severity: "HIGH",
}},
},
Light: false,
},
},
wantRes: &proto.DetectResponse{
Vulnerabilities: []*proto.Vulnerability{
Vulnerabilities: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "musl",
Severity: proto.Severity_HIGH,
Severity: common.Severity_HIGH,
},
},
},
@@ -82,16 +97,16 @@ func TestServer_Detect(t *testing.T) {
req: &proto.OSDetectRequest{
OsFamily: "alpine",
OsName: "3.10.2",
Packages: []*proto.Package{
Packages: []*common.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
},
detect: ospkg.DetectExpectation{
detectExpectation: ospkg.DetectExpectation{
Args: ospkg.DetectInput{
OSFamily: "alpine",
OSName: "3.10.2",
Pkgs: []analyzer.Package{
Pkgs: []ftypes.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
@@ -104,8 +119,9 @@ func TestServer_Detect(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := ospkg.NewMockDetector([]ospkg.DetectExpectation{tt.detect})
mockVulnClient := vulnerability.NewMockVulnClient()
mockDetector := ospkg.NewMockDetector([]ospkg.DetectExpectation{tt.detectExpectation})
mockVulnClient := new(vulnerability.MockOperation)
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
s := NewServer(mockDetector, mockVulnClient)
gotRes, err := s.Detect(context.TODO(), tt.args.req)

View File

@@ -2,136 +2,94 @@ package server
import (
"context"
"io/ioutil"
"net/http"
"os"
"sync"
"time"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/google/wire"
"github.com/twitchtv/twirp"
digest "github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/internal/server/config"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
rpc "github.com/aquasecurity/trivy/rpc/detector"
"github.com/aquasecurity/fanal/cache"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
var SuperSet = wire.NewSet(
dbFile.SuperSet,
newDBWorker,
var ScanSuperSet = wire.NewSet(
local.SuperSet,
wire.Bind(new(scanner.Driver), new(local.Scanner)),
vulnerability.SuperSet,
NewScanServer,
)
func ListenAndServe(addr string, c config.Config) error {
requestWg := &sync.WaitGroup{}
dbUpdateWg := &sync.WaitGroup{}
withWaitGroup := func(base http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Stop processing requests during DB update
dbUpdateWg.Wait()
// Wait for all requests to be processed before DB update
requestWg.Add(1)
defer requestWg.Done()
base.ServeHTTP(w, r)
})
}
go func() {
worker := initializeDBWorker(true)
ctx := context.Background()
for {
time.Sleep(1 * time.Hour)
if err := worker.update(ctx, c.AppVersion, c.CacheDir, dbUpdateWg, requestWg); err != nil {
log.Logger.Errorf("%+v\n", err)
}
}
}()
mux := http.NewServeMux()
osHandler := rpc.NewOSDetectorServer(initializeOspkgServer(), nil)
mux.Handle(rpc.OSDetectorPathPrefix, withToken(withWaitGroup(osHandler), c.Token, c.TokenHeader))
libHandler := rpc.NewLibDetectorServer(initializeLibServer(), nil)
mux.Handle(rpc.LibDetectorPathPrefix, withToken(withWaitGroup(libHandler), c.Token, c.TokenHeader))
log.Logger.Infof("Listening %s...", addr)
return http.ListenAndServe(addr, mux)
type ScanServer struct {
localScanner scanner.Driver
vulnClient vulnerability.Operation
}
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get(tokenHeader) {
rpc.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
})
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Operation) *ScanServer {
return &ScanServer{localScanner: s, vulnClient: vulnClient}
}
type dbWorker struct {
dbClient dbFile.Operation
}
func newDBWorker(dbClient dbFile.Operation) dbWorker {
return dbWorker{dbClient: dbClient}
}
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
dbUpdateWg, requestWg *sync.WaitGroup) error {
needsUpdate, err := w.dbClient.NeedsUpdate(ctx, appVersion, false, false)
func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
options := types.ScanOptions{VulnType: in.Options.VulnType}
results, os, eosl, err := s.localScanner.Scan(in.Target, digest.Digest(in.ImageId), in.LayerIds, options)
if err != nil {
return xerrors.Errorf("failed to check if db needs an update")
} else if !needsUpdate {
return nil
return nil, xerrors.Errorf("failed scan, %s: %w", in.Target, err)
}
log.Logger.Info("Updating DB...")
if err = w.hotUpdate(ctx, cacheDir, dbUpdateWg, requestWg); err != nil {
return xerrors.Errorf("failed DB hot update")
for i := range results {
s.vulnClient.FillInfo(results[i].Vulnerabilities, false)
}
return nil
return rpc.ConvertToRpcScanResponse(results, os, eosl), nil
}
func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, requestWg *sync.WaitGroup) error {
tmpDir, err := ioutil.TempDir("", "db")
if err != nil {
return xerrors.Errorf("failed to create a temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
if err := w.dbClient.Download(ctx, tmpDir, false); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
log.Logger.Info("Suspending all requests during DB update")
dbUpdateWg.Add(1)
defer dbUpdateWg.Done()
log.Logger.Info("Waiting for all requests to be processed before DB update...")
requestWg.Wait()
if err = db.Close(); err != nil {
return xerrors.Errorf("failed to close DB: %w", err)
}
if _, err = utils.CopyFile(db.Path(tmpDir), db.Path(cacheDir)); err != nil {
return xerrors.Errorf("failed to copy the database file: %w", err)
}
log.Logger.Info("Reopening DB...")
if err = db.Init(cacheDir); err != nil {
return xerrors.Errorf("failed to open DB: %w", err)
}
return nil
type CacheServer struct {
cache cache.Cache
}
func NewCacheServer(c cache.Cache) *CacheServer {
return &CacheServer{cache: c}
}
func (s *CacheServer) PutImage(_ context.Context, in *rpcCache.PutImageRequest) (*google_protobuf.Empty, error) {
if in.ImageInfo == nil {
return nil, xerrors.Errorf("empty image info")
}
imageInfo := rpc.ConvertFromRpcPutImageRequest(in)
if err := s.cache.PutImage(in.ImageId, imageInfo); err != nil {
return nil, xerrors.Errorf("unable to store image info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
func (s *CacheServer) PutLayer(_ context.Context, in *rpcCache.PutLayerRequest) (*google_protobuf.Empty, error) {
if in.LayerInfo == nil {
return nil, xerrors.Errorf("empty layer info")
}
layerInfo := rpc.ConvertFromRpcPutLayerRequest(in)
if err := s.cache.PutLayer(in.LayerId, in.DecompressedLayerId, layerInfo); err != nil {
return nil, xerrors.Errorf("unable to store layer info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
func (s *CacheServer) MissingLayers(_ context.Context, in *rpcCache.MissingLayersRequest) (*rpcCache.MissingLayersResponse, error) {
var layerIDs []string
for _, layerID := range in.LayerIds {
l, err := s.cache.GetLayer(layerID)
if err != nil || l.SchemaVersion != ftypes.LayerJSONSchemaVersion {
layerIDs = append(layerIDs, layerID)
}
}
var missingImage bool
img, err := s.cache.GetImage(in.ImageId)
if err != nil || img.SchemaVersion != ftypes.ImageJSONSchemaVersion {
missingImage = true
}
return &rpcCache.MissingLayersResponse{MissingImage: missingImage, MissingLayerIds: layerIDs}, nil
}

View File

@@ -2,138 +2,255 @@ package server
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"sync"
"errors"
"testing"
"time"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/golang/protobuf/ptypes"
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
dbFile "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/fanal/cache"
ftypes "github.com/aquasecurity/fanal/types"
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/vulnerability"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
"github.com/aquasecurity/trivy/rpc/common"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
os.Exit(m.Run())
type mockCache struct {
cache.MockImageCache
cache.MockLocalImageCache
}
func Test_dbWorker_update(t *testing.T) {
type needsUpdateInput struct {
appVersion string
skip bool
}
type needsUpdateOutput struct {
needsUpdate bool
err error
}
type needsUpdate struct {
input needsUpdateInput
output needsUpdateOutput
}
type download struct {
call bool
err error
}
func TestScanServer_Scan(t *testing.T) {
type args struct {
appVersion string
in *rpcScanner.ScanRequest
}
tests := []struct {
name string
needsUpdate needsUpdate
download download
args args
want db.Metadata
wantErr string
name string
args args
scanExpectation scanner.ScanExpectation
fillInfoExpectation vulnerability.FillInfoExpectation
want *rpcScanner.ScanResponse
wantErr string
}{
{
name: "happy path",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
args: args{
in: &rpcScanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
},
},
download: download{
call: true,
scanExpectation: scanner.ScanExpectation{
Args: scanner.ScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: scanner.ScanReturns{
Results: report.Results{
{
Target: "alpine:3.11 (alpine 3.11)",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{},
},
},
},
},
OsFound: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
},
},
args: args{appVersion: "1"},
want: db.Metadata{
Version: 1,
Type: db.TypeFull,
NextUpdate: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
UpdatedAt: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
fillInfoExpectation: vulnerability.FillInfoExpectation{
Args: vulnerability.FillInfoArgs{
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{},
},
},
Light: false,
},
},
want: &rpcScanner.ScanResponse{
Os: &common.OS{
Family: "alpine",
Name: "3.11",
},
Eosl: false,
Results: []*rpcScanner.Result{
{
Target: "alpine:3.11 (alpine 3.11)",
Vulnerabilities: []*common.Vulnerability{
{
VulnerabilityId: "CVE-2019-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
},
},
},
},
},
},
{
name: "not update",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: false},
name: "sad path: Scan returns an error",
args: args{
in: &rpcScanner.ScanRequest{
Target: "alpine:3.11",
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: &rpcScanner.ScanOptions{},
},
},
args: args{appVersion: "1"},
scanExpectation: scanner.ScanExpectation{
Args: scanner.ScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: scanner.ScanReturns{
Err: errors.New("error"),
},
},
wantErr: "failed scan, alpine:3.11",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDriver := new(scanner.MockDriver)
mockDriver.ApplyScanExpectation(tt.scanExpectation)
mockVulnClient := new(vulnerability.MockOperation)
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
s := NewScanServer(mockDriver, mockVulnClient)
got, err := s.Scan(context.Background(), tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got)
})
}
}
func TestCacheServer_PutImage(t *testing.T) {
type args struct {
in *rpcCache.PutImageRequest
}
tests := []struct {
name string
args args
putImage cache.ImageCachePutImageExpectation
want *google_protobuf.Empty
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.PutImageRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: &rpcCache.ImageInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: func() *timestamp.Timestamp {
d := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC)
t, _ := ptypes.TimestampProto(d)
return t
}(),
DockerVersion: "18.09",
Os: "linux",
},
},
},
putImage: cache.ImageCachePutImageExpectation{
Args: cache.ImageCachePutImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: ftypes.ImageInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
DockerVersion: "18.09",
OS: "linux",
},
},
},
want: &google_protobuf.Empty{},
},
{
name: "NeedsUpdate returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{err: xerrors.New("fail")},
name: "sad path",
args: args{
in: &rpcCache.PutImageRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: &rpcCache.ImageInfo{
SchemaVersion: 1,
Created: func() *timestamp.Timestamp {
d := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC)
t, _ := ptypes.TimestampProto(d)
return t
}(),
},
},
},
args: args{appVersion: "1"},
wantErr: "failed to check if db needs an update",
putImage: cache.ImageCachePutImageExpectation{
Args: cache.ImageCachePutImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
ImageInfo: ftypes.ImageInfo{
SchemaVersion: 1,
Created: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
},
},
Returns: cache.ImageCachePutImageReturns{
Err: xerrors.New("error"),
},
},
wantErr: "unable to store image info in cache",
},
{
name: "Download returns an error",
needsUpdate: needsUpdate{
input: needsUpdateInput{appVersion: "1", skip: false},
output: needsUpdateOutput{needsUpdate: true},
name: "sad path: empty image info",
args: args{
in: &rpcCache.PutImageRequest{},
},
download: download{
call: true,
err: xerrors.New("fail"),
},
args: args{appVersion: "1"},
wantErr: "failed DB hot update",
wantErr: "empty image info",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir, err := ioutil.TempDir("", "server-test")
require.NoError(t, err, tt.name)
mockCache := new(mockCache)
mockCache.ApplyPutImageExpectation(tt.putImage)
require.NoError(t, db.Init(cacheDir), tt.name)
s := NewCacheServer(mockCache)
got, err := s.PutImage(context.Background(), tt.args.in)
mockDBClient := new(dbFile.MockClient)
mockDBClient.On("NeedsUpdate", mock.Anything,
tt.needsUpdate.input.appVersion, false, tt.needsUpdate.input.skip).Return(
tt.needsUpdate.output.needsUpdate, tt.needsUpdate.output.err)
if tt.download.call {
mockDBClient.On("Download", mock.Anything, mock.Anything, false).Run(
func(args mock.Arguments) {
// fake download: copy testdata/new.db to tmpDir/db/trivy.db
content, err := ioutil.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 = ioutil.WriteFile(dbPath, content, 0444)
require.NoError(t, err, tt.name)
}).Return(tt.download.err)
}
w := newDBWorker(mockDBClient)
var dbUpdateWg, requestWg sync.WaitGroup
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)
@@ -142,16 +259,337 @@ func Test_dbWorker_update(t *testing.T) {
assert.NoError(t, err, tt.name)
}
if !tt.download.call {
return
}
dbc := db.Config{}
got, err := dbc.GetMetadata()
assert.NoError(t, err, tt.name)
assert.Equal(t, tt.want, got, tt.name)
mockDBClient.AssertExpectations(t)
assert.Equal(t, tt.want, got)
})
}
}
func TestCacheServer_PutLayer(t *testing.T) {
type args struct {
in *rpcCache.PutLayerRequest
}
tests := []struct {
name string
args args
putLayer cache.ImageCachePutLayerExpectation
want *google_protobuf.Empty
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.PutLayerRequest{
LayerId: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
DecompressedLayerId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
LayerInfo: &rpcCache.LayerInfo{
SchemaVersion: 1,
Os: &common.OS{
Family: "alpine",
Name: "3.11",
},
PackageInfos: []*common.PackageInfo{
{
FilePath: "lib/apk/db/installed",
Packages: []*common.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
{
Name: "vim-minimal",
Version: "7.4.160",
Release: "5.el7",
Epoch: 2,
Arch: "x86_64",
SrcName: "vim",
SrcVersion: "7.4.160",
SrcRelease: "5.el7",
SrcEpoch: 2,
},
},
},
},
Applications: []*common.Application{
{
Type: "composer",
FilePath: "php-app/composer.lock",
Libraries: []*common.Library{
{
Name: "guzzlehttp/guzzle",
Version: "6.2.0",
},
{
Name: "guzzlehttp/promises",
Version: "v1.3.1",
},
},
},
},
OpaqueDirs: []string{"etc/"},
WhiteoutFiles: []string{"etc/hostname"},
},
},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
LayerID: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
DecompressedLayerID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
LayerInfo: ftypes.LayerInfo{
SchemaVersion: 1,
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
PackageInfos: []ftypes.PackageInfo{
{
FilePath: "lib/apk/db/installed",
Packages: []ftypes.Package{
{
Name: "binary",
Version: "1.2.3",
Release: "1",
Epoch: 2,
Arch: "x86_64",
SrcName: "src",
SrcVersion: "1.2.3",
SrcRelease: "1",
SrcEpoch: 2,
},
{
Name: "vim-minimal",
Version: "7.4.160",
Release: "5.el7",
Epoch: 2,
Arch: "x86_64",
SrcName: "vim",
SrcVersion: "7.4.160",
SrcRelease: "5.el7",
SrcEpoch: 2,
},
},
},
},
Applications: []ftypes.Application{
{
Type: "composer",
FilePath: "php-app/composer.lock",
Libraries: []godeptypes.Library{
{
Name: "guzzlehttp/guzzle",
Version: "6.2.0",
},
{
Name: "guzzlehttp/promises",
Version: "v1.3.1",
},
},
},
},
OpaqueDirs: []string{"etc/"},
WhiteoutFiles: []string{"etc/hostname"},
},
},
},
want: &google_protobuf.Empty{},
},
{
name: "sad path",
args: args{
in: &rpcCache.PutLayerRequest{
LayerInfo: &rpcCache.LayerInfo{
SchemaVersion: 1,
},
},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
LayerIDAnything: true,
DecompressedLayerIDAnything: true,
LayerInfoAnything: true,
},
Returns: cache.ImageCachePutLayerReturns{
Err: xerrors.New("error"),
},
},
wantErr: "unable to store layer info in cache",
},
{
name: "sad path: empty layer info",
args: args{
in: &rpcCache.PutLayerRequest{},
},
putLayer: cache.ImageCachePutLayerExpectation{
Args: cache.ImageCachePutLayerArgs{
LayerIDAnything: true,
DecompressedLayerIDAnything: true,
LayerInfoAnything: true,
},
Returns: cache.ImageCachePutLayerReturns{
Err: xerrors.New("error"),
},
},
wantErr: "empty layer info",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCache := new(mockCache)
mockCache.ApplyPutLayerExpectation(tt.putLayer)
s := NewCacheServer(mockCache)
got, err := s.PutLayer(context.Background(), tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got)
})
}
}
func TestCacheServer_MissingLayers(t *testing.T) {
type args struct {
ctx context.Context
in *rpcCache.MissingLayersRequest
}
tests := []struct {
name string
args args
getLayerExpectations []cache.LocalImageCacheGetLayerExpectation
getImageExpectations []cache.LocalImageCacheGetImageExpectation
want *rpcCache.MissingLayersResponse
wantErr string
}{
{
name: "happy path",
args: args{
in: &rpcCache.MissingLayersRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
},
},
getLayerExpectations: []cache.LocalImageCacheGetLayerExpectation{
{
Args: cache.LocalImageCacheGetLayerArgs{
LayerID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{},
},
},
{
Args: cache.LocalImageCacheGetLayerArgs{
LayerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
SchemaVersion: 1,
},
},
},
},
getImageExpectations: []cache.LocalImageCacheGetImageExpectation{
{
Args: cache.LocalImageCacheGetImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
},
Returns: cache.LocalImageCacheGetImageReturns{
ImageInfo: ftypes.ImageInfo{
SchemaVersion: 1,
},
},
},
},
want: &rpcCache.MissingLayersResponse{
MissingImage: false,
MissingLayerIds: []string{"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02"},
},
},
{
name: "schema version doesn't match",
args: args{
in: &rpcCache.MissingLayersRequest{
ImageId: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
},
},
getLayerExpectations: []cache.LocalImageCacheGetLayerExpectation{
{
Args: cache.LocalImageCacheGetLayerArgs{
LayerID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
SchemaVersion: 0,
},
},
},
{
Args: cache.LocalImageCacheGetLayerArgs{
LayerID: "sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
Returns: cache.LocalImageCacheGetLayerReturns{
LayerInfo: ftypes.LayerInfo{
SchemaVersion: -1,
},
},
},
},
getImageExpectations: []cache.LocalImageCacheGetImageExpectation{
{
Args: cache.LocalImageCacheGetImageArgs{
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
},
Returns: cache.LocalImageCacheGetImageReturns{
ImageInfo: ftypes.ImageInfo{},
},
},
},
want: &rpcCache.MissingLayersResponse{
MissingImage: true,
MissingLayerIds: []string{
"sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02",
"sha256:dffd9992ca398466a663c87c92cfea2a2db0ae0cf33fcb99da60eec52addbfc5",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockCache := new(mockCache)
mockCache.ApplyGetLayerExpectations(tt.getLayerExpectations)
mockCache.ApplyGetImageExpectations(tt.getImageExpectations)
s := NewCacheServer(mockCache)
got, err := s.MissingLayers(tt.args.ctx, tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got)
mockCache.MockLocalImageCache.AssertExpectations(t)
})
}
}

View File

@@ -6,34 +6,49 @@
package server
import (
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
db2 "github.com/aquasecurity/trivy/pkg/db"
library2 "github.com/aquasecurity/trivy/pkg/detector/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/aquasecurity/trivy/pkg/rpc/server/library"
"github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
library2 "github.com/aquasecurity/trivy/pkg/rpc/server/library"
ospkg2 "github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"k8s.io/utils/clock"
)
// Injectors from inject.go:
func initializeOspkgServer() *ospkg.Server {
detector := ospkg2.Detector{}
func initializeScanServer(localLayerCache cache.LocalImageCache) *ScanServer {
applier := analyzer.NewApplier(localLayerCache)
detector := ospkg.Detector{}
driverFactory := library.DriverFactory{}
libraryDetector := library.NewDetector(driverFactory)
scanner := local.NewScanner(applier, detector, libraryDetector)
config := db.Config{}
client := vulnerability.NewClient(config)
server := ospkg.NewServer(detector, client)
scanServer := NewScanServer(scanner, client)
return scanServer
}
func initializeOspkgServer() *ospkg2.Server {
detector := ospkg.Detector{}
config := db.Config{}
client := vulnerability.NewClient(config)
server := ospkg2.NewServer(detector, client)
return server
}
func initializeLibServer() *library.Server {
driverFactory := library2.DriverFactory{}
detector := library2.NewDetector(driverFactory)
func initializeLibServer() *library2.Server {
driverFactory := library.DriverFactory{}
detector := library.NewDetector(driverFactory)
config := db.Config{}
client := vulnerability.NewClient(config)
server := library.NewServer(detector, client)
server := library2.NewServer(detector, client)
return server
}

View File

@@ -1,45 +0,0 @@
package library
import (
"time"
detector "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/fanal/analyzer"
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
)
type Scanner struct {
detector detector.Operation
}
func NewScanner(detector detector.Operation) Scanner {
return Scanner{detector: detector}
}
func (s Scanner) Scan(imageName string, created time.Time, files extractor.FileMap) (map[string][]types.DetectedVulnerability, error) {
results, err := analyzer.GetLibraries(files)
if err != nil {
return nil, xerrors.Errorf("failed to analyze libraries: %w", err)
}
vulnerabilities := map[string][]types.DetectedVulnerability{}
for path, libs := range results {
vulns, err := s.detector.Detect(imageName, string(path), created, libs)
if err != nil {
return nil, xerrors.Errorf("failed library scan: %w", err)
}
vulnerabilities[string(path)] = vulns
}
return vulnerabilities, nil
}

View File

@@ -1,214 +0,0 @@
package library
import (
"testing"
"time"
library2 "github.com/aquasecurity/trivy/pkg/detector/library"
"golang.org/x/xerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/fanal/extractor"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestScanner_Scan(t *testing.T) {
type detectInput struct {
imageName string
filePath string
created time.Time
libs []ptypes.Library
}
type detectOutput struct {
vulns []types.DetectedVulnerability
err error
}
type detect struct {
input detectInput
output detectOutput
}
type args struct {
imageName string
created time.Time
files extractor.FileMap
}
tests := []struct {
name string
args args
detect []detect
want map[string][]types.DetectedVulnerability
wantErr string
}{
{
name: "happy",
args: args{
imageName: "alpine:3.10",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
files: extractor.FileMap{
"app/Pipfile.lock": []byte(`{
"_meta": {
"hash": {
"sha256": "ad1805ab0e16cf08032c3fe45eeaa29b79e9c196650411977af14e31b12ff0cd"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"django": {
"hashes": [
"sha256:665457d4146bbd34ae9d2970fa3b37082d7b225b0671bfd24c337458f229db78",
"sha256:bde46d4dbc410678e89bc95ea5d312dd6eb4c37d0fa0e19c9415cad94addf22f"
],
"index": "pypi",
"version": "==3.0.0"
}
}
}
`),
"app/package-lock.json": []byte(`{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
}`),
},
},
detect: []detect{
{
input: detectInput{
imageName: "alpine:3.10",
filePath: "app/Pipfile.lock",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
libs: []ptypes.Library{
{Name: "django", Version: "3.0.0"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
},
},
},
{
input: detectInput{
imageName: "alpine:3.10",
filePath: "app/package-lock.json",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
libs: []ptypes.Library{
{Name: "react", Version: "16.8.6"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0002"},
{VulnerabilityID: "CVE-2019-0003"},
},
},
},
},
want: map[string][]types.DetectedVulnerability{
"app/Pipfile.lock": {{VulnerabilityID: "CVE-2019-0001"}},
"app/package-lock.json": {
{VulnerabilityID: "CVE-2019-0002"},
{VulnerabilityID: "CVE-2019-0003"},
},
},
},
{
name: "broken lock file",
args: args{
imageName: "alpine:3.10",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
files: extractor.FileMap{
"app/Pipfile.lock": []byte(`{broken}`),
},
},
wantErr: "failed to analyze libraries",
},
{
name: "Detect returns an error",
args: args{
files: extractor.FileMap{
"app/package-lock.json": []byte(`{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"react": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.6"
}
}
}
}`),
},
},
detect: []detect{
{
input: detectInput{
filePath: "app/package-lock.json",
libs: []ptypes.Library{
{Name: "react", Version: "16.8.6"},
},
},
output: detectOutput{err: xerrors.New("error")},
},
},
wantErr: "failed library scan",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(library2.MockDetector)
for _, d := range tt.detect {
mockDetector.On("Detect", d.input.imageName, d.input.filePath, d.input.created, d.input.libs).Return(
d.output.vulns, d.output.err)
}
s := Scanner{
detector: mockDetector,
}
got, err := s.Scan(tt.args.imageName, tt.args.created, tt.args.files)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got, tt.name)
mockDetector.AssertExpectations(t)
})
}
}

View File

@@ -0,0 +1,71 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package local
import digest "github.com/opencontainers/go-digest"
import mock "github.com/stretchr/testify/mock"
import types "github.com/aquasecurity/fanal/types"
// MockApplier is an autogenerated mock type for the Applier type
type MockApplier struct {
mock.Mock
}
type ApplierApplyLayersArgs struct {
ImageID digest.Digest
ImageIDAnything bool
LayerIDs []string
LayerIDsAnything bool
}
type ApplierApplyLayersReturns struct {
Detail types.ImageDetail
Err error
}
type ApplierApplyLayersExpectation struct {
Args ApplierApplyLayersArgs
Returns ApplierApplyLayersReturns
}
func (_m *MockApplier) ApplyApplyLayersExpectation(e ApplierApplyLayersExpectation) {
var args []interface{}
if e.Args.ImageIDAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ImageID)
}
if e.Args.LayerIDsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.LayerIDs)
}
_m.On("ApplyLayers", args...).Return(e.Returns.Detail, e.Returns.Err)
}
func (_m *MockApplier) ApplyApplyLayersExpectations(expectations []ApplierApplyLayersExpectation) {
for _, e := range expectations {
_m.ApplyApplyLayersExpectation(e)
}
}
// ApplyLayers provides a mock function with given fields: imageID, layerIDs
func (_m *MockApplier) ApplyLayers(imageID digest.Digest, layerIDs []string) (types.ImageDetail, error) {
ret := _m.Called(imageID, layerIDs)
var r0 types.ImageDetail
if rf, ok := ret.Get(0).(func(digest.Digest, []string) types.ImageDetail); ok {
r0 = rf(imageID, layerIDs)
} else {
r0 = ret.Get(0).(types.ImageDetail)
}
var r1 error
if rf, ok := ret.Get(1).(func(digest.Digest, []string) error); ok {
r1 = rf(imageID, layerIDs)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@@ -0,0 +1,88 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package local
import mock "github.com/stretchr/testify/mock"
import pkgtypes "github.com/aquasecurity/trivy/pkg/types"
import time "time"
import types "github.com/aquasecurity/go-dep-parser/pkg/types"
// MockLibraryDetector is an autogenerated mock type for the LibraryDetector type
type MockLibraryDetector struct {
mock.Mock
}
type LibraryDetectorDetectArgs struct {
ImageName string
ImageNameAnything bool
FilePath string
FilePathAnything bool
Created time.Time
CreatedAnything bool
Pkgs []types.Library
PkgsAnything bool
}
type LibraryDetectorDetectReturns struct {
DetectedVulns []pkgtypes.DetectedVulnerability
Err error
}
type LibraryDetectorDetectExpectation struct {
Args LibraryDetectorDetectArgs
Returns LibraryDetectorDetectReturns
}
func (_m *MockLibraryDetector) ApplyDetectExpectation(e LibraryDetectorDetectExpectation) {
var args []interface{}
if e.Args.ImageNameAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ImageName)
}
if e.Args.FilePathAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.FilePath)
}
if e.Args.CreatedAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Created)
}
if e.Args.PkgsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Pkgs)
}
_m.On("Detect", args...).Return(e.Returns.DetectedVulns, e.Returns.Err)
}
func (_m *MockLibraryDetector) ApplyDetectExpectations(expectations []LibraryDetectorDetectExpectation) {
for _, e := range expectations {
_m.ApplyDetectExpectation(e)
}
}
// Detect provides a mock function with given fields: imageName, filePath, created, pkgs
func (_m *MockLibraryDetector) Detect(imageName string, filePath string, created time.Time, pkgs []types.Library) ([]pkgtypes.DetectedVulnerability, error) {
ret := _m.Called(imageName, filePath, created, pkgs)
var r0 []pkgtypes.DetectedVulnerability
if rf, ok := ret.Get(0).(func(string, string, time.Time, []types.Library) []pkgtypes.DetectedVulnerability); ok {
r0 = rf(imageName, filePath, created, pkgs)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]pkgtypes.DetectedVulnerability)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, time.Time, []types.Library) error); ok {
r1 = rf(imageName, filePath, created, pkgs)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@@ -0,0 +1,103 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package local
import mock "github.com/stretchr/testify/mock"
import pkgtypes "github.com/aquasecurity/trivy/pkg/types"
import time "time"
import types "github.com/aquasecurity/fanal/types"
// MockOspkgDetector is an autogenerated mock type for the OspkgDetector type
type MockOspkgDetector struct {
mock.Mock
}
type OspkgDetectorDetectArgs struct {
ImageName string
ImageNameAnything bool
OsFamily string
OsFamilyAnything bool
OsName string
OsNameAnything bool
Created time.Time
CreatedAnything bool
Pkgs []types.Package
PkgsAnything bool
}
type OspkgDetectorDetectReturns struct {
DetectedVulns []pkgtypes.DetectedVulnerability
Eosl bool
Err error
}
type OspkgDetectorDetectExpectation struct {
Args OspkgDetectorDetectArgs
Returns OspkgDetectorDetectReturns
}
func (_m *MockOspkgDetector) ApplyDetectExpectation(e OspkgDetectorDetectExpectation) {
var args []interface{}
if e.Args.ImageNameAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ImageName)
}
if e.Args.OsFamilyAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.OsFamily)
}
if e.Args.OsNameAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.OsName)
}
if e.Args.CreatedAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Created)
}
if e.Args.PkgsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Pkgs)
}
_m.On("Detect", args...).Return(e.Returns.DetectedVulns, e.Returns.Eosl, e.Returns.Err)
}
func (_m *MockOspkgDetector) ApplyDetectExpectations(expectations []OspkgDetectorDetectExpectation) {
for _, e := range expectations {
_m.ApplyDetectExpectation(e)
}
}
// Detect provides a mock function with given fields: imageName, osFamily, osName, created, pkgs
func (_m *MockOspkgDetector) Detect(imageName string, osFamily string, osName string, created time.Time, pkgs []types.Package) ([]pkgtypes.DetectedVulnerability, bool, error) {
ret := _m.Called(imageName, osFamily, osName, created, pkgs)
var r0 []pkgtypes.DetectedVulnerability
if rf, ok := ret.Get(0).(func(string, string, string, time.Time, []types.Package) []pkgtypes.DetectedVulnerability); ok {
r0 = rf(imageName, osFamily, osName, created, pkgs)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]pkgtypes.DetectedVulnerability)
}
}
var r1 bool
if rf, ok := ret.Get(1).(func(string, string, string, time.Time, []types.Package) bool); ok {
r1 = rf(imageName, osFamily, osName, created, pkgs)
} else {
r1 = ret.Get(1).(bool)
}
var r2 error
if rf, ok := ret.Get(2).(func(string, string, string, time.Time, []types.Package) error); ok {
r2 = rf(imageName, osFamily, osName, created, pkgs)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}

161
pkg/scanner/local/scan.go Normal file
View File

@@ -0,0 +1,161 @@
package local
import (
"fmt"
"sort"
"time"
ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/fanal/analyzer"
"github.com/google/wire"
digest "github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
_ "github.com/aquasecurity/fanal/analyzer/command/apk"
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
_ "github.com/aquasecurity/fanal/analyzer/os/photon"
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd"
ftypes "github.com/aquasecurity/fanal/types"
libDetector "github.com/aquasecurity/trivy/pkg/detector/library"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/report"
)
var SuperSet = wire.NewSet(
analyzer.NewApplier,
wire.Bind(new(Applier), new(analyzer.Applier)),
ospkgDetector.SuperSet,
wire.Bind(new(OspkgDetector), new(ospkgDetector.Detector)),
libDetector.SuperSet,
wire.Bind(new(LibraryDetector), new(libDetector.Detector)),
NewScanner,
)
type Applier interface {
ApplyLayers(imageID digest.Digest, layerIDs []string) (detail ftypes.ImageDetail, err error)
}
type OspkgDetector interface {
Detect(imageName, osFamily, osName string, created time.Time, pkgs []ftypes.Package) (detectedVulns []types.DetectedVulnerability, eosl bool, err error)
}
type LibraryDetector interface {
Detect(imageName, filePath string, created time.Time, pkgs []ptypes.Library) (detectedVulns []types.DetectedVulnerability, err error)
}
type Scanner struct {
applier Applier
ospkgDetector OspkgDetector
libDetector LibraryDetector
}
func NewScanner(applier Applier, ospkgDetector OspkgDetector, libDetector LibraryDetector) Scanner {
return Scanner{applier: applier, ospkgDetector: ospkgDetector, libDetector: libDetector}
}
func (s Scanner) Scan(target string, imageID digest.Digest, layerIDs []string, options types.ScanOptions) (report.Results, *ftypes.OS, bool, error) {
imageDetail, err := s.applier.ApplyLayers(imageID, layerIDs)
if err != nil {
return nil, nil, false, xerrors.Errorf("failed to apply layers: %w", err)
}
var eosl bool
var results report.Results
if utils.StringInSlice("os", options.VulnType) {
pkgs := imageDetail.Packages
if options.ScanRemovedPackages {
pkgs = mergePkgs(pkgs, imageDetail.HistoryPackages)
}
var result *report.Result
result, eosl, err = s.scanOSPkg(target, imageDetail.OS.Family, imageDetail.OS.Name, pkgs)
if err != nil {
return nil, nil, false, xerrors.Errorf("failed to scan OS packages: %w", err)
}
if result != nil {
results = append(results, *result)
}
}
if utils.StringInSlice("library", options.VulnType) {
libResults, err := s.scanLibrary(imageDetail.Applications)
if err != nil {
return nil, nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
}
results = append(results, libResults...)
}
return results, imageDetail.OS, eosl, nil
}
func (s Scanner) scanOSPkg(target, osFamily, osName string, pkgs []ftypes.Package) (*report.Result, bool, error) {
if osFamily == "" {
return nil, false, nil
}
vulns, eosl, err := s.ospkgDetector.Detect("", osFamily, osName, time.Time{}, pkgs)
if err == ospkgDetector.ErrUnsupportedOS {
return nil, false, nil
} else if err != nil {
return nil, false, xerrors.Errorf("failed vulnerability detection of OS packages: %w", err)
}
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osName)
result := &report.Result{
Target: imageDetail,
Vulnerabilities: vulns,
}
return result, eosl, nil
}
func (s Scanner) scanLibrary(apps []ftypes.Application) (report.Results, error) {
var results report.Results
for _, app := range apps {
vulns, err := s.libDetector.Detect("", app.FilePath, time.Time{}, app.Libraries)
if err != nil {
return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err)
}
results = append(results, report.Result{
Target: app.FilePath,
Vulnerabilities: vulns,
})
}
sort.Slice(results, func(i, j int) bool {
return results[i].Target < results[j].Target
})
return results, nil
}
func mergePkgs(pkgs, pkgsFromCommands []ftypes.Package) []ftypes.Package {
// pkg has priority over pkgsFromCommands
uniqPkgs := map[string]struct{}{}
for _, pkg := range pkgs {
uniqPkgs[pkg.Name] = struct{}{}
}
for _, pkg := range pkgsFromCommands {
if _, ok := uniqPkgs[pkg.Name]; ok {
continue
}
pkgs = append(pkgs, pkg)
}
return pkgs
}

View File

@@ -0,0 +1,518 @@
package local
import (
"errors"
"testing"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
ftypes "github.com/aquasecurity/fanal/types"
dtypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestScanner_Scan(t *testing.T) {
type args struct {
target string
layerIDs []string
options types.ScanOptions
}
tests := []struct {
name string
args args
applyLayersExpectation ApplierApplyLayersExpectation
ospkgDetectExpectations []OspkgDetectorDetectExpectation
libDetectExpectations []LibraryDetectorDetectExpectation
wantResults report.Results
wantOS *ftypes.OS
wantEosl bool
wantErr string
}{
{
name: "happy path",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
Packages: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
Applications: []ftypes.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile.lock",
Libraries: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
},
},
},
},
ospkgDetectExpectations: []OspkgDetectorDetectExpectation{
{
Args: OspkgDetectorDetectArgs{
OsFamily: "alpine",
OsName: "3.11",
Pkgs: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
},
Returns: OspkgDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-9999",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
},
},
Eosl: false,
},
},
},
libDetectExpectations: []LibraryDetectorDetectExpectation{
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/Gemfile.lock",
Pkgs: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
Returns: LibraryDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
},
wantResults: report.Results{
{
Target: "alpine:latest (alpine 3.11)",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-9999",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
},
},
},
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
wantOS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
},
{
name: "happy path with empty os",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{},
Applications: []ftypes.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile.lock",
Libraries: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
},
},
},
},
libDetectExpectations: []LibraryDetectorDetectExpectation{
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/Gemfile.lock",
Pkgs: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
Returns: LibraryDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
},
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
wantOS: &ftypes.OS{},
},
{
name: "happy path with unknown os",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{
Family: "fedora",
Name: "27",
},
Applications: []ftypes.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile.lock",
Libraries: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
},
},
},
},
ospkgDetectExpectations: []OspkgDetectorDetectExpectation{
{
Args: OspkgDetectorDetectArgs{
OsFamily: "fedora",
OsName: "27",
},
Returns: OspkgDetectorDetectReturns{
Err: ospkgDetector.ErrUnsupportedOS,
},
},
},
libDetectExpectations: []LibraryDetectorDetectExpectation{
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/Gemfile.lock",
Pkgs: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
Returns: LibraryDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
},
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-10000",
PkgName: "rails",
InstalledVersion: "6.0",
FixedVersion: "6.1",
},
},
},
},
wantOS: &ftypes.OS{
Family: "fedora",
Name: "27",
},
},
{
name: "happy path with only library detection",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
Packages: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
Applications: []ftypes.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile.lock",
Libraries: []dtypes.Library{
{Name: "rails", Version: "5.1"},
},
},
{
Type: "composer",
FilePath: "/app/composer-lock.json",
Libraries: []dtypes.Library{
{Name: "laravel", Version: "6.0.0"},
},
},
},
},
},
},
libDetectExpectations: []LibraryDetectorDetectExpectation{
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/Gemfile.lock",
Pkgs: []dtypes.Library{
{Name: "rails", Version: "5.1"},
},
},
Returns: LibraryDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-11111",
PkgName: "rails",
InstalledVersion: "5.1",
FixedVersion: "5.2",
},
},
},
},
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/composer-lock.json",
Pkgs: []dtypes.Library{
{Name: "laravel", Version: "6.0.0"},
},
},
Returns: LibraryDetectorDetectReturns{
DetectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-22222",
PkgName: "laravel",
InstalledVersion: "6.0.0",
FixedVersion: "6.1.0",
},
},
},
},
},
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-11111",
PkgName: "rails",
InstalledVersion: "5.1",
FixedVersion: "5.2",
},
},
},
{
Target: "/app/composer-lock.json",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-22222",
PkgName: "laravel",
InstalledVersion: "6.0.0",
FixedVersion: "6.1.0",
},
},
},
},
wantOS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
},
{
name: "sad path: ApplyLayers returns an error",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Err: errors.New("error"),
},
},
wantErr: "failed to apply layers",
},
{
name: "sad path: ospkgDetector.Detect returns an error",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
Packages: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
},
},
},
ospkgDetectExpectations: []OspkgDetectorDetectExpectation{
{
Args: OspkgDetectorDetectArgs{
OsFamily: "alpine",
OsName: "3.11",
Pkgs: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
},
Returns: OspkgDetectorDetectReturns{
Err: errors.New("error"),
},
},
},
wantErr: "failed to scan OS packages",
},
{
name: "sad path: libDetector.Detect returns an error",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
Returns: ApplierApplyLayersReturns{
Detail: ftypes.ImageDetail{
OS: &ftypes.OS{
Family: "alpine",
Name: "3.11",
},
Packages: []ftypes.Package{
{Name: "musl", Version: "1.2.3"},
},
Applications: []ftypes.Application{
{
Type: "bundler",
FilePath: "/app/Gemfile.lock",
Libraries: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
},
},
},
},
libDetectExpectations: []LibraryDetectorDetectExpectation{
{
Args: LibraryDetectorDetectArgs{
FilePath: "/app/Gemfile.lock",
Pkgs: []dtypes.Library{
{Name: "rails", Version: "6.0"},
},
},
Returns: LibraryDetectorDetectReturns{
Err: errors.New("error"),
},
},
},
wantErr: "failed to scan application libraries",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
applier := new(MockApplier)
applier.ApplyApplyLayersExpectation(tt.applyLayersExpectation)
ospkgDetector := new(MockOspkgDetector)
ospkgDetector.ApplyDetectExpectations(tt.ospkgDetectExpectations)
libDetector := new(MockLibraryDetector)
libDetector.ApplyDetectExpectations(tt.libDetectExpectations)
s := NewScanner(applier, ospkgDetector, libDetector)
gotResults, gotOS, gotEosl, err := s.Scan(tt.args.target, "", tt.args.layerIDs, tt.args.options)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
require.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
require.NoError(t, err, tt.name)
}
assert.Equal(t, tt.wantResults, gotResults)
assert.Equal(t, tt.wantOS, gotOS)
assert.Equal(t, tt.wantEosl, gotEosl)
applier.AssertExpectations(t)
ospkgDetector.AssertExpectations(t)
libDetector.AssertExpectations(t)
})
}
}

View File

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

107
pkg/scanner/mock_driver.go Normal file
View File

@@ -0,0 +1,107 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package scanner
import digest "github.com/opencontainers/go-digest"
import fanaltypes "github.com/aquasecurity/fanal/types"
import mock "github.com/stretchr/testify/mock"
import report "github.com/aquasecurity/trivy/pkg/report"
import types "github.com/aquasecurity/trivy/pkg/types"
// MockDriver is an autogenerated mock type for the Driver type
type MockDriver struct {
mock.Mock
}
type ScanArgs struct {
Target string
TargetAnything bool
ImageID digest.Digest
ImageIDAnything bool
LayerIDs []string
LayerIDsAnything bool
Options types.ScanOptions
OptionsAnything bool
}
type ScanReturns struct {
Results report.Results
OsFound *fanaltypes.OS
Eols bool
Err error
}
type ScanExpectation struct {
Args ScanArgs
Returns ScanReturns
}
func (_m *MockDriver) ApplyScanExpectation(e ScanExpectation) {
var args []interface{}
if e.Args.TargetAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Target)
}
if e.Args.ImageIDAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ImageID)
}
if e.Args.LayerIDsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.LayerIDs)
}
if e.Args.OptionsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Options)
}
_m.On("Scan", args...).Return(e.Returns.Results, e.Returns.OsFound, e.Returns.Eols, e.Returns.Err)
}
func (_m *MockDriver) ApplyScanExpectations(expectations []ScanExpectation) {
for _, e := range expectations {
_m.ApplyScanExpectation(e)
}
}
// Scan provides a mock function with given fields: target, imageID, layerIDs, options
func (_m *MockDriver) Scan(target string, imageID digest.Digest, layerIDs []string, options types.ScanOptions) (report.Results, *fanaltypes.OS, bool, error) {
ret := _m.Called(target, imageID, layerIDs, options)
var r0 report.Results
if rf, ok := ret.Get(0).(func(string, digest.Digest, []string, types.ScanOptions) report.Results); ok {
r0 = rf(target, imageID, layerIDs, options)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(report.Results)
}
}
var r1 *fanaltypes.OS
if rf, ok := ret.Get(1).(func(string, digest.Digest, []string, types.ScanOptions) *fanaltypes.OS); ok {
r1 = rf(target, imageID, layerIDs, options)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*fanaltypes.OS)
}
}
var r2 bool
if rf, ok := ret.Get(2).(func(string, digest.Digest, []string, types.ScanOptions) bool); ok {
r2 = rf(target, imageID, layerIDs, options)
} else {
r2 = ret.Get(2).(bool)
}
var r3 error
if rf, ok := ret.Get(3).(func(string, digest.Digest, []string, types.ScanOptions) error); ok {
r3 = rf(target, imageID, layerIDs, options)
} else {
r3 = ret.Error(3)
}
return r0, r1, r2, r3
}

View File

@@ -1,83 +0,0 @@
package ospkg
import (
"time"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
_ "github.com/aquasecurity/fanal/analyzer/command/apk"
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
_ "github.com/aquasecurity/fanal/analyzer/os/photon"
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
"github.com/aquasecurity/fanal/extractor"
ftypes "github.com/aquasecurity/fanal/types"
detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
type Scanner struct {
detector detector.Operation
}
func NewScanner(detector detector.Operation) Scanner {
return Scanner{detector: detector}
}
func (s Scanner) Scan(imageName string, created time.Time, files extractor.FileMap) (string, string, []types.DetectedVulnerability, error) {
os, err := analyzer.GetOS(files)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to analyze OS: %w", err)
}
log.Logger.Debugf("OS family: %s, OS version: %s", os.Family, os.Name)
pkgs, err := analyzer.GetPackages(files)
if err != nil {
if xerrors.Is(err, ftypes.ErrNoRpmCmd) {
log.Logger.Error("'rpm' command is not installed")
}
return "", "", nil, xerrors.Errorf("failed to analyze OS packages: %w", err)
}
log.Logger.Debugf("the number of packages: %d", len(pkgs))
pkgsFromCommands, err := analyzer.GetPackagesFromCommands(os, files)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to analyze OS packages: %w", err)
}
log.Logger.Debugf("the number of packages from commands: %d", len(pkgsFromCommands))
pkgs = mergePkgs(pkgs, pkgsFromCommands)
log.Logger.Debugf("the number of packages: %d", len(pkgs))
vulns, eosl, err := s.detector.Detect(imageName, os.Family, os.Name, created, pkgs)
if err != nil {
return "", "", nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
if eosl {
// TODO: test logger
log.Logger.Warnf("This OS version is no longer supported by the distribution: %s %s", os.Family, os.Name)
log.Logger.Warnf("The vulnerability detection may be insufficient because security updates are not provided")
}
return os.Family, os.Name, vulns, nil
}
func mergePkgs(pkgs, pkgsFromCommands []analyzer.Package) []analyzer.Package {
uniqPkgs := map[string]struct{}{}
for _, pkg := range pkgs {
uniqPkgs[pkg.Name] = struct{}{}
}
for _, pkg := range pkgsFromCommands {
if _, ok := uniqPkgs[pkg.Name]; ok {
continue
}
pkgs = append(pkgs, pkg)
}
return pkgs
}

View File

@@ -1,7 +0,0 @@
// +build freebsd netbsd openbsd
package ospkg
import (
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd"
)

View File

@@ -1,228 +0,0 @@
package ospkg
import (
"os"
"testing"
"time"
"golang.org/x/xerrors"
ospkg2 "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestMain(m *testing.M) {
log.InitLogger(false, true)
code := m.Run()
os.Exit(code)
}
func TestScanner_Scan(t *testing.T) {
type detectInput struct {
imageName string
osFamily string
osName string
buildTime time.Time
pkgs []analyzer.Package
}
type detectOutput struct {
vulns []types.DetectedVulnerability
eosl bool
err error
}
type detect struct {
input detectInput
output detectOutput
}
type fields struct {
imageName string
created time.Time
files extractor.FileMap
}
type want struct {
osFamily string
osName string
vulns []types.DetectedVulnerability
err string
}
tests := []struct {
name string
fields fields
detect detect
want want
}{
{
name: "happy path",
fields: fields{
imageName: "alpine:3.10.2",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
files: extractor.FileMap{
"/config": []byte(`{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"ArgsEscaped":true,"Image":"sha256:09f2bbe58e774849d74dc1391c2e01731896c745c4aba1ecf69a283bdb4b537a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"c10d36fa368a7ea673683682666758adf35efe98e10989505f4f566b5b18538f","container_config":{"Hostname":"c10d36fa368a","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/bin/sh\"]"],"ArgsEscaped":true,"Image":"sha256:09f2bbe58e774849d74dc1391c2e01731896c745c4aba1ecf69a283bdb4b537a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-05-11T00:07:03.510395965Z","docker_version":"18.06.1-ce","history":[{"created":"2019-05-11T00:07:03.358250803Z","created_by":"/bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6ae03397b99ea77f2e9ee901c5c59e59f76f93adbb4035913 in / "},{"created":"2019-05-11T00:07:03.510395965Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:f1b5933fe4b5f49bbe8258745cf396afe07e625bdab3168e364daf7c956b6b81"]}}`),
"etc/alpine-release": []byte("3.10.2"),
"lib/apk/db/installed": []byte(`C:Q11Ing8/u1VIdY9czSxaDO9wJg72I=
P:musl
V:1.1.22-r3
A:x86_64
S:368204
I:598016
T:the musl c library (libc) implementation
U:http://www.musl-libc.org/
L:MIT
o:musl
m:Timo Teräs <timo.teras@iki.fi>
t:1565162130
c:0c777cf840e82cdc528651e3f3f8f9dda6b1b028
p:so:libc.musl-x86_64.so.1=1
F:lib
R:libc.musl-x86_64.so.1
a:0:0:777
Z:Q17yJ3JFNypA4mxhJJr0ou6CzsJVI=
R:ld-musl-x86_64.so.1
a:0:0:755
Z:Q1TTLtUopPeiF9JrA0cgKQZYggG+c=
F:usr
F:usr/lib
`),
},
},
detect: detect{
input: detectInput{
imageName: "alpine:3.10.2",
osFamily: "alpine",
osName: "3.10.2",
buildTime: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
output: detectOutput{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001", PkgName: "musl"},
},
err: nil,
},
},
want: want{
osFamily: "alpine",
osName: "3.10.2",
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001", PkgName: "musl"},
},
},
},
{
name: "sad path",
fields: fields{
files: extractor.FileMap{
"etc/alpine-release": []byte("3.10.2"),
"invalid": []byte(`invalid`),
},
},
want: want{err: analyzer.ErrPkgAnalysis.Error()},
},
{
name: "Detect returns an error",
fields: fields{
imageName: "alpine:3.10",
created: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
files: extractor.FileMap{
"/config": []byte(`{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"ArgsEscaped":true,"Image":"sha256:09f2bbe58e774849d74dc1391c2e01731896c745c4aba1ecf69a283bdb4b537a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"c10d36fa368a7ea673683682666758adf35efe98e10989505f4f566b5b18538f","container_config":{"Hostname":"c10d36fa368a","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/bin/sh\"]"],"ArgsEscaped":true,"Image":"sha256:09f2bbe58e774849d74dc1391c2e01731896c745c4aba1ecf69a283bdb4b537a","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2019-05-11T00:07:03.510395965Z","docker_version":"18.06.1-ce","history":[{"created":"2019-05-11T00:07:03.358250803Z","created_by":"/bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6ae03397b99ea77f2e9ee901c5c59e59f76f93adbb4035913 in / "},{"created":"2019-05-11T00:07:03.510395965Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:f1b5933fe4b5f49bbe8258745cf396afe07e625bdab3168e364daf7c956b6b81"]}}`),
"etc/alpine-release": []byte("3.10.2"),
"lib/apk/db/installed": []byte(`C:Q11Ing8/u1VIdY9czSxaDO9wJg72I=
P:musl
V:1.1.22-r3
A:x86_64
`),
},
},
detect: detect{
input: detectInput{
imageName: "alpine:3.10",
osFamily: "alpine",
osName: "3.10.2",
buildTime: time.Date(2019, 5, 11, 0, 7, 3, 510395965, time.UTC),
pkgs: []analyzer.Package{
{Name: "musl", Version: "1.1.22-r3"},
},
},
output: detectOutput{
err: xerrors.New("error"),
},
},
want: want{
err: "failed to detect vulnerabilities",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDetector := new(ospkg2.MockDetector)
mockDetector.On("Detect", tt.detect.input.imageName, tt.detect.input.osFamily, tt.detect.input.osName,
tt.detect.input.buildTime, tt.detect.input.pkgs).Return(tt.detect.output.vulns, tt.detect.output.eosl, tt.detect.output.err)
s := NewScanner(mockDetector)
got, got1, got2, err := s.Scan(tt.fields.imageName, tt.fields.created, tt.fields.files)
if tt.want.err != "" {
require.NotNil(t, err, tt.name)
assert.Contains(t, err.Error(), tt.want.err, tt.name)
return
} else {
assert.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want.osFamily, got)
assert.Equal(t, tt.want.osName, got1)
assert.Equal(t, tt.want.vulns, got2)
mockDetector.AssertExpectations(t)
})
}
}
func Test_mergePkgs(t *testing.T) {
type args struct {
pkgs []analyzer.Package
pkgsFromCommands []analyzer.Package
}
tests := []struct {
name string
args args
want []analyzer.Package
}{
{
name: "happy path",
args: args{
pkgs: []analyzer.Package{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "3.4.5"},
{Name: "baz", Version: "6.7.8"},
},
pkgsFromCommands: []analyzer.Package{
{Name: "bar", Version: "1.1.1"},
{Name: "hoge", Version: "9.0.1"},
},
},
want: []analyzer.Package{
{Name: "foo", Version: "1.2.3"},
{Name: "bar", Version: "3.4.5"},
{Name: "baz", Version: "6.7.8"},
{Name: "hoge", Version: "9.0.1"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := mergePkgs(tt.args.pkgs, tt.args.pkgsFromCommands)
assert.Equal(t, tt.want, got, tt.name)
})
}
}

View File

@@ -1,9 +0,0 @@
// +build linux darwin
package ospkg
import (
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd"
// TODO: Eliminate the dependency on "rpm" command
// _ "github.com/aquasecurity/fanal/analyzer/pkg/rpm"
)

View File

@@ -2,157 +2,103 @@ package scanner
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"sort"
"time"
"github.com/google/wire"
"golang.org/x/crypto/ssh/terminal"
digest "github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/extractor"
"github.com/aquasecurity/fanal/extractor/docker"
ftypes "github.com/aquasecurity/fanal/types"
libDetector "github.com/aquasecurity/trivy/pkg/detector/library"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
rpcLibDetector "github.com/aquasecurity/trivy/pkg/rpc/client/library"
rpcOSDetector "github.com/aquasecurity/trivy/pkg/rpc/client/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner/library"
libScanner "github.com/aquasecurity/trivy/pkg/scanner/library"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg"
ospkgScanner "github.com/aquasecurity/trivy/pkg/scanner/ospkg"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
)
var StandaloneSet = wire.NewSet(
ospkgDetector.SuperSet,
ospkgScanner.NewScanner,
libDetector.SuperSet,
libScanner.NewScanner,
// StandaloneSuperSet is used in the standalone mode
var StandaloneSuperSet = wire.NewSet(
analyzer.New,
wire.Bind(new(Analyzer), new(analyzer.Config)),
local.SuperSet,
wire.Bind(new(Driver), new(local.Scanner)),
NewScanner,
)
var ClientSet = wire.NewSet(
rpcOSDetector.SuperSet,
ospkgScanner.NewScanner,
rpcLibDetector.SuperSet,
libScanner.NewScanner,
var StandaloneDockerSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
StandaloneSuperSet,
)
var StandaloneArchiveSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerArchiveExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
StandaloneSuperSet,
)
// RemoteSuperSet is used in the client mode
var RemoteSuperSet = wire.NewSet(
analyzer.New,
wire.Bind(new(Analyzer), new(analyzer.Config)),
client.SuperSet,
wire.Bind(new(Driver), new(client.Scanner)),
NewScanner,
)
var RemoteDockerSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
RemoteSuperSet,
)
var RemoteArchiveSet = wire.NewSet(
types.GetDockerOption,
docker.NewDockerArchiveExtractor,
wire.Bind(new(extractor.Extractor), new(docker.Extractor)),
RemoteSuperSet,
)
type Scanner struct {
cacheClient cache.Cache
ospkgScanner ospkg.Scanner
libScanner library.Scanner
driver Driver
analyzer Analyzer
}
func NewScanner(cacheClient cache.Cache, ospkgScanner ospkg.Scanner, libScanner library.Scanner) Scanner {
return Scanner{cacheClient: cacheClient, ospkgScanner: ospkgScanner, libScanner: libScanner}
type Driver interface {
Scan(target string, imageID digest.Digest, layerIDs []string, options types.ScanOptions) (results report.Results, osFound *ftypes.OS, eols bool, err error)
}
func (s Scanner) ScanImage(imageName, filePath string, scanOptions types.ScanOptions, dockerOption ftypes.DockerOption) (report.Results, error) {
results := report.Results{}
type Analyzer interface {
Analyze(ctx context.Context) (info ftypes.ImageReference, err error)
}
func NewScanner(driver Driver, ac Analyzer) Scanner {
return Scanner{driver: driver, analyzer: ac}
}
func (s Scanner) ScanImage(options types.ScanOptions) (report.Results, error) {
ctx := context.Background()
var target string
var files extractor.FileMap
var ac analyzer.Config
ext, err := docker.NewDockerExtractor(dockerOption, s.cacheClient)
imageInfo, err := s.analyzer.Analyze(ctx)
if err != nil {
return nil, err
}
ac = analyzer.Config{Extractor: ext}
if imageName != "" {
target = imageName
files, err = ac.Analyze(ctx, imageName, dockerOption)
if err != nil {
return nil, xerrors.Errorf("failed to analyze image: %w", err)
}
} else if filePath != "" {
target = filePath
rc, err := openStream(filePath)
if err != nil {
return nil, xerrors.Errorf("failed to open stream: %w", err)
}
files, err = ac.AnalyzeFile(ctx, rc)
if err != nil {
return nil, err
}
} else {
return nil, xerrors.New("image name or image file must be specified")
return nil, xerrors.Errorf("failed analysis: %w", err)
}
created, err := getCreated(files["/config"])
log.Logger.Debugf("Image ID: %s", imageInfo.ID)
log.Logger.Debugf("Layer IDs: %v", imageInfo.LayerIDs)
results, osFound, eosl, err := s.driver.Scan(imageInfo.Name, imageInfo.ID, imageInfo.LayerIDs, options)
if err != nil {
return nil, err
return nil, xerrors.Errorf("scan failed: %w", err)
}
if utils.StringInSlice("os", scanOptions.VulnType) {
osFamily, osVersion, osVulns, err := s.ospkgScanner.Scan(target, created, files)
if err != nil && err != ospkgDetector.ErrUnsupportedOS {
return nil, xerrors.Errorf("failed to scan the image: %w", err)
}
if osFamily != "" {
imageDetail := fmt.Sprintf("%s (%s %s)", target, osFamily, osVersion)
results = append(results, report.Result{
Target: imageDetail,
Vulnerabilities: osVulns,
})
}
}
if utils.StringInSlice("library", scanOptions.VulnType) {
libVulns, err := s.libScanner.Scan(target, created, files)
if err != nil {
return nil, xerrors.Errorf("failed to scan libraries: %w", err)
}
var libResults report.Results
for path, vulns := range libVulns {
libResults = append(libResults, report.Result{
Target: path,
Vulnerabilities: vulns,
})
}
sort.Slice(libResults, func(i, j int) bool {
return libResults[i].Target < libResults[j].Target
})
results = append(results, libResults...)
if eosl {
log.Logger.Warnf("This OS version is no longer supported by the distribution: %s %s", osFound.Family, osFound.Name)
log.Logger.Warnf("The vulnerability detection may be insufficient because security updates are not provided")
}
return results, nil
}
type config struct {
Created time.Time
}
func getCreated(configBlob []byte) (time.Time, error) {
var config config
if err := json.Unmarshal(configBlob, &config); err != nil {
return time.Time{}, xerrors.Errorf("invalid config JSON: %w", err)
}
return config.Created, nil
}
func openStream(path string) (*os.File, error) {
if path == "-" {
if terminal.IsTerminal(0) {
flag.Usage()
os.Exit(64)
} else {
return os.Stdin, nil
}
}
return os.Open(path)
}

161
pkg/scanner/scan_test.go Normal file
View File

@@ -0,0 +1,161 @@
package scanner
import (
"errors"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestMain(m *testing.M) {
log.InitLogger(false, false)
code := m.Run()
os.Exit(code)
}
func TestScanner_ScanImage(t *testing.T) {
type args struct {
options types.ScanOptions
}
tests := []struct {
name string
args args
analyzeExpectation AnalyzerAnalyzeExpectation
scanExpectation ScanExpectation
want report.Results
wantErr string
}{
{
name: "happy path",
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Info: ftypes.ImageReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
},
},
scanExpectation: ScanExpectation{
Args: ScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: types.ScanOptions{VulnType: []string{"os"}},
},
Returns: ScanReturns{
Results: report.Results{
{
Target: "alpine:3.11",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-9999",
PkgName: "vim",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
},
},
},
},
OsFound: &ftypes.OS{
Family: "alpine",
Name: "3.10",
},
Eols: true,
},
},
want: report.Results{
{
Target: "alpine:3.11",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-9999",
PkgName: "vim",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
},
},
},
},
},
{
name: "sad path: AnalyzerAnalyze returns an error",
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Err: errors.New("error"),
},
},
wantErr: "failed analysis",
},
{
name: "sad path: Scan returns an error",
args: args{
options: types.ScanOptions{VulnType: []string{"os"}},
},
analyzeExpectation: AnalyzerAnalyzeExpectation{
Args: AnalyzerAnalyzeArgs{
CtxAnything: true,
},
Returns: AnalyzerAnalyzeReturns{
Info: ftypes.ImageReference{
Name: "alpine:3.11",
ID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
},
},
},
scanExpectation: ScanExpectation{
Args: ScanArgs{
Target: "alpine:3.11",
ImageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
LayerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
Options: types.ScanOptions{VulnType: []string{"os"}},
},
Returns: ScanReturns{
Err: errors.New("error"),
},
},
wantErr: "scan failed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := new(MockDriver)
d.ApplyScanExpectation(tt.scanExpectation)
analyzer := new(MockAnalyzer)
analyzer.ApplyAnalyzeExpectation(tt.analyzeExpectation)
s := NewScanner(d, analyzer)
got, err := s.ScanImage(tt.args.options)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
require.Contains(t, err.Error(), tt.wantErr, tt.name)
return
} else {
require.NoError(t, err, tt.name)
}
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -4,8 +4,7 @@ import (
"fmt"
"strings"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/knqyf263/go-version"
)
@@ -28,11 +27,11 @@ func MatchVersions(currentVersion *version.Version, rangeVersions []string) bool
return false
}
func FormatVersion(pkg analyzer.Package) string {
func FormatVersion(pkg types.Package) string {
return formatVersion(pkg.Epoch, pkg.Version, pkg.Release)
}
func FormatSrcVersion(pkg analyzer.Package) string {
func FormatSrcVersion(pkg types.Package) string {
return formatVersion(pkg.SrcEpoch, pkg.SrcVersion, pkg.SrcRelease)
}

View File

@@ -1,32 +0,0 @@
package types
import (
"time"
"github.com/aquasecurity/fanal/types"
"github.com/caarlos0/env/v6"
)
type DockerConfig struct {
AuthURL string `env:"TRIVY_AUTH_URL"`
UserName string `env:"TRIVY_USERNAME"`
Password string `env:"TRIVY_PASSWORD"`
Timeout time.Duration `env:"TRIVY_TIMEOUT_SEC" envDefault:"60s"`
Insecure bool `env:"TRIVY_INSECURE" envDefault:"true"`
NonSSL bool `env:"TRIVY_NON_SSL" envDefault:"false"`
}
func GetDockerOption() (types.DockerOption, error) {
cfg := DockerConfig{}
if err := env.Parse(&cfg); err != nil {
return types.DockerOption{}, err
}
return types.DockerOption{
AuthURL: cfg.AuthURL,
UserName: cfg.UserName,
Password: cfg.Password,
Timeout: cfg.Timeout,
Insecure: cfg.Insecure,
NonSSL: cfg.NonSSL,
}, nil
}

33
pkg/types/docker_conf.go Normal file
View File

@@ -0,0 +1,33 @@
package types
import (
"time"
"github.com/aquasecurity/fanal/types"
"github.com/caarlos0/env/v6"
)
type DockerConfig struct {
UserName string `env:"TRIVY_USERNAME"`
Password string `env:"TRIVY_PASSWORD"`
Insecure bool `env:"TRIVY_INSECURE" envDefault:"true"`
DockerCertPath string `env:"DOCKER_CERT_PATH"`
DockerHost string `env:"DOCKER_HOST"`
}
func GetDockerOption(timeout time.Duration) (types.DockerOption, error) {
cfg := DockerConfig{}
if err := env.Parse(&cfg); err != nil {
return types.DockerOption{}, err
}
return types.DockerOption{
UserName: cfg.UserName,
Password: cfg.Password,
Timeout: timeout,
InsecureSkipTLSVerify: cfg.Insecure,
DockerDaemonCertPath: cfg.DockerCertPath,
DockerDaemonHost: cfg.DockerHost,
}, nil
}

View File

@@ -1,9 +1,6 @@
package types
type ScanOptions struct {
VulnType []string
// for client/server
RemoteURL string
Token string
VulnType []string
ScanRemovedPackages bool
}

View File

@@ -1,11 +1,9 @@
package utils
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
@@ -71,25 +69,6 @@ func FileWalk(root string, targetFiles map[string]struct{}, walkFn func(r io.Rea
return nil
}
func IsCommandAvailable(name string) bool {
cmd := exec.Command(name, "--help")
if err := cmd.Run(); err != nil {
return false
}
return true
}
func Exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}
func StringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
@@ -99,18 +78,6 @@ func StringInSlice(a string, list []string) bool {
return false
}
func Exec(command string, args []string) (string, error) {
cmd := exec.Command(command, args...)
var stdoutBuf, stderrBuf bytes.Buffer
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
if err := cmd.Run(); err != nil {
log.Logger.Debug(stderrBuf.String())
return "", xerrors.Errorf("failed to exec: %w", err)
}
return stdoutBuf.String(), nil
}
func FilterTargets(prefixPath string, targets map[string]struct{}) (map[string]struct{}, error) {
filtered := map[string]struct{}{}
for filename := range targets {

View File

@@ -0,0 +1,116 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package vulnerability
import mock "github.com/stretchr/testify/mock"
import pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
import types "github.com/aquasecurity/trivy/pkg/types"
// MockOperation is an autogenerated mock type for the Operation type
type MockOperation struct {
mock.Mock
}
type FillInfoArgs struct {
Vulns []types.DetectedVulnerability
VulnsAnything bool
Light bool
LightAnything bool
}
type FillInfoExpectation struct {
Args FillInfoArgs
}
func (_m *MockOperation) ApplyFillInfoExpectation(e FillInfoExpectation) {
var args []interface{}
if e.Args.VulnsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Vulns)
}
if e.Args.LightAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Light)
}
_m.On("FillInfo", args...)
}
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []FillInfoExpectation) {
for _, e := range expectations {
_m.ApplyFillInfoExpectation(e)
}
}
// FillInfo provides a mock function with given fields: vulns, light
func (_m *MockOperation) FillInfo(vulns []types.DetectedVulnerability, light bool) {
_m.Called(vulns, light)
}
type FilterArgs struct {
Vulns []types.DetectedVulnerability
VulnsAnything bool
Severities []pkgtypes.Severity
SeveritiesAnything bool
IgnoreUnfixed bool
IgnoreUnfixedAnything bool
IgnoreFile string
IgnoreFileAnything bool
}
type FilterReturns struct {
_a0 []types.DetectedVulnerability
}
type FilterExpectation struct {
Args FilterArgs
Returns FilterReturns
}
func (_m *MockOperation) ApplyFilterExpectation(e FilterExpectation) {
var args []interface{}
if e.Args.VulnsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Vulns)
}
if e.Args.SeveritiesAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Severities)
}
if e.Args.IgnoreUnfixedAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.IgnoreUnfixed)
}
if e.Args.IgnoreFileAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.IgnoreFile)
}
_m.On("Filter", args...).Return(e.Returns._a0)
}
func (_m *MockOperation) ApplyFilterExpectations(expectations []FilterExpectation) {
for _, e := range expectations {
_m.ApplyFilterExpectation(e)
}
}
// Filter provides a mock function with given fields: vulns, severities, ignoreUnfixed, ignoreFile
func (_m *MockOperation) Filter(vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
ret := _m.Called(vulns, severities, ignoreUnfixed, ignoreFile)
var r0 []types.DetectedVulnerability
if rf, ok := ret.Get(0).(func([]types.DetectedVulnerability, []pkgtypes.Severity, bool, string) []types.DetectedVulnerability); ok {
r0 = rf(vulns, severities, ignoreUnfixed, ignoreFile)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.DetectedVulnerability)
}
}
return r0
}

View File

@@ -1,35 +0,0 @@
package vulnerability
import (
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/mock"
)
type MockVulnClient struct {
mock.Mock
}
func NewMockVulnClient() *MockVulnClient {
mockVulnClient := new(MockVulnClient)
mockVulnClient.On("FillInfo", mock.Anything, mock.Anything)
return mockVulnClient
}
func (_m *MockVulnClient) FillInfo(a []types.DetectedVulnerability, b bool) {
_m.Called(a, b)
}
func (_m *MockVulnClient) Filter(a []types.DetectedVulnerability, b []dbTypes.Severity,
c bool, d string) []types.DetectedVulnerability {
ret := _m.Called(a, b, c, d)
ret0 := ret.Get(0)
if ret0 == nil {
return nil
}
vulns, ok := ret0.([]types.DetectedVulnerability)
if !ok {
return nil
}
return vulns
}

484
rpc/cache/service.pb.go vendored Normal file
View File

@@ -0,0 +1,484 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: rpc/cache/service.proto
package cache
import (
fmt "fmt"
common "github.com/aquasecurity/trivy/rpc/common"
proto "github.com/golang/protobuf/proto"
_ "github.com/golang/protobuf/ptypes/empty"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ImageInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
Created *timestamp.Timestamp `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
DockerVersion string `protobuf:"bytes,4,opt,name=docker_version,json=dockerVersion,proto3" json:"docker_version,omitempty"`
Os string `protobuf:"bytes,5,opt,name=os,proto3" json:"os,omitempty"`
HistoryPackages []*common.Package `protobuf:"bytes,6,rep,name=history_packages,json=historyPackages,proto3" json:"history_packages,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ImageInfo) Reset() { *m = ImageInfo{} }
func (m *ImageInfo) String() string { return proto.CompactTextString(m) }
func (*ImageInfo) ProtoMessage() {}
func (*ImageInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{0}
}
func (m *ImageInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ImageInfo.Unmarshal(m, b)
}
func (m *ImageInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ImageInfo.Marshal(b, m, deterministic)
}
func (m *ImageInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_ImageInfo.Merge(m, src)
}
func (m *ImageInfo) XXX_Size() int {
return xxx_messageInfo_ImageInfo.Size(m)
}
func (m *ImageInfo) XXX_DiscardUnknown() {
xxx_messageInfo_ImageInfo.DiscardUnknown(m)
}
var xxx_messageInfo_ImageInfo proto.InternalMessageInfo
func (m *ImageInfo) GetSchemaVersion() int32 {
if m != nil {
return m.SchemaVersion
}
return 0
}
func (m *ImageInfo) GetArchitecture() string {
if m != nil {
return m.Architecture
}
return ""
}
func (m *ImageInfo) GetCreated() *timestamp.Timestamp {
if m != nil {
return m.Created
}
return nil
}
func (m *ImageInfo) GetDockerVersion() string {
if m != nil {
return m.DockerVersion
}
return ""
}
func (m *ImageInfo) GetOs() string {
if m != nil {
return m.Os
}
return ""
}
func (m *ImageInfo) GetHistoryPackages() []*common.Package {
if m != nil {
return m.HistoryPackages
}
return nil
}
type PutImageRequest struct {
ImageId string `protobuf:"bytes,1,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
ImageInfo *ImageInfo `protobuf:"bytes,2,opt,name=image_info,json=imageInfo,proto3" json:"image_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutImageRequest) Reset() { *m = PutImageRequest{} }
func (m *PutImageRequest) String() string { return proto.CompactTextString(m) }
func (*PutImageRequest) ProtoMessage() {}
func (*PutImageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{1}
}
func (m *PutImageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutImageRequest.Unmarshal(m, b)
}
func (m *PutImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutImageRequest.Marshal(b, m, deterministic)
}
func (m *PutImageRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutImageRequest.Merge(m, src)
}
func (m *PutImageRequest) XXX_Size() int {
return xxx_messageInfo_PutImageRequest.Size(m)
}
func (m *PutImageRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutImageRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PutImageRequest proto.InternalMessageInfo
func (m *PutImageRequest) GetImageId() string {
if m != nil {
return m.ImageId
}
return ""
}
func (m *PutImageRequest) GetImageInfo() *ImageInfo {
if m != nil {
return m.ImageInfo
}
return nil
}
type LayerInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
Os *common.OS `protobuf:"bytes,2,opt,name=os,proto3" json:"os,omitempty"`
PackageInfos []*common.PackageInfo `protobuf:"bytes,3,rep,name=package_infos,json=packageInfos,proto3" json:"package_infos,omitempty"`
Applications []*common.Application `protobuf:"bytes,4,rep,name=applications,proto3" json:"applications,omitempty"`
OpaqueDirs []string `protobuf:"bytes,5,rep,name=opaque_dirs,json=opaqueDirs,proto3" json:"opaque_dirs,omitempty"`
WhiteoutFiles []string `protobuf:"bytes,6,rep,name=whiteout_files,json=whiteoutFiles,proto3" json:"whiteout_files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *LayerInfo) Reset() { *m = LayerInfo{} }
func (m *LayerInfo) String() string { return proto.CompactTextString(m) }
func (*LayerInfo) ProtoMessage() {}
func (*LayerInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{2}
}
func (m *LayerInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LayerInfo.Unmarshal(m, b)
}
func (m *LayerInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_LayerInfo.Marshal(b, m, deterministic)
}
func (m *LayerInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_LayerInfo.Merge(m, src)
}
func (m *LayerInfo) XXX_Size() int {
return xxx_messageInfo_LayerInfo.Size(m)
}
func (m *LayerInfo) XXX_DiscardUnknown() {
xxx_messageInfo_LayerInfo.DiscardUnknown(m)
}
var xxx_messageInfo_LayerInfo proto.InternalMessageInfo
func (m *LayerInfo) GetSchemaVersion() int32 {
if m != nil {
return m.SchemaVersion
}
return 0
}
func (m *LayerInfo) GetOs() *common.OS {
if m != nil {
return m.Os
}
return nil
}
func (m *LayerInfo) GetPackageInfos() []*common.PackageInfo {
if m != nil {
return m.PackageInfos
}
return nil
}
func (m *LayerInfo) GetApplications() []*common.Application {
if m != nil {
return m.Applications
}
return nil
}
func (m *LayerInfo) GetOpaqueDirs() []string {
if m != nil {
return m.OpaqueDirs
}
return nil
}
func (m *LayerInfo) GetWhiteoutFiles() []string {
if m != nil {
return m.WhiteoutFiles
}
return nil
}
type PutLayerRequest struct {
LayerId string `protobuf:"bytes,1,opt,name=layer_id,json=layerId,proto3" json:"layer_id,omitempty"`
DecompressedLayerId string `protobuf:"bytes,2,opt,name=decompressed_layer_id,json=decompressedLayerId,proto3" json:"decompressed_layer_id,omitempty"`
LayerInfo *LayerInfo `protobuf:"bytes,3,opt,name=layer_info,json=layerInfo,proto3" json:"layer_info,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutLayerRequest) Reset() { *m = PutLayerRequest{} }
func (m *PutLayerRequest) String() string { return proto.CompactTextString(m) }
func (*PutLayerRequest) ProtoMessage() {}
func (*PutLayerRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{3}
}
func (m *PutLayerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutLayerRequest.Unmarshal(m, b)
}
func (m *PutLayerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutLayerRequest.Marshal(b, m, deterministic)
}
func (m *PutLayerRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutLayerRequest.Merge(m, src)
}
func (m *PutLayerRequest) XXX_Size() int {
return xxx_messageInfo_PutLayerRequest.Size(m)
}
func (m *PutLayerRequest) XXX_DiscardUnknown() {
xxx_messageInfo_PutLayerRequest.DiscardUnknown(m)
}
var xxx_messageInfo_PutLayerRequest proto.InternalMessageInfo
func (m *PutLayerRequest) GetLayerId() string {
if m != nil {
return m.LayerId
}
return ""
}
func (m *PutLayerRequest) GetDecompressedLayerId() string {
if m != nil {
return m.DecompressedLayerId
}
return ""
}
func (m *PutLayerRequest) GetLayerInfo() *LayerInfo {
if m != nil {
return m.LayerInfo
}
return nil
}
type PutResponse struct {
Os *common.OS `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"`
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PutResponse) Reset() { *m = PutResponse{} }
func (m *PutResponse) String() string { return proto.CompactTextString(m) }
func (*PutResponse) ProtoMessage() {}
func (*PutResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{4}
}
func (m *PutResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutResponse.Unmarshal(m, b)
}
func (m *PutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PutResponse.Marshal(b, m, deterministic)
}
func (m *PutResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_PutResponse.Merge(m, src)
}
func (m *PutResponse) XXX_Size() int {
return xxx_messageInfo_PutResponse.Size(m)
}
func (m *PutResponse) XXX_DiscardUnknown() {
xxx_messageInfo_PutResponse.DiscardUnknown(m)
}
var xxx_messageInfo_PutResponse proto.InternalMessageInfo
func (m *PutResponse) GetOs() *common.OS {
if m != nil {
return m.Os
}
return nil
}
func (m *PutResponse) GetEosl() bool {
if m != nil {
return m.Eosl
}
return false
}
type MissingLayersRequest struct {
ImageId string `protobuf:"bytes,1,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
LayerIds []string `protobuf:"bytes,2,rep,name=layer_ids,json=layerIds,proto3" json:"layer_ids,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MissingLayersRequest) Reset() { *m = MissingLayersRequest{} }
func (m *MissingLayersRequest) String() string { return proto.CompactTextString(m) }
func (*MissingLayersRequest) ProtoMessage() {}
func (*MissingLayersRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{5}
}
func (m *MissingLayersRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingLayersRequest.Unmarshal(m, b)
}
func (m *MissingLayersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingLayersRequest.Marshal(b, m, deterministic)
}
func (m *MissingLayersRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingLayersRequest.Merge(m, src)
}
func (m *MissingLayersRequest) XXX_Size() int {
return xxx_messageInfo_MissingLayersRequest.Size(m)
}
func (m *MissingLayersRequest) XXX_DiscardUnknown() {
xxx_messageInfo_MissingLayersRequest.DiscardUnknown(m)
}
var xxx_messageInfo_MissingLayersRequest proto.InternalMessageInfo
func (m *MissingLayersRequest) GetImageId() string {
if m != nil {
return m.ImageId
}
return ""
}
func (m *MissingLayersRequest) GetLayerIds() []string {
if m != nil {
return m.LayerIds
}
return nil
}
type MissingLayersResponse struct {
MissingImage bool `protobuf:"varint,1,opt,name=missing_image,json=missingImage,proto3" json:"missing_image,omitempty"`
MissingLayerIds []string `protobuf:"bytes,2,rep,name=missing_layer_ids,json=missingLayerIds,proto3" json:"missing_layer_ids,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MissingLayersResponse) Reset() { *m = MissingLayersResponse{} }
func (m *MissingLayersResponse) String() string { return proto.CompactTextString(m) }
func (*MissingLayersResponse) ProtoMessage() {}
func (*MissingLayersResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_1f1f7d564abadf42, []int{6}
}
func (m *MissingLayersResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MissingLayersResponse.Unmarshal(m, b)
}
func (m *MissingLayersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MissingLayersResponse.Marshal(b, m, deterministic)
}
func (m *MissingLayersResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_MissingLayersResponse.Merge(m, src)
}
func (m *MissingLayersResponse) XXX_Size() int {
return xxx_messageInfo_MissingLayersResponse.Size(m)
}
func (m *MissingLayersResponse) XXX_DiscardUnknown() {
xxx_messageInfo_MissingLayersResponse.DiscardUnknown(m)
}
var xxx_messageInfo_MissingLayersResponse proto.InternalMessageInfo
func (m *MissingLayersResponse) GetMissingImage() bool {
if m != nil {
return m.MissingImage
}
return false
}
func (m *MissingLayersResponse) GetMissingLayerIds() []string {
if m != nil {
return m.MissingLayerIds
}
return nil
}
func init() {
proto.RegisterType((*ImageInfo)(nil), "trivy.cache.v1.ImageInfo")
proto.RegisterType((*PutImageRequest)(nil), "trivy.cache.v1.PutImageRequest")
proto.RegisterType((*LayerInfo)(nil), "trivy.cache.v1.LayerInfo")
proto.RegisterType((*PutLayerRequest)(nil), "trivy.cache.v1.PutLayerRequest")
proto.RegisterType((*PutResponse)(nil), "trivy.cache.v1.PutResponse")
proto.RegisterType((*MissingLayersRequest)(nil), "trivy.cache.v1.MissingLayersRequest")
proto.RegisterType((*MissingLayersResponse)(nil), "trivy.cache.v1.MissingLayersResponse")
}
func init() { proto.RegisterFile("rpc/cache/service.proto", fileDescriptor_1f1f7d564abadf42) }
var fileDescriptor_1f1f7d564abadf42 = []byte{
// 676 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xd3, 0x4a,
0x10, 0x56, 0x92, 0xa6, 0x8d, 0x27, 0x49, 0xdb, 0xb3, 0xe7, 0xf4, 0x90, 0xa6, 0x17, 0x8d, 0x0c,
0x95, 0x22, 0x2e, 0x6c, 0x11, 0x10, 0xea, 0x0d, 0x08, 0x28, 0x20, 0x45, 0x2a, 0x50, 0x2d, 0x88,
0x0b, 0x84, 0x64, 0x6d, 0xd7, 0x9b, 0x64, 0xd5, 0xd8, 0xeb, 0xee, 0xae, 0x83, 0xf2, 0x00, 0xbc,
0x04, 0x3c, 0x24, 0xaf, 0x80, 0x3c, 0xb6, 0xd3, 0x24, 0x84, 0xbf, 0x3b, 0xcf, 0x8f, 0xbf, 0x99,
0xef, 0xfb, 0xc6, 0x86, 0x5b, 0x3a, 0xe1, 0x3e, 0x67, 0x7c, 0x22, 0x7c, 0x23, 0xf4, 0x4c, 0x72,
0xe1, 0x25, 0x5a, 0x59, 0x45, 0x76, 0xad, 0x96, 0xb3, 0xb9, 0x87, 0x25, 0x6f, 0x76, 0xaf, 0x7b,
0x3c, 0x56, 0x6a, 0x3c, 0x15, 0x3e, 0x56, 0x2f, 0xd3, 0x91, 0x6f, 0x65, 0x24, 0x8c, 0x65, 0x51,
0x92, 0xbf, 0xd0, 0x7d, 0x38, 0x96, 0x76, 0x92, 0x5e, 0x7a, 0x5c, 0x45, 0x3e, 0xbb, 0x4e, 0x99,
0x11, 0x3c, 0xd5, 0xd2, 0xce, 0x7d, 0x04, 0xf2, 0x71, 0x8e, 0x8a, 0x22, 0x15, 0xaf, 0x0e, 0xea,
0x1e, 0xad, 0x03, 0x8b, 0x28, 0xb1, 0xf3, 0xbc, 0xe8, 0x7e, 0xae, 0x82, 0x33, 0x8c, 0xd8, 0x58,
0x0c, 0xe3, 0x91, 0x22, 0x27, 0xb0, 0x6b, 0xf8, 0x44, 0x44, 0x2c, 0x98, 0x09, 0x6d, 0xa4, 0x8a,
0x3b, 0x95, 0x5e, 0xa5, 0x5f, 0xa7, 0xed, 0x3c, 0xfb, 0x3e, 0x4f, 0x12, 0x17, 0x5a, 0x4c, 0xf3,
0x89, 0xb4, 0x82, 0xdb, 0x54, 0x8b, 0x4e, 0xb5, 0x57, 0xe9, 0x3b, 0x74, 0x25, 0x47, 0x1e, 0xc0,
0x0e, 0xd7, 0x82, 0x59, 0x11, 0x76, 0x6a, 0xbd, 0x4a, 0xbf, 0x39, 0xe8, 0x7a, 0xf9, 0x1e, 0x5e,
0xb9, 0x87, 0xf7, 0xae, 0x24, 0x48, 0xcb, 0xd6, 0x6c, 0x81, 0x50, 0xf1, 0x2b, 0xa1, 0x17, 0x0b,
0x6c, 0x21, 0x76, 0x3b, 0xcf, 0x96, 0x0b, 0xec, 0x42, 0x55, 0x99, 0x4e, 0x1d, 0x4b, 0x55, 0x65,
0xc8, 0x13, 0xd8, 0x9f, 0x48, 0x63, 0x95, 0x9e, 0x07, 0x09, 0xe3, 0x57, 0x6c, 0x2c, 0x4c, 0x67,
0xbb, 0x57, 0xeb, 0x37, 0x07, 0x07, 0x5e, 0x21, 0x33, 0x2a, 0xe3, 0x5d, 0xe4, 0x55, 0xba, 0x57,
0xb4, 0x17, 0xb1, 0x71, 0x47, 0xb0, 0x77, 0x91, 0x5a, 0x54, 0x82, 0x8a, 0xeb, 0x54, 0x18, 0x4b,
0x0e, 0xa1, 0x21, 0xb3, 0x38, 0x90, 0x21, 0xca, 0xe0, 0xd0, 0x1d, 0x8c, 0x87, 0x21, 0x39, 0x05,
0x28, 0x4a, 0xf1, 0x48, 0x21, 0xfd, 0xe6, 0xe0, 0xd0, 0x5b, 0x35, 0xd4, 0x5b, 0xc8, 0x4a, 0x1d,
0x59, 0x3e, 0xba, 0x5f, 0xab, 0xe0, 0x9c, 0xb3, 0xb9, 0xd0, 0x7f, 0xa3, 0x77, 0x0f, 0xe9, 0xe6,
0x63, 0xf6, 0x57, 0x09, 0xbd, 0x79, 0x8b, 0x02, 0x3c, 0x86, 0x76, 0x41, 0x1c, 0x57, 0x32, 0x9d,
0x1a, 0xb2, 0x3f, 0xdc, 0xc8, 0x1e, 0x77, 0x6a, 0x25, 0x37, 0x81, 0x21, 0x8f, 0xa0, 0xc5, 0x92,
0x64, 0x2a, 0x39, 0xb3, 0x52, 0xc5, 0xa6, 0xb3, 0xb5, 0xe9, 0xf5, 0xa7, 0x37, 0x1d, 0x74, 0xa5,
0x9d, 0x1c, 0x43, 0x53, 0x25, 0xec, 0x3a, 0x15, 0x41, 0x28, 0x75, 0x66, 0x4c, 0xad, 0xef, 0x50,
0xc8, 0x53, 0xcf, 0xa5, 0x36, 0x19, 0xd1, 0x4f, 0xd9, 0x6d, 0xa8, 0xd4, 0x06, 0x23, 0x39, 0x2d,
0xec, 0x71, 0x68, 0xbb, 0xcc, 0xbe, 0xcc, 0x92, 0xee, 0x97, 0x0a, 0xda, 0x80, 0x02, 0x2d, 0xd9,
0x30, 0xcd, 0xe2, 0x25, 0x1b, 0x30, 0x1e, 0x86, 0x64, 0x00, 0x07, 0xa1, 0xe0, 0x2a, 0x4a, 0xb4,
0x30, 0x46, 0x84, 0xc1, 0xa2, 0x2f, 0x3f, 0xc8, 0x7f, 0x97, 0x8b, 0xe7, 0xc5, 0x3b, 0xa7, 0x00,
0x45, 0x5b, 0x66, 0x5d, 0x6d, 0xb3, 0x75, 0x0b, 0x87, 0xa8, 0x33, 0x2d, 0x1f, 0xdd, 0x33, 0x68,
0x5e, 0xa4, 0x96, 0x0a, 0x93, 0xa8, 0xd8, 0x88, 0xc2, 0x94, 0xca, 0x2f, 0x4c, 0x21, 0xb0, 0x25,
0x94, 0x99, 0xe2, 0x36, 0x0d, 0x8a, 0xcf, 0xee, 0x6b, 0xf8, 0xef, 0x95, 0x34, 0x46, 0xc6, 0x63,
0x9c, 0x61, 0xfe, 0xe0, 0xd8, 0x8e, 0xc0, 0x29, 0x89, 0x65, 0x47, 0x90, 0xc9, 0xd6, 0x28, 0x14,
0x30, 0xee, 0x04, 0x0e, 0xd6, 0xf0, 0x8a, 0xf5, 0x6e, 0x43, 0x3b, 0xca, 0x0b, 0x01, 0x02, 0x21,
0x6a, 0x83, 0xb6, 0x8a, 0x24, 0x1e, 0x27, 0xb9, 0x0b, 0xff, 0x94, 0x4d, 0xeb, 0x23, 0xf6, 0xa2,
0x25, 0xd8, 0x61, 0x68, 0x06, 0xdf, 0x2a, 0x50, 0x3f, 0xcb, 0x04, 0x22, 0x67, 0xd0, 0x28, 0xbf,
0x15, 0x72, 0xbc, 0x2e, 0xdd, 0xda, 0x57, 0xd4, 0xfd, 0xff, 0x87, 0xcf, 0xfe, 0x45, 0xf6, 0xfb,
0x29, 0x40, 0x10, 0x7d, 0x23, 0xc8, 0xf2, 0x0d, 0xfc, 0x14, 0xe4, 0x23, 0xb4, 0x57, 0xd8, 0x93,
0x3b, 0xeb, 0x48, 0x9b, 0xc4, 0xee, 0x9e, 0xfc, 0xa6, 0x2b, 0x97, 0xf0, 0xd9, 0xce, 0x87, 0x3a,
0x76, 0x5c, 0x6e, 0xe3, 0xd8, 0xfb, 0xdf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x84, 0x32, 0x2e,
0xcc, 0x05, 0x00, 0x00,
}

58
rpc/cache/service.proto vendored Normal file
View File

@@ -0,0 +1,58 @@
syntax = "proto3";
package trivy.cache.v1;
option go_package = "cache";
import "google/protobuf/timestamp.proto";
import "github.com/aquasecurity/trivy/rpc/common/service.proto";
import "google/protobuf/empty.proto";
service Cache {
rpc PutImage(PutImageRequest) returns (google.protobuf.Empty);
rpc PutLayer(PutLayerRequest) returns (google.protobuf.Empty);
rpc MissingLayers(MissingLayersRequest) returns (MissingLayersResponse);
}
message ImageInfo {
int32 schema_version = 1;
string architecture = 2;
google.protobuf.Timestamp created = 3;
string docker_version = 4;
string os = 5;
repeated common.Package history_packages = 6;
}
message PutImageRequest {
string image_id = 1;
ImageInfo image_info = 2;
}
message LayerInfo {
int32 schema_version = 1;
common.OS os = 2;
repeated common.PackageInfo package_infos = 3;
repeated common.Application applications = 4;
repeated string opaque_dirs = 5;
repeated string whiteout_files = 6;
}
message PutLayerRequest {
string layer_id = 1;
string decompressed_layer_id = 2;
LayerInfo layer_info = 3;
}
message PutResponse {
common.OS os = 1;
bool eosl = 2;
}
message MissingLayersRequest {
string image_id = 1;
repeated string layer_ids = 2;
}
message MissingLayersResponse {
bool missing_image = 1;
repeated string missing_layer_ids = 2;
}

1258
rpc/cache/service.twirp.go vendored Normal file

File diff suppressed because it is too large Load Diff

503
rpc/common/service.pb.go Normal file
View File

@@ -0,0 +1,503 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: rpc/common/service.proto
package common
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Severity int32
const (
Severity_UNKNOWN Severity = 0
Severity_LOW Severity = 1
Severity_MEDIUM Severity = 2
Severity_HIGH Severity = 3
Severity_CRITICAL Severity = 4
)
var Severity_name = map[int32]string{
0: "UNKNOWN",
1: "LOW",
2: "MEDIUM",
3: "HIGH",
4: "CRITICAL",
}
var Severity_value = map[string]int32{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
func (x Severity) String() string {
return proto.EnumName(Severity_name, int32(x))
}
func (Severity) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{0}
}
type OS struct {
Family string `protobuf:"bytes,1,opt,name=family,proto3" json:"family,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OS) Reset() { *m = OS{} }
func (m *OS) String() string { return proto.CompactTextString(m) }
func (*OS) ProtoMessage() {}
func (*OS) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{0}
}
func (m *OS) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OS.Unmarshal(m, b)
}
func (m *OS) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OS.Marshal(b, m, deterministic)
}
func (m *OS) XXX_Merge(src proto.Message) {
xxx_messageInfo_OS.Merge(m, src)
}
func (m *OS) XXX_Size() int {
return xxx_messageInfo_OS.Size(m)
}
func (m *OS) XXX_DiscardUnknown() {
xxx_messageInfo_OS.DiscardUnknown(m)
}
var xxx_messageInfo_OS proto.InternalMessageInfo
func (m *OS) GetFamily() string {
if m != nil {
return m.Family
}
return ""
}
func (m *OS) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type PackageInfo struct {
FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
Packages []*Package `protobuf:"bytes,2,rep,name=packages,proto3" json:"packages,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PackageInfo) Reset() { *m = PackageInfo{} }
func (m *PackageInfo) String() string { return proto.CompactTextString(m) }
func (*PackageInfo) ProtoMessage() {}
func (*PackageInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{1}
}
func (m *PackageInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PackageInfo.Unmarshal(m, b)
}
func (m *PackageInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PackageInfo.Marshal(b, m, deterministic)
}
func (m *PackageInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_PackageInfo.Merge(m, src)
}
func (m *PackageInfo) XXX_Size() int {
return xxx_messageInfo_PackageInfo.Size(m)
}
func (m *PackageInfo) XXX_DiscardUnknown() {
xxx_messageInfo_PackageInfo.DiscardUnknown(m)
}
var xxx_messageInfo_PackageInfo proto.InternalMessageInfo
func (m *PackageInfo) GetFilePath() string {
if m != nil {
return m.FilePath
}
return ""
}
func (m *PackageInfo) GetPackages() []*Package {
if m != nil {
return m.Packages
}
return nil
}
type Application struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
Libraries []*Library `protobuf:"bytes,3,rep,name=libraries,proto3" json:"libraries,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Application) Reset() { *m = Application{} }
func (m *Application) String() string { return proto.CompactTextString(m) }
func (*Application) ProtoMessage() {}
func (*Application) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{2}
}
func (m *Application) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Application.Unmarshal(m, b)
}
func (m *Application) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Application.Marshal(b, m, deterministic)
}
func (m *Application) XXX_Merge(src proto.Message) {
xxx_messageInfo_Application.Merge(m, src)
}
func (m *Application) XXX_Size() int {
return xxx_messageInfo_Application.Size(m)
}
func (m *Application) XXX_DiscardUnknown() {
xxx_messageInfo_Application.DiscardUnknown(m)
}
var xxx_messageInfo_Application proto.InternalMessageInfo
func (m *Application) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Application) GetFilePath() string {
if m != nil {
return m.FilePath
}
return ""
}
func (m *Application) GetLibraries() []*Library {
if m != nil {
return m.Libraries
}
return nil
}
type Package struct {
// binary package
// e.g. bind-utils
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
Release string `protobuf:"bytes,3,opt,name=release,proto3" json:"release,omitempty"`
Epoch int32 `protobuf:"varint,4,opt,name=epoch,proto3" json:"epoch,omitempty"`
Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"`
// src package containing some binary packages
// e.g. bind
SrcName string `protobuf:"bytes,6,opt,name=src_name,json=srcName,proto3" json:"src_name,omitempty"`
SrcVersion string `protobuf:"bytes,7,opt,name=src_version,json=srcVersion,proto3" json:"src_version,omitempty"`
SrcRelease string `protobuf:"bytes,8,opt,name=src_release,json=srcRelease,proto3" json:"src_release,omitempty"`
SrcEpoch int32 `protobuf:"varint,9,opt,name=src_epoch,json=srcEpoch,proto3" json:"src_epoch,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Package) Reset() { *m = Package{} }
func (m *Package) String() string { return proto.CompactTextString(m) }
func (*Package) ProtoMessage() {}
func (*Package) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{3}
}
func (m *Package) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Package.Unmarshal(m, b)
}
func (m *Package) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Package.Marshal(b, m, deterministic)
}
func (m *Package) XXX_Merge(src proto.Message) {
xxx_messageInfo_Package.Merge(m, src)
}
func (m *Package) XXX_Size() int {
return xxx_messageInfo_Package.Size(m)
}
func (m *Package) XXX_DiscardUnknown() {
xxx_messageInfo_Package.DiscardUnknown(m)
}
var xxx_messageInfo_Package proto.InternalMessageInfo
func (m *Package) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Package) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Package) GetRelease() string {
if m != nil {
return m.Release
}
return ""
}
func (m *Package) GetEpoch() int32 {
if m != nil {
return m.Epoch
}
return 0
}
func (m *Package) GetArch() string {
if m != nil {
return m.Arch
}
return ""
}
func (m *Package) GetSrcName() string {
if m != nil {
return m.SrcName
}
return ""
}
func (m *Package) GetSrcVersion() string {
if m != nil {
return m.SrcVersion
}
return ""
}
func (m *Package) GetSrcRelease() string {
if m != nil {
return m.SrcRelease
}
return ""
}
func (m *Package) GetSrcEpoch() int32 {
if m != nil {
return m.SrcEpoch
}
return 0
}
type Library struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Library) Reset() { *m = Library{} }
func (m *Library) String() string { return proto.CompactTextString(m) }
func (*Library) ProtoMessage() {}
func (*Library) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{4}
}
func (m *Library) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Library.Unmarshal(m, b)
}
func (m *Library) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Library.Marshal(b, m, deterministic)
}
func (m *Library) XXX_Merge(src proto.Message) {
xxx_messageInfo_Library.Merge(m, src)
}
func (m *Library) XXX_Size() int {
return xxx_messageInfo_Library.Size(m)
}
func (m *Library) XXX_DiscardUnknown() {
xxx_messageInfo_Library.DiscardUnknown(m)
}
var xxx_messageInfo_Library proto.InternalMessageInfo
func (m *Library) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Library) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type Vulnerability struct {
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.common.Severity" json:"severity,omitempty"`
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
func (m *Vulnerability) String() string { return proto.CompactTextString(m) }
func (*Vulnerability) ProtoMessage() {}
func (*Vulnerability) Descriptor() ([]byte, []int) {
return fileDescriptor_6e749acacaaabfff, []int{5}
}
func (m *Vulnerability) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Vulnerability.Unmarshal(m, b)
}
func (m *Vulnerability) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Vulnerability.Marshal(b, m, deterministic)
}
func (m *Vulnerability) XXX_Merge(src proto.Message) {
xxx_messageInfo_Vulnerability.Merge(m, src)
}
func (m *Vulnerability) XXX_Size() int {
return xxx_messageInfo_Vulnerability.Size(m)
}
func (m *Vulnerability) XXX_DiscardUnknown() {
xxx_messageInfo_Vulnerability.DiscardUnknown(m)
}
var xxx_messageInfo_Vulnerability proto.InternalMessageInfo
func (m *Vulnerability) GetVulnerabilityId() string {
if m != nil {
return m.VulnerabilityId
}
return ""
}
func (m *Vulnerability) GetPkgName() string {
if m != nil {
return m.PkgName
}
return ""
}
func (m *Vulnerability) GetInstalledVersion() string {
if m != nil {
return m.InstalledVersion
}
return ""
}
func (m *Vulnerability) GetFixedVersion() string {
if m != nil {
return m.FixedVersion
}
return ""
}
func (m *Vulnerability) GetTitle() string {
if m != nil {
return m.Title
}
return ""
}
func (m *Vulnerability) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *Vulnerability) GetSeverity() Severity {
if m != nil {
return m.Severity
}
return Severity_UNKNOWN
}
func (m *Vulnerability) GetReferences() []string {
if m != nil {
return m.References
}
return nil
}
func init() {
proto.RegisterEnum("trivy.common.Severity", Severity_name, Severity_value)
proto.RegisterType((*OS)(nil), "trivy.common.OS")
proto.RegisterType((*PackageInfo)(nil), "trivy.common.PackageInfo")
proto.RegisterType((*Application)(nil), "trivy.common.Application")
proto.RegisterType((*Package)(nil), "trivy.common.Package")
proto.RegisterType((*Library)(nil), "trivy.common.Library")
proto.RegisterType((*Vulnerability)(nil), "trivy.common.Vulnerability")
}
func init() { proto.RegisterFile("rpc/common/service.proto", fileDescriptor_6e749acacaaabfff) }
var fileDescriptor_6e749acacaaabfff = []byte{
// 543 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xd1, 0x6e, 0xd3, 0x30,
0x14, 0x25, 0x49, 0xdb, 0xa4, 0x37, 0x1d, 0x04, 0x6b, 0x4c, 0x41, 0x93, 0xa0, 0x0a, 0x2f, 0x05,
0xa4, 0x0e, 0xba, 0x07, 0x9e, 0xc7, 0x36, 0xb1, 0x88, 0xae, 0x9d, 0x32, 0xb6, 0x49, 0x48, 0xa8,
0x72, 0x5d, 0xb7, 0xb5, 0x9a, 0x26, 0x91, 0x1d, 0x2a, 0xf2, 0x5d, 0x7c, 0x1b, 0xef, 0xc8, 0x8e,
0x93, 0xb6, 0x88, 0x97, 0xbd, 0xf9, 0xde, 0x73, 0x7c, 0xcf, 0x39, 0x37, 0x0e, 0xf8, 0x3c, 0x23,
0x27, 0x24, 0x5d, 0xaf, 0xd3, 0xe4, 0x44, 0x50, 0xbe, 0x61, 0x84, 0xf6, 0x33, 0x9e, 0xe6, 0x29,
0xea, 0xe4, 0x9c, 0x6d, 0x8a, 0x7e, 0x89, 0x05, 0x1f, 0xc0, 0x1c, 0xdf, 0xa2, 0x23, 0x68, 0xcd,
0xf1, 0x9a, 0xc5, 0x85, 0x6f, 0x74, 0x8d, 0x5e, 0x3b, 0xd2, 0x15, 0x42, 0xd0, 0x48, 0xf0, 0x9a,
0xfa, 0xa6, 0xea, 0xaa, 0x73, 0xf0, 0x03, 0xdc, 0x1b, 0x4c, 0x56, 0x78, 0x41, 0xc3, 0x64, 0x9e,
0xa2, 0x63, 0x68, 0xcf, 0x59, 0x4c, 0x27, 0x19, 0xce, 0x97, 0xfa, 0xb6, 0x23, 0x1b, 0x37, 0x38,
0x5f, 0xa2, 0x8f, 0xe0, 0x64, 0x25, 0x57, 0xf8, 0x66, 0xd7, 0xea, 0xb9, 0x83, 0x17, 0xfd, 0x5d,
0xf9, 0xbe, 0x9e, 0x14, 0xd5, 0xb4, 0x40, 0x80, 0x7b, 0x96, 0x65, 0x31, 0x23, 0x38, 0x67, 0x69,
0x22, 0x1d, 0xe4, 0x45, 0x46, 0xf5, 0x64, 0x75, 0xde, 0x97, 0x34, 0xff, 0x91, 0x3c, 0x85, 0x76,
0xcc, 0xa6, 0x1c, 0x73, 0x46, 0x85, 0x6f, 0xfd, 0x4f, 0x73, 0xa8, 0xe0, 0x22, 0xda, 0xf2, 0x82,
0x3f, 0x06, 0xd8, 0xda, 0x4a, 0x9d, 0xd9, 0xd8, 0x66, 0x46, 0x3e, 0xd8, 0x1b, 0xca, 0x05, 0x4b,
0x13, 0xad, 0x57, 0x95, 0x12, 0xe1, 0x34, 0xa6, 0x58, 0x50, 0xdf, 0x2a, 0x11, 0x5d, 0xa2, 0x43,
0x68, 0xd2, 0x2c, 0x25, 0x4b, 0xbf, 0xd1, 0x35, 0x7a, 0xcd, 0xa8, 0x2c, 0xe4, 0x74, 0xcc, 0xc9,
0xd2, 0x6f, 0x96, 0xd3, 0xe5, 0x19, 0xbd, 0x04, 0x47, 0x70, 0x32, 0x51, 0xaa, 0xad, 0x72, 0x88,
0xe0, 0x64, 0x24, 0x85, 0x5f, 0x83, 0x2b, 0xa1, 0x4a, 0xdc, 0x56, 0x28, 0x08, 0x4e, 0xee, 0xb5,
0xbe, 0x26, 0x54, 0x1e, 0x9c, 0x9a, 0x10, 0x69, 0x1b, 0xc7, 0xd0, 0x96, 0x84, 0xd2, 0x4a, 0x5b,
0x59, 0x91, 0x6a, 0x97, 0xb2, 0x0e, 0x3e, 0x81, 0xad, 0xb7, 0xf1, 0xb8, 0xd8, 0xc1, 0x6f, 0x13,
0x0e, 0xee, 0x7f, 0xc6, 0x09, 0xe5, 0x78, 0xca, 0x62, 0x96, 0x17, 0xe8, 0x2d, 0x78, 0x9b, 0xdd,
0xc6, 0x84, 0xcd, 0xf4, 0xac, 0x67, 0x7b, 0xfd, 0x70, 0x26, 0xf3, 0x66, 0xab, 0xc5, 0x64, 0xe7,
0x65, 0xd9, 0xd9, 0x6a, 0xa1, 0xf2, 0xbe, 0x87, 0xe7, 0x2c, 0x11, 0x39, 0x8e, 0x63, 0x3a, 0xab,
0x53, 0x97, 0x8b, 0xf5, 0x6a, 0xa0, 0xca, 0xfe, 0x06, 0x0e, 0xe6, 0xec, 0xd7, 0x0e, 0xb1, 0xa1,
0x88, 0x1d, 0xd5, 0xac, 0x48, 0x87, 0xd0, 0xcc, 0x59, 0x1e, 0x53, 0xbd, 0xf1, 0xb2, 0x40, 0x5d,
0x70, 0x67, 0x54, 0x10, 0xce, 0x32, 0xf9, 0xca, 0xf4, 0xd6, 0x77, 0x5b, 0x68, 0x00, 0x8e, 0xa0,
0x1b, 0xca, 0x59, 0x5e, 0xa8, 0xb5, 0x3f, 0x1d, 0x1c, 0xed, 0x3f, 0xa3, 0x5b, 0x8d, 0x46, 0x35,
0x0f, 0xbd, 0x02, 0xe0, 0x74, 0x4e, 0x39, 0x4d, 0x08, 0x15, 0xbe, 0xd3, 0xb5, 0xe4, 0xb7, 0xd8,
0x76, 0xde, 0x5d, 0x80, 0x53, 0xdd, 0x42, 0x2e, 0xd8, 0x77, 0xa3, 0xaf, 0xa3, 0xf1, 0xc3, 0xc8,
0x7b, 0x82, 0x6c, 0xb0, 0x86, 0xe3, 0x07, 0xcf, 0x40, 0x00, 0xad, 0xeb, 0xcb, 0x8b, 0xf0, 0xee,
0xda, 0x33, 0x91, 0x03, 0x8d, 0xab, 0xf0, 0xcb, 0x95, 0x67, 0xa1, 0x0e, 0x38, 0xe7, 0x51, 0xf8,
0x2d, 0x3c, 0x3f, 0x1b, 0x7a, 0x8d, 0xcf, 0xce, 0xf7, 0x56, 0x69, 0x61, 0xda, 0x52, 0x7f, 0xf4,
0xe9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x19, 0x99, 0x46, 0xed, 0x03, 0x00, 0x00,
}

60
rpc/common/service.proto Normal file
View File

@@ -0,0 +1,60 @@
syntax = "proto3";
package trivy.common;
option go_package = "common";
message OS {
string family = 1;
string name = 2;
}
message PackageInfo {
string file_path = 1;
repeated Package packages = 2;
}
message Application {
string type = 1;
string file_path = 2;
repeated Library libraries = 3;
}
message Package {
// binary package
// e.g. bind-utils
string name = 1;
string version = 2;
string release = 3;
int32 epoch = 4;
string arch = 5;
// src package containing some binary packages
// e.g. bind
string src_name = 6;
string src_version = 7;
string src_release = 8;
int32 src_epoch = 9;
}
message Library {
string name = 1;
string version = 2;
}
message Vulnerability {
string vulnerability_id = 1;
string pkg_name = 2;
string installed_version = 3;
string fixed_version = 4;
string title = 5;
string description = 6;
Severity severity = 7;
repeated string references = 8;
}
enum Severity {
UNKNOWN = 0;
LOW = 1;
MEDIUM = 2;
HIGH = 3;
CRITICAL = 4;
}

View File

@@ -5,6 +5,7 @@ package detector
import (
fmt "fmt"
common "github.com/aquasecurity/trivy/rpc/common"
proto "github.com/golang/protobuf/proto"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
math "math"
@@ -21,44 +22,10 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Severity int32
const (
Severity_UNKNOWN Severity = 0
Severity_LOW Severity = 1
Severity_MEDIUM Severity = 2
Severity_HIGH Severity = 3
Severity_CRITICAL Severity = 4
)
var Severity_name = map[int32]string{
0: "UNKNOWN",
1: "LOW",
2: "MEDIUM",
3: "HIGH",
4: "CRITICAL",
}
var Severity_value = map[string]int32{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
func (x Severity) String() string {
return proto.EnumName(Severity_name, int32(x))
}
func (Severity) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{0}
}
type OSDetectRequest struct {
OsFamily string `protobuf:"bytes,1,opt,name=os_family,json=osFamily,proto3" json:"os_family,omitempty"`
OsName string `protobuf:"bytes,2,opt,name=os_name,json=osName,proto3" json:"os_name,omitempty"`
Packages []*Package `protobuf:"bytes,3,rep,name=packages,proto3" json:"packages,omitempty"`
Packages []*common.Package `protobuf:"bytes,3,rep,name=packages,proto3" json:"packages,omitempty"`
ImageName string `protobuf:"bytes,4,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"`
Created *timestamp.Timestamp `protobuf:"bytes,5,opt,name=created,proto3" json:"created,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -105,7 +72,7 @@ func (m *OSDetectRequest) GetOsName() string {
return ""
}
func (m *OSDetectRequest) GetPackages() []*Package {
func (m *OSDetectRequest) GetPackages() []*common.Package {
if m != nil {
return m.Packages
}
@@ -127,11 +94,11 @@ func (m *OSDetectRequest) GetCreated() *timestamp.Timestamp {
}
type DetectResponse struct {
Vulnerabilities []*Vulnerability `protobuf:"bytes,1,rep,name=vulnerabilities,proto3" json:"vulnerabilities,omitempty"`
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Vulnerabilities []*common.Vulnerability `protobuf:"bytes,1,rep,name=vulnerabilities,proto3" json:"vulnerabilities,omitempty"`
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DetectResponse) Reset() { *m = DetectResponse{} }
@@ -159,7 +126,7 @@ func (m *DetectResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_DetectResponse proto.InternalMessageInfo
func (m *DetectResponse) GetVulnerabilities() []*Vulnerability {
func (m *DetectResponse) GetVulnerabilities() []*common.Vulnerability {
if m != nil {
return m.Vulnerabilities
}
@@ -173,116 +140,9 @@ func (m *DetectResponse) GetEosl() bool {
return false
}
type Package struct {
// binary package
// e.g. bind-utils
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
Release string `protobuf:"bytes,3,opt,name=release,proto3" json:"release,omitempty"`
Epoch int32 `protobuf:"varint,4,opt,name=epoch,proto3" json:"epoch,omitempty"`
Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"`
// src package containing some binary packages
// e.g. bind
SrcName string `protobuf:"bytes,6,opt,name=src_name,json=srcName,proto3" json:"src_name,omitempty"`
SrcVersion string `protobuf:"bytes,7,opt,name=src_version,json=srcVersion,proto3" json:"src_version,omitempty"`
SrcRelease string `protobuf:"bytes,8,opt,name=src_release,json=srcRelease,proto3" json:"src_release,omitempty"`
SrcEpoch int32 `protobuf:"varint,9,opt,name=src_epoch,json=srcEpoch,proto3" json:"src_epoch,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Package) Reset() { *m = Package{} }
func (m *Package) String() string { return proto.CompactTextString(m) }
func (*Package) ProtoMessage() {}
func (*Package) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{2}
}
func (m *Package) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Package.Unmarshal(m, b)
}
func (m *Package) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Package.Marshal(b, m, deterministic)
}
func (m *Package) XXX_Merge(src proto.Message) {
xxx_messageInfo_Package.Merge(m, src)
}
func (m *Package) XXX_Size() int {
return xxx_messageInfo_Package.Size(m)
}
func (m *Package) XXX_DiscardUnknown() {
xxx_messageInfo_Package.DiscardUnknown(m)
}
var xxx_messageInfo_Package proto.InternalMessageInfo
func (m *Package) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Package) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Package) GetRelease() string {
if m != nil {
return m.Release
}
return ""
}
func (m *Package) GetEpoch() int32 {
if m != nil {
return m.Epoch
}
return 0
}
func (m *Package) GetArch() string {
if m != nil {
return m.Arch
}
return ""
}
func (m *Package) GetSrcName() string {
if m != nil {
return m.SrcName
}
return ""
}
func (m *Package) GetSrcVersion() string {
if m != nil {
return m.SrcVersion
}
return ""
}
func (m *Package) GetSrcRelease() string {
if m != nil {
return m.SrcRelease
}
return ""
}
func (m *Package) GetSrcEpoch() int32 {
if m != nil {
return m.SrcEpoch
}
return 0
}
type LibDetectRequest struct {
FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
Libraries []*Library `protobuf:"bytes,2,rep,name=libraries,proto3" json:"libraries,omitempty"`
Libraries []*common.Library `protobuf:"bytes,2,rep,name=libraries,proto3" json:"libraries,omitempty"`
ImageName string `protobuf:"bytes,3,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"`
Created *timestamp.Timestamp `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -294,7 +154,7 @@ func (m *LibDetectRequest) Reset() { *m = LibDetectRequest{} }
func (m *LibDetectRequest) String() string { return proto.CompactTextString(m) }
func (*LibDetectRequest) ProtoMessage() {}
func (*LibDetectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{3}
return fileDescriptor_93e16dbd737b8924, []int{2}
}
func (m *LibDetectRequest) XXX_Unmarshal(b []byte) error {
@@ -322,7 +182,7 @@ func (m *LibDetectRequest) GetFilePath() string {
return ""
}
func (m *LibDetectRequest) GetLibraries() []*Library {
func (m *LibDetectRequest) GetLibraries() []*common.Library {
if m != nil {
return m.Libraries
}
@@ -343,204 +203,41 @@ func (m *LibDetectRequest) GetCreated() *timestamp.Timestamp {
return nil
}
type Library struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Library) Reset() { *m = Library{} }
func (m *Library) String() string { return proto.CompactTextString(m) }
func (*Library) ProtoMessage() {}
func (*Library) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{4}
}
func (m *Library) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Library.Unmarshal(m, b)
}
func (m *Library) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Library.Marshal(b, m, deterministic)
}
func (m *Library) XXX_Merge(src proto.Message) {
xxx_messageInfo_Library.Merge(m, src)
}
func (m *Library) XXX_Size() int {
return xxx_messageInfo_Library.Size(m)
}
func (m *Library) XXX_DiscardUnknown() {
xxx_messageInfo_Library.DiscardUnknown(m)
}
var xxx_messageInfo_Library proto.InternalMessageInfo
func (m *Library) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Library) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type Vulnerability struct {
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.detector.Severity" json:"severity,omitempty"`
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
func (m *Vulnerability) String() string { return proto.CompactTextString(m) }
func (*Vulnerability) ProtoMessage() {}
func (*Vulnerability) Descriptor() ([]byte, []int) {
return fileDescriptor_93e16dbd737b8924, []int{5}
}
func (m *Vulnerability) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Vulnerability.Unmarshal(m, b)
}
func (m *Vulnerability) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Vulnerability.Marshal(b, m, deterministic)
}
func (m *Vulnerability) XXX_Merge(src proto.Message) {
xxx_messageInfo_Vulnerability.Merge(m, src)
}
func (m *Vulnerability) XXX_Size() int {
return xxx_messageInfo_Vulnerability.Size(m)
}
func (m *Vulnerability) XXX_DiscardUnknown() {
xxx_messageInfo_Vulnerability.DiscardUnknown(m)
}
var xxx_messageInfo_Vulnerability proto.InternalMessageInfo
func (m *Vulnerability) GetVulnerabilityId() string {
if m != nil {
return m.VulnerabilityId
}
return ""
}
func (m *Vulnerability) GetPkgName() string {
if m != nil {
return m.PkgName
}
return ""
}
func (m *Vulnerability) GetInstalledVersion() string {
if m != nil {
return m.InstalledVersion
}
return ""
}
func (m *Vulnerability) GetFixedVersion() string {
if m != nil {
return m.FixedVersion
}
return ""
}
func (m *Vulnerability) GetTitle() string {
if m != nil {
return m.Title
}
return ""
}
func (m *Vulnerability) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *Vulnerability) GetSeverity() Severity {
if m != nil {
return m.Severity
}
return Severity_UNKNOWN
}
func (m *Vulnerability) GetReferences() []string {
if m != nil {
return m.References
}
return nil
}
func init() {
proto.RegisterEnum("trivy.detector.Severity", Severity_name, Severity_value)
proto.RegisterType((*OSDetectRequest)(nil), "trivy.detector.OSDetectRequest")
proto.RegisterType((*DetectResponse)(nil), "trivy.detector.DetectResponse")
proto.RegisterType((*Package)(nil), "trivy.detector.Package")
proto.RegisterType((*LibDetectRequest)(nil), "trivy.detector.LibDetectRequest")
proto.RegisterType((*Library)(nil), "trivy.detector.Library")
proto.RegisterType((*Vulnerability)(nil), "trivy.detector.Vulnerability")
}
func init() { proto.RegisterFile("rpc/detector/service.proto", fileDescriptor_93e16dbd737b8924) }
var fileDescriptor_93e16dbd737b8924 = []byte{
// 693 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xc5, 0xf9, 0xb2, 0x33, 0xe9, 0x87, 0x59, 0x55, 0xaa, 0x49, 0xd5, 0x36, 0x0a, 0x97, 0x02,
0x52, 0x22, 0xa5, 0x45, 0x9c, 0xa1, 0x2d, 0x6d, 0x20, 0x4d, 0x2b, 0xf7, 0x4b, 0x70, 0x89, 0x36,
0xce, 0x24, 0x59, 0xd5, 0xc9, 0x9a, 0xdd, 0x6d, 0x44, 0x7e, 0x18, 0x27, 0x7e, 0x05, 0x3f, 0x86,
0x3b, 0xf2, 0xae, 0x9d, 0x26, 0xa1, 0x42, 0xf4, 0xb6, 0x33, 0xef, 0xed, 0xec, 0xbc, 0x99, 0x67,
0x43, 0x59, 0x44, 0x41, 0xbd, 0x87, 0x0a, 0x03, 0xc5, 0x45, 0x5d, 0xa2, 0x98, 0xb0, 0x00, 0x6b,
0x91, 0xe0, 0x8a, 0x93, 0x35, 0x25, 0xd8, 0x64, 0x5a, 0x4b, 0xd1, 0xf2, 0xee, 0x80, 0xf3, 0x41,
0x88, 0x75, 0x8d, 0x76, 0xef, 0xfb, 0x75, 0xc5, 0x46, 0x28, 0x15, 0x1d, 0x45, 0xe6, 0x42, 0xf5,
0x97, 0x05, 0xeb, 0xe7, 0x97, 0x47, 0x9a, 0xef, 0xe3, 0xb7, 0x7b, 0x94, 0x8a, 0x6c, 0x41, 0x91,
0xcb, 0x4e, 0x9f, 0x8e, 0x58, 0x38, 0xf5, 0xac, 0x8a, 0xb5, 0x57, 0xf4, 0x1d, 0x2e, 0x3f, 0xea,
0x98, 0x6c, 0x82, 0xcd, 0x65, 0x67, 0x4c, 0x47, 0xe8, 0x65, 0x34, 0x54, 0xe0, 0xb2, 0x4d, 0x47,
0x48, 0xf6, 0xc1, 0x89, 0x68, 0x70, 0x47, 0x07, 0x28, 0xbd, 0x6c, 0x25, 0xbb, 0x57, 0x6a, 0x6c,
0xd6, 0x16, 0xbb, 0xa9, 0x5d, 0x18, 0xdc, 0x9f, 0x11, 0xc9, 0x36, 0x00, 0x1b, 0xd1, 0x01, 0x9a,
0x82, 0x39, 0x5d, 0xb0, 0xa8, 0x33, 0xba, 0xe6, 0x01, 0xd8, 0x81, 0x40, 0xaa, 0xb0, 0xe7, 0xe5,
0x2b, 0xd6, 0x5e, 0xa9, 0x51, 0xae, 0x19, 0x41, 0xb5, 0x54, 0x50, 0xed, 0x2a, 0x15, 0xe4, 0xa7,
0xd4, 0xea, 0x08, 0xd6, 0x52, 0x41, 0x32, 0xe2, 0x63, 0x89, 0xe4, 0x04, 0xd6, 0x27, 0xf7, 0xe1,
0x18, 0x05, 0xed, 0xb2, 0x90, 0x29, 0x86, 0xd2, 0xb3, 0x74, 0x8b, 0xdb, 0xcb, 0x2d, 0xde, 0xcc,
0xd1, 0xa6, 0xfe, 0xf2, 0x2d, 0x42, 0x20, 0x87, 0x5c, 0x86, 0x5a, 0xba, 0xe3, 0xeb, 0x73, 0xf5,
0xb7, 0x05, 0x76, 0xa2, 0x2c, 0xc6, 0xb5, 0x12, 0x33, 0x35, 0x7d, 0x26, 0x1e, 0xd8, 0x13, 0x14,
0x92, 0xf1, 0x71, 0x32, 0xb1, 0x34, 0x8c, 0x11, 0x81, 0x21, 0x52, 0x89, 0x5e, 0xd6, 0x20, 0x49,
0x48, 0x36, 0x20, 0x8f, 0x11, 0x0f, 0x86, 0x7a, 0x24, 0x79, 0xdf, 0x04, 0x71, 0x75, 0x2a, 0x82,
0xa1, 0x9e, 0x45, 0xd1, 0xd7, 0x67, 0xf2, 0x02, 0x1c, 0x29, 0x02, 0x33, 0xbf, 0x82, 0x29, 0x22,
0x45, 0xa0, 0xa7, 0xb7, 0x0b, 0xa5, 0x18, 0x4a, 0x1f, 0xb7, 0x35, 0x0a, 0x52, 0x04, 0x37, 0xc9,
0xfb, 0x09, 0x21, 0xed, 0xc1, 0x99, 0x11, 0xfc, 0xa4, 0x8d, 0x2d, 0x28, 0xc6, 0x04, 0xd3, 0x4a,
0x51, 0xb7, 0x12, 0xbf, 0x76, 0x1c, 0xc7, 0xd5, 0x9f, 0x16, 0xb8, 0x2d, 0xd6, 0xfd, 0xcb, 0x3b,
0x7d, 0x16, 0x62, 0x27, 0xa2, 0x6a, 0x98, 0x7a, 0x27, 0x4e, 0x5c, 0x50, 0x35, 0x24, 0x6f, 0xa1,
0x18, 0xb2, 0xae, 0xa0, 0x22, 0x5e, 0x40, 0xe6, 0x71, 0x8f, 0xb4, 0x34, 0x61, 0xea, 0x3f, 0x30,
0x97, 0x4c, 0x92, 0xfd, 0x87, 0x49, 0x72, 0xff, 0x6f, 0x92, 0x77, 0x60, 0x27, 0x4f, 0x3d, 0x6d,
0x69, 0xd5, 0x1f, 0x19, 0x58, 0x5d, 0x70, 0x09, 0x79, 0x05, 0xee, 0xbc, 0x4f, 0xa6, 0x1d, 0xd6,
0x4b, 0x6a, 0x2d, 0xf8, 0x67, 0xda, 0xec, 0xc5, 0xdb, 0x8a, 0xee, 0x06, 0xf3, 0x9f, 0x8f, 0x1d,
0xdd, 0x0d, 0xb4, 0x8c, 0x37, 0xf0, 0x9c, 0x8d, 0xa5, 0xa2, 0x61, 0x88, 0xbd, 0xd9, 0xce, 0x8c,
0x58, 0x77, 0x06, 0xa4, 0x9b, 0x7b, 0x09, 0xab, 0x7d, 0xf6, 0x7d, 0x8e, 0x68, 0x3e, 0x9d, 0x15,
0x9d, 0x4c, 0x49, 0x1b, 0x90, 0x57, 0x4c, 0x85, 0x98, 0xf8, 0xc5, 0x04, 0xa4, 0x02, 0xa5, 0x1e,
0xca, 0x40, 0xb0, 0x48, 0xc5, 0x17, 0x8d, 0x67, 0xe6, 0x53, 0xe4, 0x00, 0x1c, 0x89, 0x13, 0x14,
0x4c, 0x4d, 0xb5, 0x69, 0xd6, 0x1a, 0xde, 0xf2, 0x96, 0x2e, 0x13, 0xdc, 0x9f, 0x31, 0xc9, 0x0e,
0x80, 0xc0, 0x3e, 0x0a, 0x1c, 0x07, 0x28, 0x3d, 0xa7, 0x92, 0x8d, 0xbd, 0xf4, 0x90, 0x79, 0x7d,
0x04, 0x4e, 0x7a, 0x8b, 0x94, 0xc0, 0xbe, 0x6e, 0x7f, 0x6e, 0x9f, 0xdf, 0xb6, 0xdd, 0x67, 0xc4,
0x86, 0x6c, 0xeb, 0xfc, 0xd6, 0xb5, 0x08, 0x40, 0xe1, 0xec, 0xf8, 0xa8, 0x79, 0x7d, 0xe6, 0x66,
0x88, 0x03, 0xb9, 0xd3, 0xe6, 0xc9, 0xa9, 0x9b, 0x25, 0x2b, 0xe0, 0x1c, 0xfa, 0xcd, 0xab, 0xe6,
0xe1, 0xfb, 0x96, 0x9b, 0x6b, 0xdc, 0x02, 0xa4, 0xbf, 0x2b, 0x2e, 0x48, 0x13, 0x0a, 0xe6, 0x4c,
0x76, 0x97, 0x3b, 0x5c, 0xfa, 0xa9, 0x95, 0x77, 0x96, 0x09, 0x8b, 0xbf, 0x88, 0xc6, 0x17, 0x28,
0xcd, 0xcc, 0xcc, 0x05, 0xf9, 0x34, 0xab, 0x5c, 0x79, 0xc4, 0xa1, 0x4f, 0x2a, 0xfd, 0x01, 0xbe,
0x3a, 0x29, 0xd4, 0x2d, 0x68, 0x4f, 0xee, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x65, 0x1f, 0xdb,
0x8d, 0xc5, 0x05, 0x00, 0x00,
// 422 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xd1, 0x8a, 0xd3, 0x40,
0x14, 0x86, 0xc9, 0xb6, 0x76, 0xdb, 0x53, 0xd8, 0x95, 0x01, 0x31, 0x64, 0xd1, 0x2d, 0xbd, 0xea,
0xd5, 0x04, 0xbb, 0xe2, 0x03, 0x88, 0x0a, 0xca, 0xa2, 0x4b, 0x14, 0x45, 0x6f, 0xca, 0x64, 0xf6,
0x34, 0x1d, 0x36, 0xd3, 0x93, 0xce, 0x4c, 0x0a, 0x79, 0x34, 0x9f, 0xc2, 0x57, 0x92, 0x4e, 0x98,
0x6a, 0xa2, 0xe8, 0xde, 0x4d, 0xe6, 0xff, 0xcf, 0x7f, 0xce, 0xf9, 0x32, 0x90, 0x98, 0x4a, 0xa6,
0xb7, 0xe8, 0x50, 0x3a, 0x32, 0xa9, 0x45, 0xb3, 0x57, 0x12, 0x79, 0x65, 0xc8, 0x11, 0x3b, 0x73,
0x46, 0xed, 0x1b, 0x1e, 0xd4, 0xe4, 0xb2, 0x20, 0x2a, 0x4a, 0x4c, 0xbd, 0x9a, 0xd7, 0xeb, 0xd4,
0x29, 0x8d, 0xd6, 0x09, 0x5d, 0xb5, 0x05, 0xc9, 0x8b, 0x42, 0xb9, 0x4d, 0x9d, 0x73, 0x49, 0x3a,
0x15, 0xbb, 0x5a, 0x58, 0x94, 0xb5, 0x51, 0xae, 0x49, 0x7d, 0x50, 0x7a, 0x68, 0x25, 0x49, 0x6b,
0xda, 0x76, 0x1b, 0xcd, 0x7f, 0x44, 0x70, 0xfe, 0xe1, 0xe3, 0x2b, 0xdf, 0x27, 0xc3, 0x5d, 0x8d,
0xd6, 0xb1, 0x0b, 0x98, 0x90, 0x5d, 0xad, 0x85, 0x56, 0x65, 0x13, 0x47, 0xb3, 0x68, 0x31, 0xc9,
0xc6, 0x64, 0xdf, 0xf8, 0x6f, 0xf6, 0x18, 0x4e, 0xc9, 0xae, 0xb6, 0x42, 0x63, 0x7c, 0xe2, 0xa5,
0x11, 0xd9, 0xf7, 0x42, 0x23, 0x7b, 0x06, 0xe3, 0x4a, 0xc8, 0x3b, 0x51, 0xa0, 0x8d, 0x07, 0xb3,
0xc1, 0x62, 0xba, 0x7c, 0xc4, 0xdb, 0x2d, 0xda, 0xc6, 0xfc, 0xa6, 0x55, 0xb3, 0xa3, 0x8d, 0x3d,
0x01, 0x50, 0x5a, 0x14, 0xd8, 0xc6, 0x0d, 0x7d, 0xdc, 0xc4, 0xdf, 0xf8, 0xc4, 0xe7, 0x70, 0x2a,
0x0d, 0x0a, 0x87, 0xb7, 0xf1, 0x83, 0x59, 0xb4, 0x98, 0x2e, 0x13, 0xde, 0x62, 0xe0, 0x01, 0x03,
0xff, 0x14, 0x30, 0x64, 0xc1, 0x3a, 0xbf, 0x83, 0xb3, 0xb0, 0x8e, 0xad, 0x68, 0x6b, 0x91, 0xbd,
0x86, 0xf3, 0x7d, 0x5d, 0x6e, 0xd1, 0x88, 0x5c, 0x95, 0xca, 0x29, 0xb4, 0x71, 0xe4, 0x07, 0xbc,
0xe8, 0x0e, 0xf8, 0xf9, 0x37, 0x53, 0x93, 0xf5, 0x6b, 0x18, 0x83, 0x21, 0x92, 0x2d, 0xfd, 0xda,
0xe3, 0xcc, 0x9f, 0xe7, 0xdf, 0x23, 0x78, 0x78, 0xad, 0xf2, 0x3f, 0xf8, 0xad, 0x55, 0x89, 0xab,
0x4a, 0xb8, 0x4d, 0xe0, 0x77, 0xb8, 0xb8, 0x11, 0x6e, 0xc3, 0xae, 0x60, 0x52, 0xaa, 0xdc, 0x08,
0x73, 0x18, 0xe3, 0xe4, 0x6f, 0x9c, 0xae, 0xbd, 0xdc, 0x64, 0xbf, 0x7c, 0x3d, 0x50, 0x83, 0x7f,
0x80, 0x1a, 0xde, 0x1b, 0xd4, 0xf2, 0x0b, 0x40, 0xf8, 0xf3, 0x64, 0xd8, 0x5b, 0x18, 0xb5, 0x67,
0x76, 0xc9, 0xbb, 0x8f, 0x8f, 0xf7, 0xde, 0x47, 0xf2, 0xb4, 0x6f, 0xe8, 0xf2, 0x5e, 0x7e, 0x85,
0xe9, 0x91, 0x09, 0x19, 0xf6, 0xee, 0x98, 0x3c, 0xeb, 0x17, 0xf6, 0xd1, 0xfd, 0x2f, 0xfa, 0x25,
0x7c, 0x1b, 0x07, 0x29, 0x1f, 0xf9, 0xe5, 0xae, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x60, 0x68,
0xcc, 0x2b, 0x48, 0x03, 0x00, 0x00,
}

View File

@@ -1,3 +1,4 @@
// for backward compatibility
syntax = "proto3";
import "google/protobuf/timestamp.proto";
@@ -5,37 +6,23 @@ import "google/protobuf/timestamp.proto";
package trivy.detector;
option go_package = "detector";
import "github.com/aquasecurity/trivy/rpc/common/service.proto";
service OSDetector {
rpc Detect(OSDetectRequest) returns (DetectResponse);
}
message OSDetectRequest {
string os_family = 1;
string os_name = 2;
repeated Package packages = 3;
string os_family = 1;
string os_name = 2;
repeated common.Package packages = 3;
string image_name = 4;
google.protobuf.Timestamp created = 5;
}
message DetectResponse {
repeated Vulnerability vulnerabilities = 1;
bool eosl = 2;
}
message Package {
// binary package
// e.g. bind-utils
string name = 1;
string version = 2;
string release = 3;
int32 epoch = 4;
string arch = 5;
// src package containing some binary packages
// e.g. bind
string src_name = 6;
string src_version = 7;
string src_release = 8;
int32 src_epoch = 9;
repeated common.Vulnerability vulnerabilities = 1;
bool eosl = 2;
}
service LibDetector {
@@ -43,32 +30,8 @@ service LibDetector {
}
message LibDetectRequest {
string file_path = 1;
repeated Library libraries = 2;
string file_path = 1;
repeated common.Library libraries = 2;
string image_name = 3;
google.protobuf.Timestamp created = 4;
}
message Library {
string name = 1;
string version = 2;
}
message Vulnerability {
string vulnerability_id = 1;
string pkg_name = 2;
string installed_version = 3;
string fixed_version = 4;
string title = 5;
string description = 6;
Severity severity = 7;
repeated string references = 8;
}
enum Severity {
UNKNOWN = 0;
LOW = 1;
MEDIUM = 2;
HIGH = 3;
CRITICAL = 4;
}

View File

@@ -1,9 +1,9 @@
// Code generated by protoc-gen-twirp v5.9.0, DO NOT EDIT.
// Code generated by protoc-gen-twirp v5.10.1, DO NOT EDIT.
// source: rpc/detector/service.proto
/*
Package detector is a generated twirp stub package.
This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.9.0.
This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.10.1.
It is generated from these files:
rpc/detector/service.proto
@@ -43,24 +43,30 @@ type OSDetector interface {
type oSDetectorProtobufClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewOSDetectorProtobufClient creates a Protobuf client that implements the OSDetector interface.
// It communicates using Protobuf and can be configured with a custom HTTPClient.
func NewOSDetectorProtobufClient(addr string, client HTTPClient) OSDetector {
func NewOSDetectorProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) OSDetector {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + OSDetectorPathPrefix
urls := [1]string{
prefix + "Detect",
}
if httpClient, ok := client.(*http.Client); ok {
return &oSDetectorProtobufClient{
client: withoutRedirects(httpClient),
urls: urls,
}
}
return &oSDetectorProtobufClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
@@ -69,10 +75,18 @@ func (c *oSDetectorProtobufClient) Detect(ctx context.Context, in *OSDetectReque
ctx = ctxsetters.WithServiceName(ctx, "OSDetector")
ctx = ctxsetters.WithMethodName(ctx, "Detect")
out := new(DetectResponse)
err := doProtobufRequest(ctx, c.client, c.urls[0], in, out)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
@@ -83,24 +97,30 @@ func (c *oSDetectorProtobufClient) Detect(ctx context.Context, in *OSDetectReque
type oSDetectorJSONClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewOSDetectorJSONClient creates a JSON client that implements the OSDetector interface.
// It communicates using JSON and can be configured with a custom HTTPClient.
func NewOSDetectorJSONClient(addr string, client HTTPClient) OSDetector {
func NewOSDetectorJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) OSDetector {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + OSDetectorPathPrefix
urls := [1]string{
prefix + "Detect",
}
if httpClient, ok := client.(*http.Client); ok {
return &oSDetectorJSONClient{
client: withoutRedirects(httpClient),
urls: urls,
}
}
return &oSDetectorJSONClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
@@ -109,10 +129,18 @@ func (c *oSDetectorJSONClient) Detect(ctx context.Context, in *OSDetectRequest)
ctx = ctxsetters.WithServiceName(ctx, "OSDetector")
ctx = ctxsetters.WithMethodName(ctx, "Detect")
out := new(DetectResponse)
err := doJSONRequest(ctx, c.client, c.urls[0], in, out)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
@@ -309,7 +337,7 @@ func (s *oSDetectorServer) ServiceDescriptor() ([]byte, int) {
}
func (s *oSDetectorServer) ProtocGenTwirpVersion() string {
return "v5.9.0"
return "v5.10.1"
}
func (s *oSDetectorServer) PathPrefix() string {
@@ -331,24 +359,30 @@ type LibDetector interface {
type libDetectorProtobufClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewLibDetectorProtobufClient creates a Protobuf client that implements the LibDetector interface.
// It communicates using Protobuf and can be configured with a custom HTTPClient.
func NewLibDetectorProtobufClient(addr string, client HTTPClient) LibDetector {
func NewLibDetectorProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) LibDetector {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + LibDetectorPathPrefix
urls := [1]string{
prefix + "Detect",
}
if httpClient, ok := client.(*http.Client); ok {
return &libDetectorProtobufClient{
client: withoutRedirects(httpClient),
urls: urls,
}
}
return &libDetectorProtobufClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
@@ -357,10 +391,18 @@ func (c *libDetectorProtobufClient) Detect(ctx context.Context, in *LibDetectReq
ctx = ctxsetters.WithServiceName(ctx, "LibDetector")
ctx = ctxsetters.WithMethodName(ctx, "Detect")
out := new(DetectResponse)
err := doProtobufRequest(ctx, c.client, c.urls[0], in, out)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
@@ -371,24 +413,30 @@ func (c *libDetectorProtobufClient) Detect(ctx context.Context, in *LibDetectReq
type libDetectorJSONClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewLibDetectorJSONClient creates a JSON client that implements the LibDetector interface.
// It communicates using JSON and can be configured with a custom HTTPClient.
func NewLibDetectorJSONClient(addr string, client HTTPClient) LibDetector {
func NewLibDetectorJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) LibDetector {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + LibDetectorPathPrefix
urls := [1]string{
prefix + "Detect",
}
if httpClient, ok := client.(*http.Client); ok {
return &libDetectorJSONClient{
client: withoutRedirects(httpClient),
urls: urls,
}
}
return &libDetectorJSONClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
@@ -397,10 +445,18 @@ func (c *libDetectorJSONClient) Detect(ctx context.Context, in *LibDetectRequest
ctx = ctxsetters.WithServiceName(ctx, "LibDetector")
ctx = ctxsetters.WithMethodName(ctx, "Detect")
out := new(DetectResponse)
err := doJSONRequest(ctx, c.client, c.urls[0], in, out)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
@@ -597,7 +653,7 @@ func (s *libDetectorServer) ServiceDescriptor() ([]byte, int) {
}
func (s *libDetectorServer) ProtocGenTwirpVersion() string {
return "v5.9.0"
return "v5.10.1"
}
func (s *libDetectorServer) PathPrefix() string {
@@ -738,7 +794,7 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Twirp-Version", "v5.9.0")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -950,7 +1006,7 @@ func withoutRedirects(in *http.Client) *http.Client {
}
// doProtobufRequest makes a Protobuf request to the remote Twirp service.
func doProtobufRequest(ctx context.Context, client HTTPClient, url string, in, out proto.Message) (err error) {
func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
reqBodyBytes, err := proto.Marshal(in)
if err != nil {
return wrapInternal(err, "failed to marshal proto request")
@@ -964,6 +1020,12 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, url string, in, o
if err != nil {
return wrapInternal(err, "could not build request")
}
ctx, err = callClientRequestPrepared(ctx, hooks, req)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
return wrapInternal(err, "failed to do request")
@@ -999,7 +1061,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, url string, in, o
}
// doJSONRequest makes a JSON request to the remote Twirp service.
func doJSONRequest(ctx context.Context, client HTTPClient, url string, in, out proto.Message) (err error) {
func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
reqBody := bytes.NewBuffer(nil)
marshaler := &jsonpb.Marshaler{OrigName: true}
if err = marshaler.Marshal(reqBody, in); err != nil {
@@ -1013,6 +1075,12 @@ func doJSONRequest(ctx context.Context, client HTTPClient, url string, in, out p
if err != nil {
return wrapInternal(err, "could not build request")
}
ctx, err = callClientRequestPrepared(ctx, hooks, req)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
return wrapInternal(err, "failed to do request")
@@ -1083,50 +1151,54 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
var twirpFileDescriptor0 = []byte{
// 693 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0xc5, 0xf9, 0xb2, 0x33, 0xe9, 0x87, 0x59, 0x55, 0xaa, 0x49, 0xd5, 0x36, 0x0a, 0x97, 0x02,
0x52, 0x22, 0xa5, 0x45, 0x9c, 0xa1, 0x2d, 0x6d, 0x20, 0x4d, 0x2b, 0xf7, 0x4b, 0x70, 0x89, 0x36,
0xce, 0x24, 0x59, 0xd5, 0xc9, 0x9a, 0xdd, 0x6d, 0x44, 0x7e, 0x18, 0x27, 0x7e, 0x05, 0x3f, 0x86,
0x3b, 0xf2, 0xae, 0x9d, 0x26, 0xa1, 0x42, 0xf4, 0xb6, 0x33, 0xef, 0xed, 0xec, 0xbc, 0x99, 0x67,
0x43, 0x59, 0x44, 0x41, 0xbd, 0x87, 0x0a, 0x03, 0xc5, 0x45, 0x5d, 0xa2, 0x98, 0xb0, 0x00, 0x6b,
0x91, 0xe0, 0x8a, 0x93, 0x35, 0x25, 0xd8, 0x64, 0x5a, 0x4b, 0xd1, 0xf2, 0xee, 0x80, 0xf3, 0x41,
0x88, 0x75, 0x8d, 0x76, 0xef, 0xfb, 0x75, 0xc5, 0x46, 0x28, 0x15, 0x1d, 0x45, 0xe6, 0x42, 0xf5,
0x97, 0x05, 0xeb, 0xe7, 0x97, 0x47, 0x9a, 0xef, 0xe3, 0xb7, 0x7b, 0x94, 0x8a, 0x6c, 0x41, 0x91,
0xcb, 0x4e, 0x9f, 0x8e, 0x58, 0x38, 0xf5, 0xac, 0x8a, 0xb5, 0x57, 0xf4, 0x1d, 0x2e, 0x3f, 0xea,
0x98, 0x6c, 0x82, 0xcd, 0x65, 0x67, 0x4c, 0x47, 0xe8, 0x65, 0x34, 0x54, 0xe0, 0xb2, 0x4d, 0x47,
0x48, 0xf6, 0xc1, 0x89, 0x68, 0x70, 0x47, 0x07, 0x28, 0xbd, 0x6c, 0x25, 0xbb, 0x57, 0x6a, 0x6c,
0xd6, 0x16, 0xbb, 0xa9, 0x5d, 0x18, 0xdc, 0x9f, 0x11, 0xc9, 0x36, 0x00, 0x1b, 0xd1, 0x01, 0x9a,
0x82, 0x39, 0x5d, 0xb0, 0xa8, 0x33, 0xba, 0xe6, 0x01, 0xd8, 0x81, 0x40, 0xaa, 0xb0, 0xe7, 0xe5,
0x2b, 0xd6, 0x5e, 0xa9, 0x51, 0xae, 0x19, 0x41, 0xb5, 0x54, 0x50, 0xed, 0x2a, 0x15, 0xe4, 0xa7,
0xd4, 0xea, 0x08, 0xd6, 0x52, 0x41, 0x32, 0xe2, 0x63, 0x89, 0xe4, 0x04, 0xd6, 0x27, 0xf7, 0xe1,
0x18, 0x05, 0xed, 0xb2, 0x90, 0x29, 0x86, 0xd2, 0xb3, 0x74, 0x8b, 0xdb, 0xcb, 0x2d, 0xde, 0xcc,
0xd1, 0xa6, 0xfe, 0xf2, 0x2d, 0x42, 0x20, 0x87, 0x5c, 0x86, 0x5a, 0xba, 0xe3, 0xeb, 0x73, 0xf5,
0xb7, 0x05, 0x76, 0xa2, 0x2c, 0xc6, 0xb5, 0x12, 0x33, 0x35, 0x7d, 0x26, 0x1e, 0xd8, 0x13, 0x14,
0x92, 0xf1, 0x71, 0x32, 0xb1, 0x34, 0x8c, 0x11, 0x81, 0x21, 0x52, 0x89, 0x5e, 0xd6, 0x20, 0x49,
0x48, 0x36, 0x20, 0x8f, 0x11, 0x0f, 0x86, 0x7a, 0x24, 0x79, 0xdf, 0x04, 0x71, 0x75, 0x2a, 0x82,
0xa1, 0x9e, 0x45, 0xd1, 0xd7, 0x67, 0xf2, 0x02, 0x1c, 0x29, 0x02, 0x33, 0xbf, 0x82, 0x29, 0x22,
0x45, 0xa0, 0xa7, 0xb7, 0x0b, 0xa5, 0x18, 0x4a, 0x1f, 0xb7, 0x35, 0x0a, 0x52, 0x04, 0x37, 0xc9,
0xfb, 0x09, 0x21, 0xed, 0xc1, 0x99, 0x11, 0xfc, 0xa4, 0x8d, 0x2d, 0x28, 0xc6, 0x04, 0xd3, 0x4a,
0x51, 0xb7, 0x12, 0xbf, 0x76, 0x1c, 0xc7, 0xd5, 0x9f, 0x16, 0xb8, 0x2d, 0xd6, 0xfd, 0xcb, 0x3b,
0x7d, 0x16, 0x62, 0x27, 0xa2, 0x6a, 0x98, 0x7a, 0x27, 0x4e, 0x5c, 0x50, 0x35, 0x24, 0x6f, 0xa1,
0x18, 0xb2, 0xae, 0xa0, 0x22, 0x5e, 0x40, 0xe6, 0x71, 0x8f, 0xb4, 0x34, 0x61, 0xea, 0x3f, 0x30,
0x97, 0x4c, 0x92, 0xfd, 0x87, 0x49, 0x72, 0xff, 0x6f, 0x92, 0x77, 0x60, 0x27, 0x4f, 0x3d, 0x6d,
0x69, 0xd5, 0x1f, 0x19, 0x58, 0x5d, 0x70, 0x09, 0x79, 0x05, 0xee, 0xbc, 0x4f, 0xa6, 0x1d, 0xd6,
0x4b, 0x6a, 0x2d, 0xf8, 0x67, 0xda, 0xec, 0xc5, 0xdb, 0x8a, 0xee, 0x06, 0xf3, 0x9f, 0x8f, 0x1d,
0xdd, 0x0d, 0xb4, 0x8c, 0x37, 0xf0, 0x9c, 0x8d, 0xa5, 0xa2, 0x61, 0x88, 0xbd, 0xd9, 0xce, 0x8c,
0x58, 0x77, 0x06, 0xa4, 0x9b, 0x7b, 0x09, 0xab, 0x7d, 0xf6, 0x7d, 0x8e, 0x68, 0x3e, 0x9d, 0x15,
0x9d, 0x4c, 0x49, 0x1b, 0x90, 0x57, 0x4c, 0x85, 0x98, 0xf8, 0xc5, 0x04, 0xa4, 0x02, 0xa5, 0x1e,
0xca, 0x40, 0xb0, 0x48, 0xc5, 0x17, 0x8d, 0x67, 0xe6, 0x53, 0xe4, 0x00, 0x1c, 0x89, 0x13, 0x14,
0x4c, 0x4d, 0xb5, 0x69, 0xd6, 0x1a, 0xde, 0xf2, 0x96, 0x2e, 0x13, 0xdc, 0x9f, 0x31, 0xc9, 0x0e,
0x80, 0xc0, 0x3e, 0x0a, 0x1c, 0x07, 0x28, 0x3d, 0xa7, 0x92, 0x8d, 0xbd, 0xf4, 0x90, 0x79, 0x7d,
0x04, 0x4e, 0x7a, 0x8b, 0x94, 0xc0, 0xbe, 0x6e, 0x7f, 0x6e, 0x9f, 0xdf, 0xb6, 0xdd, 0x67, 0xc4,
0x86, 0x6c, 0xeb, 0xfc, 0xd6, 0xb5, 0x08, 0x40, 0xe1, 0xec, 0xf8, 0xa8, 0x79, 0x7d, 0xe6, 0x66,
0x88, 0x03, 0xb9, 0xd3, 0xe6, 0xc9, 0xa9, 0x9b, 0x25, 0x2b, 0xe0, 0x1c, 0xfa, 0xcd, 0xab, 0xe6,
0xe1, 0xfb, 0x96, 0x9b, 0x6b, 0xdc, 0x02, 0xa4, 0xbf, 0x2b, 0x2e, 0x48, 0x13, 0x0a, 0xe6, 0x4c,
0x76, 0x97, 0x3b, 0x5c, 0xfa, 0xa9, 0x95, 0x77, 0x96, 0x09, 0x8b, 0xbf, 0x88, 0xc6, 0x17, 0x28,
0xcd, 0xcc, 0xcc, 0x05, 0xf9, 0x34, 0xab, 0x5c, 0x79, 0xc4, 0xa1, 0x4f, 0x2a, 0xfd, 0x01, 0xbe,
0x3a, 0x29, 0xd4, 0x2d, 0x68, 0x4f, 0xee, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x65, 0x1f, 0xdb,
0x8d, 0xc5, 0x05, 0x00, 0x00,
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return
}
h.ResponseReceived(ctx)
}
func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) {
if h == nil || h.RequestPrepared == nil {
return ctx, nil
}
return h.RequestPrepared(ctx, req)
}
func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) {
if h == nil || h.Error == nil {
return
}
h.Error(ctx, err)
}
var twirpFileDescriptor0 = []byte{
// 422 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xd1, 0x8a, 0xd3, 0x40,
0x14, 0x86, 0xc9, 0xb6, 0x76, 0xdb, 0x53, 0xd8, 0x95, 0x01, 0x31, 0x64, 0xd1, 0x2d, 0xbd, 0xea,
0xd5, 0x04, 0xbb, 0xe2, 0x03, 0x88, 0x0a, 0xca, 0xa2, 0x4b, 0x14, 0x45, 0x6f, 0xca, 0x64, 0xf6,
0x34, 0x1d, 0x36, 0xd3, 0x93, 0xce, 0x4c, 0x0a, 0x79, 0x34, 0x9f, 0xc2, 0x57, 0x92, 0x4e, 0x98,
0x6a, 0xa2, 0xe8, 0xde, 0x4d, 0xe6, 0xff, 0xcf, 0x7f, 0xce, 0xf9, 0x32, 0x90, 0x98, 0x4a, 0xa6,
0xb7, 0xe8, 0x50, 0x3a, 0x32, 0xa9, 0x45, 0xb3, 0x57, 0x12, 0x79, 0x65, 0xc8, 0x11, 0x3b, 0x73,
0x46, 0xed, 0x1b, 0x1e, 0xd4, 0xe4, 0xb2, 0x20, 0x2a, 0x4a, 0x4c, 0xbd, 0x9a, 0xd7, 0xeb, 0xd4,
0x29, 0x8d, 0xd6, 0x09, 0x5d, 0xb5, 0x05, 0xc9, 0x8b, 0x42, 0xb9, 0x4d, 0x9d, 0x73, 0x49, 0x3a,
0x15, 0xbb, 0x5a, 0x58, 0x94, 0xb5, 0x51, 0xae, 0x49, 0x7d, 0x50, 0x7a, 0x68, 0x25, 0x49, 0x6b,
0xda, 0x76, 0x1b, 0xcd, 0x7f, 0x44, 0x70, 0xfe, 0xe1, 0xe3, 0x2b, 0xdf, 0x27, 0xc3, 0x5d, 0x8d,
0xd6, 0xb1, 0x0b, 0x98, 0x90, 0x5d, 0xad, 0x85, 0x56, 0x65, 0x13, 0x47, 0xb3, 0x68, 0x31, 0xc9,
0xc6, 0x64, 0xdf, 0xf8, 0x6f, 0xf6, 0x18, 0x4e, 0xc9, 0xae, 0xb6, 0x42, 0x63, 0x7c, 0xe2, 0xa5,
0x11, 0xd9, 0xf7, 0x42, 0x23, 0x7b, 0x06, 0xe3, 0x4a, 0xc8, 0x3b, 0x51, 0xa0, 0x8d, 0x07, 0xb3,
0xc1, 0x62, 0xba, 0x7c, 0xc4, 0xdb, 0x2d, 0xda, 0xc6, 0xfc, 0xa6, 0x55, 0xb3, 0xa3, 0x8d, 0x3d,
0x01, 0x50, 0x5a, 0x14, 0xd8, 0xc6, 0x0d, 0x7d, 0xdc, 0xc4, 0xdf, 0xf8, 0xc4, 0xe7, 0x70, 0x2a,
0x0d, 0x0a, 0x87, 0xb7, 0xf1, 0x83, 0x59, 0xb4, 0x98, 0x2e, 0x13, 0xde, 0x62, 0xe0, 0x01, 0x03,
0xff, 0x14, 0x30, 0x64, 0xc1, 0x3a, 0xbf, 0x83, 0xb3, 0xb0, 0x8e, 0xad, 0x68, 0x6b, 0x91, 0xbd,
0x86, 0xf3, 0x7d, 0x5d, 0x6e, 0xd1, 0x88, 0x5c, 0x95, 0xca, 0x29, 0xb4, 0x71, 0xe4, 0x07, 0xbc,
0xe8, 0x0e, 0xf8, 0xf9, 0x37, 0x53, 0x93, 0xf5, 0x6b, 0x18, 0x83, 0x21, 0x92, 0x2d, 0xfd, 0xda,
0xe3, 0xcc, 0x9f, 0xe7, 0xdf, 0x23, 0x78, 0x78, 0xad, 0xf2, 0x3f, 0xf8, 0xad, 0x55, 0x89, 0xab,
0x4a, 0xb8, 0x4d, 0xe0, 0x77, 0xb8, 0xb8, 0x11, 0x6e, 0xc3, 0xae, 0x60, 0x52, 0xaa, 0xdc, 0x08,
0x73, 0x18, 0xe3, 0xe4, 0x6f, 0x9c, 0xae, 0xbd, 0xdc, 0x64, 0xbf, 0x7c, 0x3d, 0x50, 0x83, 0x7f,
0x80, 0x1a, 0xde, 0x1b, 0xd4, 0xf2, 0x0b, 0x40, 0xf8, 0xf3, 0x64, 0xd8, 0x5b, 0x18, 0xb5, 0x67,
0x76, 0xc9, 0xbb, 0x8f, 0x8f, 0xf7, 0xde, 0x47, 0xf2, 0xb4, 0x6f, 0xe8, 0xf2, 0x5e, 0x7e, 0x85,
0xe9, 0x91, 0x09, 0x19, 0xf6, 0xee, 0x98, 0x3c, 0xeb, 0x17, 0xf6, 0xd1, 0xfd, 0x2f, 0xfa, 0x25,
0x7c, 0x1b, 0x07, 0x29, 0x1f, 0xf9, 0xe5, 0xae, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x60, 0x68,
0xcc, 0x2b, 0x48, 0x03, 0x00, 0x00,
}

263
rpc/scanner/service.pb.go Normal file
View File

@@ -0,0 +1,263 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: rpc/scanner/service.proto
package scanner
import (
fmt "fmt"
common "github.com/aquasecurity/trivy/rpc/common"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ScanRequest struct {
Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"`
ImageId string `protobuf:"bytes,2,opt,name=image_id,json=imageId,proto3" json:"image_id,omitempty"`
LayerIds []string `protobuf:"bytes,3,rep,name=layer_ids,json=layerIds,proto3" json:"layer_ids,omitempty"`
Options *ScanOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ScanRequest) Reset() { *m = ScanRequest{} }
func (m *ScanRequest) String() string { return proto.CompactTextString(m) }
func (*ScanRequest) ProtoMessage() {}
func (*ScanRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_60d0e837512b18d4, []int{0}
}
func (m *ScanRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ScanRequest.Unmarshal(m, b)
}
func (m *ScanRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ScanRequest.Marshal(b, m, deterministic)
}
func (m *ScanRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ScanRequest.Merge(m, src)
}
func (m *ScanRequest) XXX_Size() int {
return xxx_messageInfo_ScanRequest.Size(m)
}
func (m *ScanRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ScanRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ScanRequest proto.InternalMessageInfo
func (m *ScanRequest) GetTarget() string {
if m != nil {
return m.Target
}
return ""
}
func (m *ScanRequest) GetImageId() string {
if m != nil {
return m.ImageId
}
return ""
}
func (m *ScanRequest) GetLayerIds() []string {
if m != nil {
return m.LayerIds
}
return nil
}
func (m *ScanRequest) GetOptions() *ScanOptions {
if m != nil {
return m.Options
}
return nil
}
type ScanOptions struct {
VulnType []string `protobuf:"bytes,1,rep,name=vuln_type,json=vulnType,proto3" json:"vuln_type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ScanOptions) Reset() { *m = ScanOptions{} }
func (m *ScanOptions) String() string { return proto.CompactTextString(m) }
func (*ScanOptions) ProtoMessage() {}
func (*ScanOptions) Descriptor() ([]byte, []int) {
return fileDescriptor_60d0e837512b18d4, []int{1}
}
func (m *ScanOptions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ScanOptions.Unmarshal(m, b)
}
func (m *ScanOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ScanOptions.Marshal(b, m, deterministic)
}
func (m *ScanOptions) XXX_Merge(src proto.Message) {
xxx_messageInfo_ScanOptions.Merge(m, src)
}
func (m *ScanOptions) XXX_Size() int {
return xxx_messageInfo_ScanOptions.Size(m)
}
func (m *ScanOptions) XXX_DiscardUnknown() {
xxx_messageInfo_ScanOptions.DiscardUnknown(m)
}
var xxx_messageInfo_ScanOptions proto.InternalMessageInfo
func (m *ScanOptions) GetVulnType() []string {
if m != nil {
return m.VulnType
}
return nil
}
type ScanResponse struct {
Os *common.OS `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"`
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
Results []*Result `protobuf:"bytes,3,rep,name=results,proto3" json:"results,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ScanResponse) Reset() { *m = ScanResponse{} }
func (m *ScanResponse) String() string { return proto.CompactTextString(m) }
func (*ScanResponse) ProtoMessage() {}
func (*ScanResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_60d0e837512b18d4, []int{2}
}
func (m *ScanResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ScanResponse.Unmarshal(m, b)
}
func (m *ScanResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ScanResponse.Marshal(b, m, deterministic)
}
func (m *ScanResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ScanResponse.Merge(m, src)
}
func (m *ScanResponse) XXX_Size() int {
return xxx_messageInfo_ScanResponse.Size(m)
}
func (m *ScanResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ScanResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ScanResponse proto.InternalMessageInfo
func (m *ScanResponse) GetOs() *common.OS {
if m != nil {
return m.Os
}
return nil
}
func (m *ScanResponse) GetEosl() bool {
if m != nil {
return m.Eosl
}
return false
}
func (m *ScanResponse) GetResults() []*Result {
if m != nil {
return m.Results
}
return nil
}
// Result is the same as github.com/aquasecurity/trivy/pkg/report.Result
type Result struct {
Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"`
Vulnerabilities []*common.Vulnerability `protobuf:"bytes,2,rep,name=vulnerabilities,proto3" json:"vulnerabilities,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Result) Reset() { *m = Result{} }
func (m *Result) String() string { return proto.CompactTextString(m) }
func (*Result) ProtoMessage() {}
func (*Result) Descriptor() ([]byte, []int) {
return fileDescriptor_60d0e837512b18d4, []int{3}
}
func (m *Result) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Result.Unmarshal(m, b)
}
func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Result.Marshal(b, m, deterministic)
}
func (m *Result) XXX_Merge(src proto.Message) {
xxx_messageInfo_Result.Merge(m, src)
}
func (m *Result) XXX_Size() int {
return xxx_messageInfo_Result.Size(m)
}
func (m *Result) XXX_DiscardUnknown() {
xxx_messageInfo_Result.DiscardUnknown(m)
}
var xxx_messageInfo_Result proto.InternalMessageInfo
func (m *Result) GetTarget() string {
if m != nil {
return m.Target
}
return ""
}
func (m *Result) GetVulnerabilities() []*common.Vulnerability {
if m != nil {
return m.Vulnerabilities
}
return nil
}
func init() {
proto.RegisterType((*ScanRequest)(nil), "trivy.scanner.v1.ScanRequest")
proto.RegisterType((*ScanOptions)(nil), "trivy.scanner.v1.ScanOptions")
proto.RegisterType((*ScanResponse)(nil), "trivy.scanner.v1.ScanResponse")
proto.RegisterType((*Result)(nil), "trivy.scanner.v1.Result")
}
func init() { proto.RegisterFile("rpc/scanner/service.proto", fileDescriptor_60d0e837512b18d4) }
var fileDescriptor_60d0e837512b18d4 = []byte{
// 361 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xc1, 0x6b, 0xbb, 0x30,
0x18, 0x45, 0x5b, 0x6a, 0x8d, 0x3f, 0xf8, 0x95, 0x1c, 0x86, 0x6d, 0xd9, 0x10, 0x4f, 0x65, 0x07,
0x65, 0x0e, 0xb6, 0xfb, 0xa0, 0x87, 0x9e, 0x3a, 0xd2, 0xb1, 0xc3, 0x2e, 0x25, 0xd5, 0x0f, 0x17,
0x50, 0x63, 0x93, 0x28, 0xf3, 0x1f, 0xd9, 0xdf, 0x3b, 0x4c, 0x2c, 0xac, 0x1d, 0xbd, 0x25, 0xef,
0x3d, 0xbf, 0xf7, 0xde, 0x67, 0xd0, 0x5c, 0xd4, 0x69, 0x2c, 0x53, 0x5a, 0x55, 0x20, 0x62, 0x09,
0xa2, 0x65, 0x29, 0x44, 0xb5, 0xe0, 0x8a, 0xe3, 0x99, 0x12, 0xac, 0xed, 0xa2, 0x81, 0x8c, 0xda,
0x87, 0xc5, 0x53, 0xce, 0xd4, 0x67, 0x73, 0x88, 0x52, 0x5e, 0xc6, 0xf4, 0xd8, 0x50, 0x09, 0x69,
0x23, 0x98, 0xea, 0x62, 0xad, 0x8c, 0xfb, 0x51, 0x29, 0x2f, 0x4b, 0x5e, 0x9d, 0x4f, 0x0a, 0xbf,
0x2d, 0xe4, 0xed, 0x52, 0x5a, 0x11, 0x38, 0x36, 0x20, 0x15, 0xbe, 0x41, 0x13, 0x45, 0x45, 0x0e,
0xca, 0xb7, 0x02, 0x6b, 0xe5, 0x92, 0xe1, 0x86, 0xe7, 0x68, 0xca, 0x4a, 0x9a, 0xc3, 0x9e, 0x65,
0xbe, 0xad, 0x19, 0x47, 0xdf, 0x37, 0x19, 0x5e, 0x22, 0xb7, 0xa0, 0x1d, 0x88, 0x3d, 0xcb, 0xa4,
0x3f, 0x0a, 0x46, 0x2b, 0x97, 0x4c, 0x35, 0xb0, 0xc9, 0x24, 0x7e, 0x46, 0x0e, 0xaf, 0x15, 0xe3,
0x95, 0xf4, 0xc7, 0x81, 0xb5, 0xf2, 0x92, 0xdb, 0xe8, 0x32, 0x7b, 0xd4, 0xfb, 0x6f, 0x8d, 0x88,
0x9c, 0xd4, 0xe1, 0xbd, 0xc9, 0x35, 0xe0, 0xbd, 0x49, 0xdb, 0x14, 0xd5, 0x5e, 0x75, 0x35, 0xf8,
0x96, 0x31, 0xe9, 0x81, 0xb7, 0xae, 0x86, 0xf0, 0x0b, 0xfd, 0x33, 0x1d, 0x64, 0xcd, 0x2b, 0x09,
0x38, 0x40, 0x36, 0x97, 0xba, 0x80, 0x97, 0xcc, 0x06, 0x3f, 0xd3, 0x3e, 0xda, 0xee, 0x88, 0xcd,
0x25, 0xc6, 0x68, 0x0c, 0x5c, 0x16, 0xba, 0xca, 0x94, 0xe8, 0x33, 0x4e, 0x90, 0x23, 0x40, 0x36,
0x85, 0x32, 0x2d, 0xbc, 0xc4, 0xff, 0x1b, 0x95, 0x68, 0x01, 0x39, 0x09, 0xc3, 0x1c, 0x4d, 0x0c,
0x74, 0x75, 0x71, 0x6b, 0xf4, 0xbf, 0xcf, 0x09, 0x82, 0x1e, 0x58, 0xc1, 0x14, 0x03, 0xe9, 0xdb,
0x7a, 0xfa, 0xf2, 0x3c, 0xd8, 0xfb, 0x2f, 0x51, 0x47, 0x2e, 0xbf, 0x49, 0x5e, 0x91, 0xb3, 0x33,
0x31, 0xf0, 0x1a, 0x8d, 0xfb, 0x23, 0xbe, 0xb2, 0xc9, 0xe1, 0x4f, 0x2e, 0xee, 0xae, 0xd1, 0x66,
0x49, 0x2f, 0xee, 0x87, 0x33, 0x50, 0x87, 0x89, 0x7e, 0x0b, 0x8f, 0x3f, 0x01, 0x00, 0x00, 0xff,
0xff, 0x22, 0xf3, 0x23, 0xc0, 0x72, 0x02, 0x00, 0x00,
}

33
rpc/scanner/service.proto Normal file
View File

@@ -0,0 +1,33 @@
syntax = "proto3";
package trivy.scanner.v1;
option go_package = "scanner";
import "github.com/aquasecurity/trivy/rpc/common/service.proto";
service Scanner {
rpc Scan(ScanRequest) returns (ScanResponse);
}
message ScanRequest {
string target = 1; // image name or tar file path
string image_id = 2;
repeated string layer_ids = 3;
ScanOptions options = 4;
}
message ScanOptions {
repeated string vuln_type = 1;
}
message ScanResponse {
common.OS os = 1;
bool eosl = 2;
repeated Result results = 3;
}
// Result is the same as github.com/aquasecurity/trivy/pkg/report.Result
message Result {
string target = 1;
repeated common.Vulnerability vulnerabilities = 2;
}

View File

@@ -0,0 +1,884 @@
// Code generated by protoc-gen-twirp v5.10.1, DO NOT EDIT.
// source: rpc/scanner/service.proto
/*
Package scanner is a generated twirp stub package.
This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.10.1.
It is generated from these files:
rpc/scanner/service.proto
*/
package scanner
import bytes "bytes"
import strings "strings"
import context "context"
import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
import twirp "github.com/twitchtv/twirp"
import ctxsetters "github.com/twitchtv/twirp/ctxsetters"
// Imports only used by utility functions:
import io "io"
import json "encoding/json"
import url "net/url"
// =================
// Scanner Interface
// =================
type Scanner interface {
Scan(context.Context, *ScanRequest) (*ScanResponse, error)
}
// =======================
// Scanner Protobuf Client
// =======================
type scannerProtobufClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewScannerProtobufClient creates a Protobuf client that implements the Scanner interface.
// It communicates using Protobuf and can be configured with a custom HTTPClient.
func NewScannerProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) Scanner {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + ScannerPathPrefix
urls := [1]string{
prefix + "Scan",
}
return &scannerProtobufClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
func (c *scannerProtobufClient) Scan(ctx context.Context, in *ScanRequest) (*ScanResponse, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.scanner.v1")
ctx = ctxsetters.WithServiceName(ctx, "Scanner")
ctx = ctxsetters.WithMethodName(ctx, "Scan")
out := new(ScanResponse)
err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
// ===================
// Scanner JSON Client
// ===================
type scannerJSONClient struct {
client HTTPClient
urls [1]string
opts twirp.ClientOptions
}
// NewScannerJSONClient creates a JSON client that implements the Scanner interface.
// It communicates using JSON and can be configured with a custom HTTPClient.
func NewScannerJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) Scanner {
if c, ok := client.(*http.Client); ok {
client = withoutRedirects(c)
}
clientOpts := twirp.ClientOptions{}
for _, o := range opts {
o(&clientOpts)
}
prefix := urlBase(addr) + ScannerPathPrefix
urls := [1]string{
prefix + "Scan",
}
return &scannerJSONClient{
client: client,
urls: urls,
opts: clientOpts,
}
}
func (c *scannerJSONClient) Scan(ctx context.Context, in *ScanRequest) (*ScanResponse, error) {
ctx = ctxsetters.WithPackageName(ctx, "trivy.scanner.v1")
ctx = ctxsetters.WithServiceName(ctx, "Scanner")
ctx = ctxsetters.WithMethodName(ctx, "Scan")
out := new(ScanResponse)
err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
if err != nil {
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
callClientError(ctx, c.opts.Hooks, twerr)
return nil, err
}
callClientResponseReceived(ctx, c.opts.Hooks)
return out, nil
}
// ======================
// Scanner Server Handler
// ======================
type scannerServer struct {
Scanner
hooks *twirp.ServerHooks
}
func NewScannerServer(svc Scanner, hooks *twirp.ServerHooks) TwirpServer {
return &scannerServer{
Scanner: svc,
hooks: hooks,
}
}
// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks.
// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
func (s *scannerServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) {
writeError(ctx, resp, err, s.hooks)
}
// ScannerPathPrefix is used for all URL paths on a twirp Scanner server.
// Requests are always: POST ScannerPathPrefix/method
// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes.
const ScannerPathPrefix = "/twirp/trivy.scanner.v1.Scanner/"
func (s *scannerServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
ctx := req.Context()
ctx = ctxsetters.WithPackageName(ctx, "trivy.scanner.v1")
ctx = ctxsetters.WithServiceName(ctx, "Scanner")
ctx = ctxsetters.WithResponseWriter(ctx, resp)
var err error
ctx, err = callRequestReceived(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
if req.Method != "POST" {
msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method)
err = badRouteError(msg, req.Method, req.URL.Path)
s.writeError(ctx, resp, err)
return
}
switch req.URL.Path {
case "/twirp/trivy.scanner.v1.Scanner/Scan":
s.serveScan(ctx, resp, req)
return
default:
msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
err = badRouteError(msg, req.Method, req.URL.Path)
s.writeError(ctx, resp, err)
return
}
}
func (s *scannerServer) serveScan(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
header := req.Header.Get("Content-Type")
i := strings.Index(header, ";")
if i == -1 {
i = len(header)
}
switch strings.TrimSpace(strings.ToLower(header[:i])) {
case "application/json":
s.serveScanJSON(ctx, resp, req)
case "application/protobuf":
s.serveScanProtobuf(ctx, resp, req)
default:
msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
twerr := badRouteError(msg, req.Method, req.URL.Path)
s.writeError(ctx, resp, twerr)
}
}
func (s *scannerServer) serveScanJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "Scan")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
reqContent := new(ScanRequest)
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
return
}
// Call service method
var respContent *ScanResponse
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Scanner.Scan(ctx, reqContent)
}()
if err != nil {
s.writeError(ctx, resp, err)
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *ScanResponse and nil error while calling Scan. nil responses are not supported"))
return
}
ctx = callResponsePrepared(ctx, s.hooks)
var buf bytes.Buffer
marshaler := &jsonpb.Marshaler{OrigName: true}
if err = marshaler.Marshal(&buf, respContent); err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response"))
return
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
respBytes := buf.Bytes()
resp.Header().Set("Content-Type", "application/json")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
resp.WriteHeader(http.StatusOK)
if n, err := resp.Write(respBytes); err != nil {
msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
twerr := twirp.NewError(twirp.Unknown, msg)
callError(ctx, s.hooks, twerr)
}
callResponseSent(ctx, s.hooks)
}
func (s *scannerServer) serveScanProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
var err error
ctx = ctxsetters.WithMethodName(ctx, "Scan")
ctx, err = callRequestRouted(ctx, s.hooks)
if err != nil {
s.writeError(ctx, resp, err)
return
}
buf, err := ioutil.ReadAll(req.Body)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
return
}
reqContent := new(ScanRequest)
if err = proto.Unmarshal(buf, reqContent); err != nil {
s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
return
}
// Call service method
var respContent *ScanResponse
func() {
defer ensurePanicResponses(ctx, resp, s.hooks)
respContent, err = s.Scanner.Scan(ctx, reqContent)
}()
if err != nil {
s.writeError(ctx, resp, err)
return
}
if respContent == nil {
s.writeError(ctx, resp, twirp.InternalError("received a nil *ScanResponse and nil error while calling Scan. nil responses are not supported"))
return
}
ctx = callResponsePrepared(ctx, s.hooks)
respBytes, err := proto.Marshal(respContent)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response"))
return
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
resp.WriteHeader(http.StatusOK)
if n, err := resp.Write(respBytes); err != nil {
msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
twerr := twirp.NewError(twirp.Unknown, msg)
callError(ctx, s.hooks, twerr)
}
callResponseSent(ctx, s.hooks)
}
func (s *scannerServer) ServiceDescriptor() ([]byte, int) {
return twirpFileDescriptor0, 0
}
func (s *scannerServer) ProtocGenTwirpVersion() string {
return "v5.10.1"
}
func (s *scannerServer) PathPrefix() string {
return ScannerPathPrefix
}
// =====
// Utils
// =====
// HTTPClient is the interface used by generated clients to send HTTP requests.
// It is fulfilled by *(net/http).Client, which is sufficient for most users.
// Users can provide their own implementation for special retry policies.
//
// HTTPClient implementations should not follow redirects. Redirects are
// automatically disabled if *(net/http).Client is passed to client
// constructors. See the withoutRedirects function in this file for more
// details.
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
// TwirpServer is the interface generated server structs will support: they're
// HTTP handlers with additional methods for accessing metadata about the
// service. Those accessors are a low-level API for building reflection tools.
// Most people can think of TwirpServers as just http.Handlers.
type TwirpServer interface {
http.Handler
// ServiceDescriptor returns gzipped bytes describing the .proto file that
// this service was generated from. Once unzipped, the bytes can be
// unmarshalled as a
// github.com/golang/protobuf/protoc-gen-go/descriptor.FileDescriptorProto.
//
// The returned integer is the index of this particular service within that
// FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a
// low-level field, expected to be used for reflection.
ServiceDescriptor() ([]byte, int)
// ProtocGenTwirpVersion is the semantic version string of the version of
// twirp used to generate this file.
ProtocGenTwirpVersion() string
// PathPrefix returns the HTTP URL path prefix for all methods handled by this
// service. This can be used with an HTTP mux to route twirp requests
// alongside non-twirp requests on one HTTP listener.
PathPrefix() string
}
// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta).
// Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks.
// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
func WriteError(resp http.ResponseWriter, err error) {
writeError(context.Background(), resp, err, nil)
}
// writeError writes Twirp errors in the response and triggers hooks.
func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) {
// Non-twirp errors are wrapped as Internal (default)
twerr, ok := err.(twirp.Error)
if !ok {
twerr = twirp.InternalErrorWith(err)
}
statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code())
ctx = ctxsetters.WithStatusCode(ctx, statusCode)
ctx = callError(ctx, hooks, twerr)
respBody := marshalErrorToJSON(twerr)
resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON
resp.Header().Set("Content-Length", strconv.Itoa(len(respBody)))
resp.WriteHeader(statusCode) // set HTTP status code and send response
_, writeErr := resp.Write(respBody)
if writeErr != nil {
// We have three options here. We could log the error, call the Error
// hook, or just silently ignore the error.
//
// Logging is unacceptable because we don't have a user-controlled
// logger; writing out to stderr without permission is too rude.
//
// Calling the Error hook would confuse users: it would mean the Error
// hook got called twice for one request, which is likely to lead to
// duplicated log messages and metrics, no matter how well we document
// the behavior.
//
// Silently ignoring the error is our least-bad option. It's highly
// likely that the connection is broken and the original 'err' says
// so anyway.
_ = writeErr
}
callResponseSent(ctx, hooks)
}
// urlBase helps ensure that addr specifies a scheme. If it is unparsable
// as a URL, it returns addr unchanged.
func urlBase(addr string) string {
// If the addr specifies a scheme, use it. If not, default to
// http. If url.Parse fails on it, return it unchanged.
url, err := url.Parse(addr)
if err != nil {
return addr
}
if url.Scheme == "" {
url.Scheme = "http"
}
return url.String()
}
// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in
// a context through the twirp.WithHTTPRequestHeaders function.
// If there are no headers set, or if they have the wrong type, nil is returned.
func getCustomHTTPReqHeaders(ctx context.Context) http.Header {
header, ok := twirp.HTTPRequestHeaders(ctx)
if !ok || header == nil {
return nil
}
copied := make(http.Header)
for k, vv := range header {
if vv == nil {
copied[k] = nil
continue
}
copied[k] = make([]string, len(vv))
copy(copied[k], vv)
}
return copied
}
// newRequest makes an http.Request from a client, adding common headers.
func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) {
req, err := http.NewRequest("POST", url, reqBody)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil {
req.Header = customHeader
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
// JSON serialization for errors
type twerrJSON struct {
Code string `json:"code"`
Msg string `json:"msg"`
Meta map[string]string `json:"meta,omitempty"`
}
// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body.
// If serialization fails, it will use a descriptive Internal error instead.
func marshalErrorToJSON(twerr twirp.Error) []byte {
// make sure that msg is not too large
msg := twerr.Msg()
if len(msg) > 1e6 {
msg = msg[:1e6]
}
tj := twerrJSON{
Code: string(twerr.Code()),
Msg: msg,
Meta: twerr.MetaMap(),
}
buf, err := json.Marshal(&tj)
if err != nil {
buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback
}
return buf
}
// errorFromResponse builds a twirp.Error from a non-200 HTTP response.
// If the response has a valid serialized Twirp error, then it's returned.
// If not, the response status code is used to generate a similar twirp
// error. See twirpErrorFromIntermediary for more info on intermediary errors.
func errorFromResponse(resp *http.Response) twirp.Error {
statusCode := resp.StatusCode
statusText := http.StatusText(statusCode)
if isHTTPRedirect(statusCode) {
// Unexpected redirect: it must be an error from an intermediary.
// Twirp clients don't follow redirects automatically, Twirp only handles
// POST requests, redirects should only happen on GET and HEAD requests.
location := resp.Header.Get("Location")
msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location)
return twirpErrorFromIntermediary(statusCode, msg, location)
}
respBodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return wrapInternal(err, "failed to read server error response body")
}
var tj twerrJSON
dec := json.NewDecoder(bytes.NewReader(respBodyBytes))
dec.DisallowUnknownFields()
if err := dec.Decode(&tj); err != nil || tj.Code == "" {
// Invalid JSON response; it must be an error from an intermediary.
msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText)
return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes))
}
errorCode := twirp.ErrorCode(tj.Code)
if !twirp.IsValidErrorCode(errorCode) {
msg := "invalid type returned from server error response: " + tj.Code
return twirp.InternalError(msg)
}
twerr := twirp.NewError(errorCode, tj.Msg)
for k, v := range tj.Meta {
twerr = twerr.WithMeta(k, v)
}
return twerr
}
// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors.
// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
// Returned twirp Errors have some additional metadata for inspection.
func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error {
var code twirp.ErrorCode
if isHTTPRedirect(status) { // 3xx
code = twirp.Internal
} else {
switch status {
case 400: // Bad Request
code = twirp.Internal
case 401: // Unauthorized
code = twirp.Unauthenticated
case 403: // Forbidden
code = twirp.PermissionDenied
case 404: // Not Found
code = twirp.BadRoute
case 429, 502, 503, 504: // Too Many Requests, Bad Gateway, Service Unavailable, Gateway Timeout
code = twirp.Unavailable
default: // All other codes
code = twirp.Unknown
}
}
twerr := twirp.NewError(code, msg)
twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary
twerr = twerr.WithMeta("status_code", strconv.Itoa(status))
if isHTTPRedirect(status) {
twerr = twerr.WithMeta("location", bodyOrLocation)
} else {
twerr = twerr.WithMeta("body", bodyOrLocation)
}
return twerr
}
func isHTTPRedirect(status int) bool {
return status >= 300 && status <= 399
}
// wrapInternal wraps an error with a prefix as an Internal error.
// The original error cause is accessible by github.com/pkg/errors.Cause.
func wrapInternal(err error, prefix string) twirp.Error {
return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err})
}
type wrappedError struct {
prefix string
cause error
}
func (e *wrappedError) Cause() error { return e.cause }
func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() }
// ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal
// error response (status 500), and error hooks are properly called with the panic wrapped as an error.
// The panic is re-raised so it can be handled normally with middleware.
func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) {
if r := recover(); r != nil {
// Wrap the panic as an error so it can be passed to error hooks.
// The original error is accessible from error hooks, but not visible in the response.
err := errFromPanic(r)
twerr := &internalWithCause{msg: "Internal service panic", cause: err}
// Actually write the error
writeError(ctx, resp, twerr, hooks)
// If possible, flush the error to the wire.
f, ok := resp.(http.Flusher)
if ok {
f.Flush()
}
panic(r)
}
}
// errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error.
func errFromPanic(p interface{}) error {
if err, ok := p.(error); ok {
return err
}
return fmt.Errorf("panic: %v", p)
}
// internalWithCause is a Twirp Internal error wrapping an original error cause, accessible
// by github.com/pkg/errors.Cause, but the original error message is not exposed on Msg().
type internalWithCause struct {
msg string
cause error
}
func (e *internalWithCause) Cause() error { return e.cause }
func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() }
func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal }
func (e *internalWithCause) Msg() string { return e.msg }
func (e *internalWithCause) Meta(key string) string { return "" }
func (e *internalWithCause) MetaMap() map[string]string { return nil }
func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e }
// malformedRequestError is used when the twirp server cannot unmarshal a request
func malformedRequestError(msg string) twirp.Error {
return twirp.NewError(twirp.Malformed, msg)
}
// badRouteError is used when the twirp server cannot route a request
func badRouteError(msg string, method, url string) twirp.Error {
err := twirp.NewError(twirp.BadRoute, msg)
err = err.WithMeta("twirp_invalid_route", method+" "+url)
return err
}
// withoutRedirects makes sure that the POST request can not be redirected.
// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or
// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the
// method to GET and removing the body. This produces very confusing error messages, so instead we
// set a redirect policy that always errors. This stops Go from executing the redirect.
//
// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect
// policy - if so, we'll run through that policy first.
//
// Because this requires modifying the http.Client, we make a new copy of the client and return it.
func withoutRedirects(in *http.Client) *http.Client {
copy := *in
copy.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if in.CheckRedirect != nil {
// Run the input's redirect if it exists, in case it has side effects, but ignore any error it
// returns, since we want to use ErrUseLastResponse.
err := in.CheckRedirect(req, via)
_ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use.
}
return http.ErrUseLastResponse
}
return &copy
}
// doProtobufRequest makes a Protobuf request to the remote Twirp service.
func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
reqBodyBytes, err := proto.Marshal(in)
if err != nil {
return wrapInternal(err, "failed to marshal proto request")
}
reqBody := bytes.NewBuffer(reqBodyBytes)
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
req, err := newRequest(ctx, url, reqBody, "application/protobuf")
if err != nil {
return wrapInternal(err, "could not build request")
}
ctx, err = callClientRequestPrepared(ctx, hooks, req)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
return wrapInternal(err, "failed to do request")
}
defer func() {
cerr := resp.Body.Close()
if err == nil && cerr != nil {
err = wrapInternal(cerr, "failed to close response body")
}
}()
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
if resp.StatusCode != 200 {
return errorFromResponse(resp)
}
respBodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
if err = proto.Unmarshal(respBodyBytes, out); err != nil {
return wrapInternal(err, "failed to unmarshal proto response")
}
return nil
}
// doJSONRequest makes a JSON request to the remote Twirp service.
func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
reqBody := bytes.NewBuffer(nil)
marshaler := &jsonpb.Marshaler{OrigName: true}
if err = marshaler.Marshal(reqBody, in); err != nil {
return wrapInternal(err, "failed to marshal json request")
}
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
req, err := newRequest(ctx, url, reqBody, "application/json")
if err != nil {
return wrapInternal(err, "could not build request")
}
ctx, err = callClientRequestPrepared(ctx, hooks, req)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
return wrapInternal(err, "failed to do request")
}
defer func() {
cerr := resp.Body.Close()
if err == nil && cerr != nil {
err = wrapInternal(cerr, "failed to close response body")
}
}()
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
if resp.StatusCode != 200 {
return errorFromResponse(resp)
}
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err = unmarshaler.Unmarshal(resp.Body, out); err != nil {
return wrapInternal(err, "failed to unmarshal json response")
}
if err = ctx.Err(); err != nil {
return wrapInternal(err, "aborted because context was done")
}
return nil
}
// Call twirp.ServerHooks.RequestReceived if the hook is available
func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
if h == nil || h.RequestReceived == nil {
return ctx, nil
}
return h.RequestReceived(ctx)
}
// Call twirp.ServerHooks.RequestRouted if the hook is available
func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
if h == nil || h.RequestRouted == nil {
return ctx, nil
}
return h.RequestRouted(ctx)
}
// Call twirp.ServerHooks.ResponsePrepared if the hook is available
func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context {
if h == nil || h.ResponsePrepared == nil {
return ctx
}
return h.ResponsePrepared(ctx)
}
// Call twirp.ServerHooks.ResponseSent if the hook is available
func callResponseSent(ctx context.Context, h *twirp.ServerHooks) {
if h == nil || h.ResponseSent == nil {
return
}
h.ResponseSent(ctx)
}
// Call twirp.ServerHooks.Error if the hook is available
func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context {
if h == nil || h.Error == nil {
return ctx
}
return h.Error(ctx, err)
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return
}
h.ResponseReceived(ctx)
}
func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) {
if h == nil || h.RequestPrepared == nil {
return ctx, nil
}
return h.RequestPrepared(ctx, req)
}
func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) {
if h == nil || h.Error == nil {
return
}
h.Error(ctx, err)
}
var twirpFileDescriptor0 = []byte{
// 361 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xc1, 0x6b, 0xbb, 0x30,
0x18, 0x45, 0x5b, 0x6a, 0x8d, 0x3f, 0xf8, 0x95, 0x1c, 0x86, 0x6d, 0xd9, 0x10, 0x4f, 0x65, 0x07,
0x65, 0x0e, 0xb6, 0xfb, 0xa0, 0x87, 0x9e, 0x3a, 0xd2, 0xb1, 0xc3, 0x2e, 0x25, 0xd5, 0x0f, 0x17,
0x50, 0x63, 0x93, 0x28, 0xf3, 0x1f, 0xd9, 0xdf, 0x3b, 0x4c, 0x2c, 0xac, 0x1d, 0xbd, 0x25, 0xef,
0x3d, 0xbf, 0xf7, 0xde, 0x67, 0xd0, 0x5c, 0xd4, 0x69, 0x2c, 0x53, 0x5a, 0x55, 0x20, 0x62, 0x09,
0xa2, 0x65, 0x29, 0x44, 0xb5, 0xe0, 0x8a, 0xe3, 0x99, 0x12, 0xac, 0xed, 0xa2, 0x81, 0x8c, 0xda,
0x87, 0xc5, 0x53, 0xce, 0xd4, 0x67, 0x73, 0x88, 0x52, 0x5e, 0xc6, 0xf4, 0xd8, 0x50, 0x09, 0x69,
0x23, 0x98, 0xea, 0x62, 0xad, 0x8c, 0xfb, 0x51, 0x29, 0x2f, 0x4b, 0x5e, 0x9d, 0x4f, 0x0a, 0xbf,
0x2d, 0xe4, 0xed, 0x52, 0x5a, 0x11, 0x38, 0x36, 0x20, 0x15, 0xbe, 0x41, 0x13, 0x45, 0x45, 0x0e,
0xca, 0xb7, 0x02, 0x6b, 0xe5, 0x92, 0xe1, 0x86, 0xe7, 0x68, 0xca, 0x4a, 0x9a, 0xc3, 0x9e, 0x65,
0xbe, 0xad, 0x19, 0x47, 0xdf, 0x37, 0x19, 0x5e, 0x22, 0xb7, 0xa0, 0x1d, 0x88, 0x3d, 0xcb, 0xa4,
0x3f, 0x0a, 0x46, 0x2b, 0x97, 0x4c, 0x35, 0xb0, 0xc9, 0x24, 0x7e, 0x46, 0x0e, 0xaf, 0x15, 0xe3,
0x95, 0xf4, 0xc7, 0x81, 0xb5, 0xf2, 0x92, 0xdb, 0xe8, 0x32, 0x7b, 0xd4, 0xfb, 0x6f, 0x8d, 0x88,
0x9c, 0xd4, 0xe1, 0xbd, 0xc9, 0x35, 0xe0, 0xbd, 0x49, 0xdb, 0x14, 0xd5, 0x5e, 0x75, 0x35, 0xf8,
0x96, 0x31, 0xe9, 0x81, 0xb7, 0xae, 0x86, 0xf0, 0x0b, 0xfd, 0x33, 0x1d, 0x64, 0xcd, 0x2b, 0x09,
0x38, 0x40, 0x36, 0x97, 0xba, 0x80, 0x97, 0xcc, 0x06, 0x3f, 0xd3, 0x3e, 0xda, 0xee, 0x88, 0xcd,
0x25, 0xc6, 0x68, 0x0c, 0x5c, 0x16, 0xba, 0xca, 0x94, 0xe8, 0x33, 0x4e, 0x90, 0x23, 0x40, 0x36,
0x85, 0x32, 0x2d, 0xbc, 0xc4, 0xff, 0x1b, 0x95, 0x68, 0x01, 0x39, 0x09, 0xc3, 0x1c, 0x4d, 0x0c,
0x74, 0x75, 0x71, 0x6b, 0xf4, 0xbf, 0xcf, 0x09, 0x82, 0x1e, 0x58, 0xc1, 0x14, 0x03, 0xe9, 0xdb,
0x7a, 0xfa, 0xf2, 0x3c, 0xd8, 0xfb, 0x2f, 0x51, 0x47, 0x2e, 0xbf, 0x49, 0x5e, 0x91, 0xb3, 0x33,
0x31, 0xf0, 0x1a, 0x8d, 0xfb, 0x23, 0xbe, 0xb2, 0xc9, 0xe1, 0x4f, 0x2e, 0xee, 0xae, 0xd1, 0x66,
0x49, 0x2f, 0xee, 0x87, 0x33, 0x50, 0x87, 0x89, 0x7e, 0x0b, 0x8f, 0x3f, 0x01, 0x00, 0x00, 0xff,
0xff, 0x22, 0xf3, 0x23, 0xc0, 0x72, 0x02, 0x00, 0x00,
}