diff --git a/Makefile b/Makefile index 4f83f36890..f8f2901dc4 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/go.mod b/go.mod index a390658726..c4322e4b58 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 8631ae1d73..2a6afbc556 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/integration/docker_engine_test.go b/integration/docker_engine_test.go index 815c39a6f0..02841d5bea 100644 --- a/integration/docker_engine_test.go +++ b/integration/docker_engine_test.go @@ -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) } }) diff --git a/internal/app.go b/internal/app.go index ef2a0cc708..6c7c69ac8d 100644 --- a/internal/app.go +++ b/internal/app.go @@ -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, diff --git a/internal/client/config/config.go b/internal/client/config/config.go index c9b5f21628..e02be710fc 100644 --- a/internal/client/config/config.go +++ b/internal/client/config/config.go @@ -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"), diff --git a/internal/client/inject.go b/internal/client/inject.go index 58f9f4db1f..fc42a5a22a 100644 --- a/internal/client/inject.go +++ b/internal/client/inject.go @@ -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 { diff --git a/internal/client/run.go b/internal/client/run.go index f3d254ca8a..d90d2e93d2 100644 --- a/internal/client/run.go +++ b/internal/client/run.go @@ -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) } diff --git a/internal/client/wire_gen.go b/internal/client/wire_gen.go index bc2e3edbb7..ca97e705dd 100644 --- a/internal/client/wire_gen.go +++ b/internal/client/wire_gen.go @@ -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 } diff --git a/internal/operation/operation.go b/internal/operation/operation.go index e264b31fa8..cb4a8088b3 100644 --- a/internal/operation/operation.go +++ b/internal/operation/operation.go @@ -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} } diff --git a/internal/server/run.go b/internal/server/run.go index 3a40948e77..a4b8072afb 100644 --- a/internal/server/run.go +++ b/internal/server/run.go @@ -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) } diff --git a/internal/standalone/config/config.go b/internal/standalone/config/config.go index bed2cd49b1..0de005b088 100644 --- a/internal/standalone/config/config.go +++ b/internal/standalone/config/config.go @@ -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"), diff --git a/internal/standalone/inject.go b/internal/standalone/inject.go index db60dfe0d9..11626d7981 100644 --- a/internal/standalone/inject.go +++ b/internal/standalone/inject.go @@ -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 { diff --git a/internal/standalone/run.go b/internal/standalone/run.go index d76fbb8c53..bcf7761824 100644 --- a/internal/standalone/run.go +++ b/internal/standalone/run.go @@ -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) } diff --git a/internal/standalone/wire_gen.go b/internal/standalone/wire_gen.go index c37551c12b..6567c6e14b 100644 --- a/internal/standalone/wire_gen.go +++ b/internal/standalone/wire_gen.go @@ -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 { diff --git a/pkg/cache/remote.go b/pkg/cache/remote.go new file mode 100644 index 0000000000..619a23adb6 --- /dev/null +++ b/pkg/cache/remote.go @@ -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 +} diff --git a/pkg/cache/remote_test.go b/pkg/cache/remote_test.go new file mode 100644 index 0000000000..dc34134888 --- /dev/null +++ b/pkg/cache/remote_test.go @@ -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) + }) + } +} diff --git a/pkg/detector/ospkg/alpine/alpine.go b/pkg/detector/ospkg/alpine/alpine.go index debf5a00d8..fa98b8fd13 100644 --- a/pkg/detector/ospkg/alpine/alpine.go +++ b/pkg/detector/ospkg/alpine/alpine.go @@ -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, ".")] diff --git a/pkg/detector/ospkg/alpine/alpine_test.go b/pkg/detector/ospkg/alpine/alpine_test.go index c8bc05ff38..216f040aed 100644 --- a/pkg/detector/ospkg/alpine/alpine_test.go +++ b/pkg/detector/ospkg/alpine/alpine_test.go @@ -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", diff --git a/pkg/detector/ospkg/amazon/amazon.go b/pkg/detector/ospkg/amazon/amazon.go index 47e9fa5eb0..f99164ae42 100644 --- a/pkg/detector/ospkg/amazon/amazon.go +++ b/pkg/detector/ospkg/amazon/amazon.go @@ -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] diff --git a/pkg/detector/ospkg/amazon/amazon_test.go b/pkg/detector/ospkg/amazon/amazon_test.go index fd4d880f82..81d53a25b6 100644 --- a/pkg/detector/ospkg/amazon/amazon_test.go +++ b/pkg/detector/ospkg/amazon/amazon_test.go @@ -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", diff --git a/pkg/detector/ospkg/debian/debian.go b/pkg/detector/ospkg/debian/debian.go index a9f2a3708b..a79e2d38d6 100644 --- a/pkg/detector/ospkg/debian/debian.go +++ b/pkg/detector/ospkg/debian/debian.go @@ -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 { diff --git a/pkg/detector/ospkg/detect.go b/pkg/detector/ospkg/detect.go index 7831e23bee..f885f4a20b 100644 --- a/pkg/detector/ospkg/detect.go +++ b/pkg/detector/ospkg/detect.go @@ -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 diff --git a/pkg/detector/ospkg/detector_mock.go b/pkg/detector/ospkg/detector_mock.go index e1749f7bfb..a2ff264586 100644 --- a/pkg/detector/ospkg/detector_mock.go +++ b/pkg/detector/ospkg/detector_mock.go @@ -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 { diff --git a/pkg/detector/ospkg/oracle/oracle.go b/pkg/detector/ospkg/oracle/oracle.go index c912a2d8f7..dce928ae5b 100644 --- a/pkg/detector/ospkg/oracle/oracle.go +++ b/pkg/detector/ospkg/oracle/oracle.go @@ -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 { diff --git a/pkg/detector/ospkg/photon/photon.go b/pkg/detector/ospkg/photon/photon.go index 1daed41cd3..bd1367e863 100644 --- a/pkg/detector/ospkg/photon/photon.go +++ b/pkg/detector/ospkg/photon/photon.go @@ -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)) diff --git a/pkg/detector/ospkg/redhat/redhat.go b/pkg/detector/ospkg/redhat/redhat.go index af5f4c330d..fb615cfd05 100644 --- a/pkg/detector/ospkg/redhat/redhat.go +++ b/pkg/detector/ospkg/redhat/redhat.go @@ -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, ".")] diff --git a/pkg/detector/ospkg/redhat/redhat_test.go b/pkg/detector/ospkg/redhat/redhat_test.go index 777cd2032a..9e311095fe 100644 --- a/pkg/detector/ospkg/redhat/redhat_test.go +++ b/pkg/detector/ospkg/redhat/redhat_test.go @@ -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", diff --git a/pkg/detector/ospkg/suse/suse.go b/pkg/detector/ospkg/suse/suse.go index 9dfdb3b9e5..9f58aa5673 100644 --- a/pkg/detector/ospkg/suse/suse.go +++ b/pkg/detector/ospkg/suse/suse.go @@ -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)) diff --git a/pkg/detector/ospkg/ubuntu/ubuntu.go b/pkg/detector/ospkg/ubuntu/ubuntu.go index e0e23cc8ab..885a0c3130 100644 --- a/pkg/detector/ospkg/ubuntu/ubuntu.go +++ b/pkg/detector/ospkg/ubuntu/ubuntu.go @@ -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)) diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go new file mode 100644 index 0000000000..81955080cc --- /dev/null +++ b/pkg/rpc/client/client.go @@ -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 +} diff --git a/pkg/rpc/client/client_test.go b/pkg/rpc/client/client_test.go new file mode 100644 index 0000000000..b53171e672 --- /dev/null +++ b/pkg/rpc/client/client_test.go @@ -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) + }) + } +} diff --git a/pkg/rpc/client/library/client.go b/pkg/rpc/client/library/client.go deleted file mode 100644 index b8d7d2a653..0000000000 --- a/pkg/rpc/client/library/client.go +++ /dev/null @@ -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 -} diff --git a/pkg/rpc/client/library/client_test.go b/pkg/rpc/client/library/client_test.go deleted file mode 100644 index 2fb9ca7784..0000000000 --- a/pkg/rpc/client/library/client_test.go +++ /dev/null @@ -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) - }) - } -} diff --git a/pkg/rpc/client/ospkg/client.go b/pkg/rpc/client/ospkg/client.go deleted file mode 100644 index 77b1dc93f4..0000000000 --- a/pkg/rpc/client/ospkg/client.go +++ /dev/null @@ -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 -} diff --git a/pkg/rpc/client/ospkg/client_test.go b/pkg/rpc/client/ospkg/client_test.go deleted file mode 100644 index fc84644bc1..0000000000 --- a/pkg/rpc/client/ospkg/client_test.go +++ /dev/null @@ -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) - }) - } -} diff --git a/pkg/rpc/convert.go b/pkg/rpc/convert.go index eaf4df7b52..a9e9c3ad36 100644 --- a/pkg/rpc/convert.go +++ b/pkg/rpc/convert.go @@ -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, + } +} diff --git a/pkg/rpc/convert_test.go b/pkg/rpc/convert_test.go index c31be065ef..859351628d 100644 --- a/pkg/rpc/convert_test.go +++ b/pkg/rpc/convert_test.go @@ -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"}, }, }, diff --git a/pkg/rpc/server/inject.go b/pkg/rpc/server/inject.go index d525a53dfd..aadedcab76 100644 --- a/pkg/rpc/server/inject.go +++ b/pkg/rpc/server/inject.go @@ -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{} } diff --git a/pkg/rpc/server/library/server.go b/pkg/rpc/server/library/server.go index bd22768716..38aa009b06 100644 --- a/pkg/rpc/server/library/server.go +++ b/pkg/rpc/server/library/server.go @@ -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 { diff --git a/pkg/rpc/server/library/server_test.go b/pkg/rpc/server/library/server_test.go index 40e111101a..3f677a2cb1 100644 --- a/pkg/rpc/server/library/server_test.go +++ b/pkg/rpc/server/library/server_test.go @@ -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() diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go new file mode 100644 index 0000000000..0f2d17f429 --- /dev/null +++ b/pkg/rpc/server/listen.go @@ -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 +} diff --git a/pkg/rpc/server/listen_test.go b/pkg/rpc/server/listen_test.go new file mode 100644 index 0000000000..497930f5e0 --- /dev/null +++ b/pkg/rpc/server/listen_test.go @@ -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) + }) + } +} diff --git a/pkg/rpc/server/ospkg/server.go b/pkg/rpc/server/ospkg/server.go index 839e774846..ae4c4f67bd 100644 --- a/pkg/rpc/server/ospkg/server.go +++ b/pkg/rpc/server/ospkg/server.go @@ -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) diff --git a/pkg/rpc/server/ospkg/server_test.go b/pkg/rpc/server/ospkg/server_test.go index 9eedc09326..2fb17df1b9 100644 --- a/pkg/rpc/server/ospkg/server_test.go +++ b/pkg/rpc/server/ospkg/server_test.go @@ -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) diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 5ab89dbd5e..183f3f8154 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -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 } diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 497930f5e0..91cc4471c7 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -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) }) } } diff --git a/pkg/rpc/server/wire_gen.go b/pkg/rpc/server/wire_gen.go index 22ce988ddf..eedd670280 100644 --- a/pkg/rpc/server/wire_gen.go +++ b/pkg/rpc/server/wire_gen.go @@ -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 } diff --git a/pkg/scanner/library/scan.go b/pkg/scanner/library/scan.go deleted file mode 100644 index 7b05e1f03f..0000000000 --- a/pkg/scanner/library/scan.go +++ /dev/null @@ -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 -} diff --git a/pkg/scanner/library/scan_test.go b/pkg/scanner/library/scan_test.go deleted file mode 100644 index e2bad645eb..0000000000 --- a/pkg/scanner/library/scan_test.go +++ /dev/null @@ -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) - }) - } -} diff --git a/pkg/scanner/local/mock_applier.go b/pkg/scanner/local/mock_applier.go new file mode 100644 index 0000000000..b3c5c191f2 --- /dev/null +++ b/pkg/scanner/local/mock_applier.go @@ -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 +} diff --git a/pkg/scanner/local/mock_library_detector.go b/pkg/scanner/local/mock_library_detector.go new file mode 100644 index 0000000000..7ff9252d92 --- /dev/null +++ b/pkg/scanner/local/mock_library_detector.go @@ -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 +} diff --git a/pkg/scanner/local/mock_ospkg_detector.go b/pkg/scanner/local/mock_ospkg_detector.go new file mode 100644 index 0000000000..abb04edf3d --- /dev/null +++ b/pkg/scanner/local/mock_ospkg_detector.go @@ -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 +} diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go new file mode 100644 index 0000000000..4772900b77 --- /dev/null +++ b/pkg/scanner/local/scan.go @@ -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 +} diff --git a/pkg/scanner/local/scan_test.go b/pkg/scanner/local/scan_test.go new file mode 100644 index 0000000000..d51e466b63 --- /dev/null +++ b/pkg/scanner/local/scan_test.go @@ -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) + }) + } +} diff --git a/pkg/scanner/mock_analyzer.go b/pkg/scanner/mock_analyzer.go new file mode 100644 index 0000000000..27bd8017c1 --- /dev/null +++ b/pkg/scanner/mock_analyzer.go @@ -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 +} diff --git a/pkg/scanner/mock_driver.go b/pkg/scanner/mock_driver.go new file mode 100644 index 0000000000..0ec8275ad2 --- /dev/null +++ b/pkg/scanner/mock_driver.go @@ -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 +} diff --git a/pkg/scanner/ospkg/scan.go b/pkg/scanner/ospkg/scan.go deleted file mode 100644 index 72bc19c1d5..0000000000 --- a/pkg/scanner/ospkg/scan.go +++ /dev/null @@ -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 -} diff --git a/pkg/scanner/ospkg/scan_bsd.go b/pkg/scanner/ospkg/scan_bsd.go deleted file mode 100644 index 154f463272..0000000000 --- a/pkg/scanner/ospkg/scan_bsd.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build freebsd netbsd openbsd - -package ospkg - -import ( - _ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd" -) diff --git a/pkg/scanner/ospkg/scan_test.go b/pkg/scanner/ospkg/scan_test.go deleted file mode 100644 index 5d21fb596e..0000000000 --- a/pkg/scanner/ospkg/scan_test.go +++ /dev/null @@ -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 -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) - }) - } -} diff --git a/pkg/scanner/ospkg/scan_unix.go b/pkg/scanner/ospkg/scan_unix.go deleted file mode 100644 index 784872c61b..0000000000 --- a/pkg/scanner/ospkg/scan_unix.go +++ /dev/null @@ -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" -) diff --git a/pkg/scanner/scan.go b/pkg/scanner/scan.go index 9d027fc436..4036d0962f 100644 --- a/pkg/scanner/scan.go +++ b/pkg/scanner/scan.go @@ -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) -} diff --git a/pkg/scanner/scan_test.go b/pkg/scanner/scan_test.go new file mode 100644 index 0000000000..1703bcde7c --- /dev/null +++ b/pkg/scanner/scan_test.go @@ -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) + }) + } +} diff --git a/pkg/scanner/utils/utils.go b/pkg/scanner/utils/utils.go index 52eb6e6bb3..9dce7ae2e3 100644 --- a/pkg/scanner/utils/utils.go +++ b/pkg/scanner/utils/utils.go @@ -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) } diff --git a/pkg/types/dockerConf.go b/pkg/types/dockerConf.go deleted file mode 100644 index 76fb8c2700..0000000000 --- a/pkg/types/dockerConf.go +++ /dev/null @@ -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 -} diff --git a/pkg/types/docker_conf.go b/pkg/types/docker_conf.go new file mode 100644 index 0000000000..2b290c7ddd --- /dev/null +++ b/pkg/types/docker_conf.go @@ -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 +} diff --git a/pkg/types/scanoptions.go b/pkg/types/scanoptions.go index c71aa60759..2c4dbc55fe 100644 --- a/pkg/types/scanoptions.go +++ b/pkg/types/scanoptions.go @@ -1,9 +1,6 @@ package types type ScanOptions struct { - VulnType []string - - // for client/server - RemoteURL string - Token string + VulnType []string + ScanRemovedPackages bool } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index c3bdd40319..1755d04ff4 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -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 { diff --git a/pkg/vulnerability/mock_operation.go b/pkg/vulnerability/mock_operation.go new file mode 100644 index 0000000000..673e7041ba --- /dev/null +++ b/pkg/vulnerability/mock_operation.go @@ -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 +} diff --git a/pkg/vulnerability/vulnerability_mock.go b/pkg/vulnerability/vulnerability_mock.go deleted file mode 100644 index 652dcf57d8..0000000000 --- a/pkg/vulnerability/vulnerability_mock.go +++ /dev/null @@ -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 -} diff --git a/rpc/cache/service.pb.go b/rpc/cache/service.pb.go new file mode 100644 index 0000000000..0854b68861 --- /dev/null +++ b/rpc/cache/service.pb.go @@ -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, +} diff --git a/rpc/cache/service.proto b/rpc/cache/service.proto new file mode 100644 index 0000000000..61514754c8 --- /dev/null +++ b/rpc/cache/service.proto @@ -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; +} diff --git a/rpc/cache/service.twirp.go b/rpc/cache/service.twirp.go new file mode 100644 index 0000000000..618f666ef7 --- /dev/null +++ b/rpc/cache/service.twirp.go @@ -0,0 +1,1258 @@ +// Code generated by protoc-gen-twirp v5.10.1, DO NOT EDIT. +// source: rpc/cache/service.proto + +/* +Package cache 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/cache/service.proto +*/ +package cache + +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" + +import google_protobuf1 "github.com/golang/protobuf/ptypes/empty" + +// Imports only used by utility functions: +import io "io" +import json "encoding/json" +import url "net/url" + +// =============== +// Cache Interface +// =============== + +type Cache interface { + PutImage(context.Context, *PutImageRequest) (*google_protobuf1.Empty, error) + + PutLayer(context.Context, *PutLayerRequest) (*google_protobuf1.Empty, error) + + MissingLayers(context.Context, *MissingLayersRequest) (*MissingLayersResponse, error) +} + +// ===================== +// Cache Protobuf Client +// ===================== + +type cacheProtobufClient struct { + client HTTPClient + urls [3]string + opts twirp.ClientOptions +} + +// NewCacheProtobufClient creates a Protobuf client that implements the Cache interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewCacheProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) Cache { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + prefix := urlBase(addr) + CachePathPrefix + urls := [3]string{ + prefix + "PutImage", + prefix + "PutLayer", + prefix + "MissingLayers", + } + + return &cacheProtobufClient{ + client: client, + urls: urls, + opts: clientOpts, + } +} + +func (c *cacheProtobufClient) PutImage(ctx context.Context, in *PutImageRequest) (*google_protobuf1.Empty, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "PutImage") + out := new(google_protobuf1.Empty) + 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 +} + +func (c *cacheProtobufClient) PutLayer(ctx context.Context, in *PutLayerRequest) (*google_protobuf1.Empty, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "PutLayer") + out := new(google_protobuf1.Empty) + err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], 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 +} + +func (c *cacheProtobufClient) MissingLayers(ctx context.Context, in *MissingLayersRequest) (*MissingLayersResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "MissingLayers") + out := new(MissingLayersResponse) + err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], 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 +} + +// ================= +// Cache JSON Client +// ================= + +type cacheJSONClient struct { + client HTTPClient + urls [3]string + opts twirp.ClientOptions +} + +// NewCacheJSONClient creates a JSON client that implements the Cache interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewCacheJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) Cache { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + prefix := urlBase(addr) + CachePathPrefix + urls := [3]string{ + prefix + "PutImage", + prefix + "PutLayer", + prefix + "MissingLayers", + } + + return &cacheJSONClient{ + client: client, + urls: urls, + opts: clientOpts, + } +} + +func (c *cacheJSONClient) PutImage(ctx context.Context, in *PutImageRequest) (*google_protobuf1.Empty, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "PutImage") + out := new(google_protobuf1.Empty) + 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 +} + +func (c *cacheJSONClient) PutLayer(ctx context.Context, in *PutLayerRequest) (*google_protobuf1.Empty, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "PutLayer") + out := new(google_protobuf1.Empty) + err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], 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 +} + +func (c *cacheJSONClient) MissingLayers(ctx context.Context, in *MissingLayersRequest) (*MissingLayersResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + ctx = ctxsetters.WithMethodName(ctx, "MissingLayers") + out := new(MissingLayersResponse) + err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], 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 +} + +// ==================== +// Cache Server Handler +// ==================== + +type cacheServer struct { + Cache + hooks *twirp.ServerHooks +} + +func NewCacheServer(svc Cache, hooks *twirp.ServerHooks) TwirpServer { + return &cacheServer{ + Cache: 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 *cacheServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// CachePathPrefix is used for all URL paths on a twirp Cache server. +// Requests are always: POST CachePathPrefix/method +// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes. +const CachePathPrefix = "/twirp/trivy.cache.v1.Cache/" + +func (s *cacheServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "trivy.cache.v1") + ctx = ctxsetters.WithServiceName(ctx, "Cache") + 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.cache.v1.Cache/PutImage": + s.servePutImage(ctx, resp, req) + return + case "/twirp/trivy.cache.v1.Cache/PutLayer": + s.servePutLayer(ctx, resp, req) + return + case "/twirp/trivy.cache.v1.Cache/MissingLayers": + s.serveMissingLayers(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 *cacheServer) servePutImage(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.servePutImageJSON(ctx, resp, req) + case "application/protobuf": + s.servePutImageProtobuf(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 *cacheServer) servePutImageJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "PutImage") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(PutImageRequest) + 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 *google_protobuf1.Empty + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.PutImage(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutImage. nil responses are not supported")) + 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 *cacheServer) servePutImageProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "PutImage") + 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(PutImageRequest) + 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 *google_protobuf1.Empty + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.PutImage(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutImage. nil responses are not supported")) + 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 *cacheServer) servePutLayer(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.servePutLayerJSON(ctx, resp, req) + case "application/protobuf": + s.servePutLayerProtobuf(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 *cacheServer) servePutLayerJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "PutLayer") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(PutLayerRequest) + 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 *google_protobuf1.Empty + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.PutLayer(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutLayer. nil responses are not supported")) + 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 *cacheServer) servePutLayerProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "PutLayer") + 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(PutLayerRequest) + 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 *google_protobuf1.Empty + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.PutLayer(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling PutLayer. nil responses are not supported")) + 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 *cacheServer) serveMissingLayers(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.serveMissingLayersJSON(ctx, resp, req) + case "application/protobuf": + s.serveMissingLayersProtobuf(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 *cacheServer) serveMissingLayersJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "MissingLayers") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(MissingLayersRequest) + 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 *MissingLayersResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.MissingLayers(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingLayersResponse and nil error while calling MissingLayers. nil responses are not supported")) + 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 *cacheServer) serveMissingLayersProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "MissingLayers") + 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(MissingLayersRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + // Call service method + var respContent *MissingLayersResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = s.Cache.MissingLayers(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *MissingLayersResponse and nil error while calling MissingLayers. nil responses are not supported")) + 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 *cacheServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 0 +} + +func (s *cacheServer) ProtocGenTwirpVersion() string { + return "v5.10.1" +} + +func (s *cacheServer) PathPrefix() string { + return CachePathPrefix +} + +// ===== +// 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 © +} + +// 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{ + // 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, +} diff --git a/rpc/common/service.pb.go b/rpc/common/service.pb.go new file mode 100644 index 0000000000..b1e60679e6 --- /dev/null +++ b/rpc/common/service.pb.go @@ -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, +} diff --git a/rpc/common/service.proto b/rpc/common/service.proto new file mode 100644 index 0000000000..f447092cc5 --- /dev/null +++ b/rpc/common/service.proto @@ -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; +} diff --git a/rpc/detector/service.pb.go b/rpc/detector/service.pb.go index 496b35f67c..5d012010fa 100644 --- a/rpc/detector/service.pb.go +++ b/rpc/detector/service.pb.go @@ -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, } diff --git a/rpc/detector/service.proto b/rpc/detector/service.proto index 65cdbf1a72..bd7c3c3bfe 100644 --- a/rpc/detector/service.proto +++ b/rpc/detector/service.proto @@ -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; -} diff --git a/rpc/detector/service.twirp.go b/rpc/detector/service.twirp.go index e10b3b2d4c..5accf05a6a 100644 --- a/rpc/detector/service.twirp.go +++ b/rpc/detector/service.twirp.go @@ -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, } diff --git a/rpc/scanner/service.pb.go b/rpc/scanner/service.pb.go new file mode 100644 index 0000000000..075640f7bb --- /dev/null +++ b/rpc/scanner/service.pb.go @@ -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, +} diff --git a/rpc/scanner/service.proto b/rpc/scanner/service.proto new file mode 100644 index 0000000000..643dfd4c6b --- /dev/null +++ b/rpc/scanner/service.proto @@ -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; +} diff --git a/rpc/scanner/service.twirp.go b/rpc/scanner/service.twirp.go new file mode 100644 index 0000000000..886558233f --- /dev/null +++ b/rpc/scanner/service.twirp.go @@ -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 © +} + +// 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, +}