feat: prepare for config scanning (#1005)

* temp: disable config scanning
This commit is contained in:
Teppei Fukuda
2021-05-20 09:05:36 +03:00
committed by GitHub
parent 8fc6ea6489
commit 1b66b77f69
77 changed files with 2212 additions and 2341 deletions

View File

@@ -17,7 +17,7 @@ jobs:
- name: Lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.31
version: v1.39
args: --deadline=30m
- name: Run unit tests

View File

@@ -15,7 +15,7 @@ $(GOBIN)/wire:
.PHONY: wire
wire: $(GOBIN)/wire
wire gen ./pkg/... ./internal/...
wire gen ./pkg/...
.PHONY: mock
mock: $(GOBIN)/mockery

6
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/fanal v0.0.0-20210501235003-c816628070c1
github.com/aquasecurity/fanal v0.0.0-20210519050514-051631be3f69
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
@@ -19,11 +19,12 @@ require (
github.com/docker/docker v20.10.3+incompatible
github.com/docker/go-connections v0.4.0
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d // indirect
github.com/fatih/color v1.10.0
github.com/go-redis/redis/v8 v8.4.0
github.com/goccy/go-yaml v1.8.2 // indirect
github.com/golang/protobuf v1.4.3
github.com/google/go-containerregistry v0.1.2
github.com/google/go-github/v28 v28.1.1
github.com/google/go-github/v33 v33.0.0
github.com/google/wire v0.4.0
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/hashicorp/go-getter v1.5.2
@@ -46,6 +47,7 @@ require (
github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.16.0
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect

27
go.sum
View File

@@ -141,6 +141,7 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
@@ -166,16 +167,14 @@ github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDw
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70 h1:0v2pf+hIElPvGKLyDye08UtPH8AjujTVLUOWOgoM5O4=
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70/go.mod h1:cPTOJcf8bdP24oXhBsPVVczcjkICcApAwAOsn6CpHTI=
github.com/aquasecurity/fanal v0.0.0-20210501093021-8aaac3e8dea7 h1:bY5D5GVthqQCvnNllG2NVXYpOQJJRi7KFhLdVrskaDg=
github.com/aquasecurity/fanal v0.0.0-20210501093021-8aaac3e8dea7/go.mod h1:cPTOJcf8bdP24oXhBsPVVczcjkICcApAwAOsn6CpHTI=
github.com/aquasecurity/fanal v0.0.0-20210501235003-c816628070c1 h1:xgdjcsA4Go/9k9XDXYimVF+BgyMlt7YoeWTMs2DpR8Y=
github.com/aquasecurity/fanal v0.0.0-20210501235003-c816628070c1/go.mod h1:cPTOJcf8bdP24oXhBsPVVczcjkICcApAwAOsn6CpHTI=
github.com/aquasecurity/fanal v0.0.0-20210519050514-051631be3f69 h1:J7uGm7PSBI4yAACkaxH4/PTdfhQYIQy7tSlwUknr4U8=
github.com/aquasecurity/fanal v0.0.0-20210519050514-051631be3f69/go.mod h1://8XzKt4IvM6QUSIYpY5KURCEArOzscy6R0FsmVLEBo=
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976 h1:ypl/IDxujzEymmwtzGJqQyboI2oZr1se+OoYaGqgBzQ=
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976/go.mod h1:Cv/FOCXy6gwvDbz/KX48+y//SmbnKroFwW5hquXn5G4=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
@@ -309,6 +308,7 @@ github.com/containerd/stargz-snapshotter v0.0.0-20201027054423-3a04e4c2c116/go.m
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v1.0.1 h1:PvuK4E3D5S5q6IqsPDCy928FhP0LUIGcmZ/Yhgp5Djw=
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -481,6 +481,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
@@ -590,8 +591,9 @@ github.com/google/go-containerregistry v0.1.2 h1:YjFNKqxzWUVZND8d4ItF9wuYlE75WQf
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -711,7 +713,9 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.6.0 h1:3krZOfGY6SziUXa6H9PJU6TyohHn7I+ARYnhbeNBz+o=
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -791,6 +795,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -869,6 +874,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
@@ -876,9 +882,12 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.8.1 h1:zrGxLwffKM8nVxBvaJa7H404eQLfqlg1GB6YVIzXVQ0=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
@@ -951,6 +960,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/open-policy-agent/conftest v0.23.0 h1:i/cmUjNKDz973vR1cm+x3DqTei/jBPosPvjeot6+p9M=
github.com/open-policy-agent/conftest v0.23.0/go.mod h1:NA6+vKd93pb04H9jiV3WRGJKLj/pzYdQg7XCdoPPUDI=
github.com/open-policy-agent/opa v0.25.2 h1:zTQuUMvB5xkYixKB9LFVbUd7DcUt1jfS0QKTo+/Vfyc=
github.com/open-policy-agent/opa v0.25.2/go.mod h1:iGThTRECCfKQKICueOZkXUi0opN7BR3qiAnIrNHCmlI=
@@ -1177,6 +1187,7 @@ github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPf
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmccombs/hcl2json v0.3.1 h1:Pf+Lb9OpZ5lkQuIC0BB5txdCQskZ2ud/l8sz/Nkjf3A=
github.com/tmccombs/hcl2json v0.3.1/go.mod h1:ljY0/prd2IFUF3cagQjV3cpPEEQKzqyGqnKI7m5DBVY=
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
@@ -1201,7 +1212,6 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
@@ -1243,6 +1253,7 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.6.1 h1:wHtZ+LSSQVwUSb+XIJ5E9hgAQxyWATZsAWT+ESJ9dQ0=
github.com/zclconf/go-cty v1.6.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=

View File

@@ -1,7 +1,6 @@
[
{
"Target": "testdata/fixtures/opensuse-leap-423.tar.gz (opensuse.leap 42.3)",
"Type": "opensuse.leap",
"Vulnerabilities": null
"Type": "opensuse.leap"
}
]

8
pkg/cache/remote.go vendored
View File

@@ -30,8 +30,8 @@ func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ArtifactCach
}
// PutArtifact sends artifact to remote client
func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) error {
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, imageInfo))
func (c RemoteCache) PutArtifact(imageID string, artifactInfo types.ArtifactInfo) error {
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, artifactInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
@@ -39,8 +39,8 @@ func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) e
}
// PutBlob sends blobInfo to remote client
func (c RemoteCache) PutBlob(diffID string, layerInfo types.BlobInfo) error {
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, layerInfo))
func (c RemoteCache) PutBlob(diffID string, blobInfo types.BlobInfo) error {
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, blobInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}

View File

@@ -12,12 +12,13 @@ import (
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/commands/artifact"
"github.com/aquasecurity/trivy/pkg/commands/client"
"github.com/aquasecurity/trivy/pkg/commands/plugin"
"github.com/aquasecurity/trivy/pkg/commands/server"
tdb "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnerability"
)
@@ -56,7 +57,7 @@ var (
severityFlag = cli.StringFlag{
Name: "severity",
Aliases: []string{"s"},
Value: strings.Join(types.SeverityNames, ","),
Value: strings.Join(dbTypes.SeverityNames, ","),
Usage: "severities of vulnerabilities to be displayed (comma separated)",
EnvVars: []string{"TRIVY_SEVERITY"},
}
@@ -134,11 +135,19 @@ var (
vulnTypeFlag = cli.StringFlag{
Name: "vuln-type",
Value: "os,library",
Value: strings.Join([]string{types.VulnTypeOS, types.VulnTypeLibrary}, ","),
Usage: "comma-separated list of vulnerability types (os,library)",
EnvVars: []string{"TRIVY_VULN_TYPE"},
}
securityChecksFlag = cli.StringFlag{
Name: "security-checks",
Value: types.SecurityCheckVulnerability,
Usage: "comma-separated list of what security issues to detect (vuln,config)",
EnvVars: []string{"TRIVY_SECURITY_CHECKS"},
Hidden: true,
}
cacheDirFlag = cli.StringFlag{
Name: "cache-dir",
Value: utils.DefaultCacheDir(),
@@ -231,6 +240,7 @@ var (
&ignoreUnfixedFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag,
&timeoutFlag,
&lightFlag,
@@ -405,6 +415,7 @@ func NewFilesystemCommand() *cli.Command {
&ignoreUnfixedFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag,
&cacheBackendFlag,
&timeoutFlag,
@@ -437,6 +448,7 @@ func NewRepositoryCommand() *cli.Command {
&ignoreUnfixedFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag,
&cacheBackendFlag,
&timeoutFlag,
@@ -468,6 +480,7 @@ func NewClientCommand() *cli.Command {
&ignoreUnfixedFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag,
&timeoutFlag,
&ignorePolicy,
@@ -519,9 +532,10 @@ func NewServerCommand() *cli.Command {
// NewPluginCommand is the factory method to add plugin command
func NewPluginCommand() *cli.Command {
return &cli.Command{
Name: "plugin",
Aliases: []string{"p"},
Usage: "manage plugins",
Name: "plugin",
Aliases: []string{"p"},
ArgsUsage: "plugin_uri",
Usage: "manage plugins",
Subcommands: cli.Commands{
{
Name: "install",

View File

@@ -1,88 +0,0 @@
package artifact
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/commands/config"
)
// Config holds the artifact config
type Config struct {
config.GlobalConfig
config.ArtifactConfig
config.DBConfig
config.ImageConfig
config.ReportConfig
config.CacheConfig
// deprecated
onlyUpdate string
// deprecated
refresh bool
// deprecated
autoRefresh bool
}
// NewConfig is the factory method to return config
func NewConfig(c *cli.Context) (Config, error) {
gc, err := config.NewGlobalConfig(c)
if err != nil {
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
}
return Config{
GlobalConfig: gc,
ArtifactConfig: config.NewArtifactConfig(c),
DBConfig: config.NewDBConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),
CacheConfig: config.NewCacheConfig(c),
onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),
autoRefresh: c.Bool("auto-refresh"),
}, nil
}
// Init initializes the artifact config
func (c *Config) Init() error {
if c.onlyUpdate != "" || c.refresh || c.autoRefresh {
c.Logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
}
if err := c.initPreScanConfigs(); err != nil {
return err
}
// --clear-cache, --download-db-only and --reset don't conduct the scan
if c.skipScan() {
return nil
}
if err := c.ArtifactConfig.Init(c.Context, c.Logger); err != nil {
return err
}
return nil
}
func (c *Config) initPreScanConfigs() error {
if err := c.ReportConfig.Init(c.Logger); err != nil {
return err
}
if err := c.DBConfig.Init(); err != nil {
return err
}
if err := c.CacheConfig.Init(); err != nil {
return err
}
return nil
}
func (c *Config) skipScan() bool {
if c.ClearCache || c.DownloadDBOnly || c.Reset {
return true
}
return false
}

View File

@@ -8,13 +8,14 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
_ time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled)
_ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled, opt)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
@@ -23,15 +24,15 @@ func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
// FilesystemRun runs scan on filesystem
func FilesystemRun(ctx *cli.Context) error {
c, err := NewConfig(ctx)
opt, err := NewOption(ctx)
if err != nil {
return err
return xerrors.Errorf("option error: %w", err)
}
// initialize config
if err = c.Init(); err != nil {
// initialize options
if err = opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
return run(ctx.Context, c, filesystemScanner)
return Run(ctx.Context, opt, filesystemScanner, initFSCache)
}

View File

@@ -8,13 +8,14 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled)
timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled, opt)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
@@ -22,9 +23,8 @@ func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, l
}
func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
timeout time.Duration, disabled []analyzer.Type) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled)
timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled, opt)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
}
@@ -33,20 +33,20 @@ func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache
// ImageRun runs scan on docker image
func ImageRun(ctx *cli.Context) error {
c, err := NewConfig(ctx)
opt, err := NewOption(ctx)
if err != nil {
return err
return xerrors.Errorf("option error: %w", err)
}
// initialize config
if err = c.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
// initialize options
if err = opt.Init(); err != nil {
return xerrors.Errorf("option initialize error: %w", err)
}
if c.Input != "" {
if opt.Input != "" {
// scan tar file
return run(ctx.Context, c, archiveScanner)
return Run(ctx.Context, opt, archiveScanner, initFSCache)
}
return run(ctx.Context, c, dockerScanner)
return Run(ctx.Context, opt, dockerScanner, initFSCache)
}

View File

@@ -9,38 +9,41 @@ import (
"github.com/google/wire"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability"
)
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (
scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (
scanner.Scanner, error) {
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil
}
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
func initializeResultClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}
}

View File

@@ -0,0 +1,88 @@
package artifact
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/commands/option"
)
// Option holds the artifact options
type Option struct {
option.GlobalOption
option.ArtifactOption
option.DBOption
option.ImageOption
option.ReportOption
option.CacheOption
// deprecated
onlyUpdate string
// deprecated
refresh bool
// deprecated
autoRefresh bool
}
// NewOption is the factory method to return options
func NewOption(c *cli.Context) (Option, error) {
gc, err := option.NewGlobalOption(c)
if err != nil {
return Option{}, xerrors.Errorf("failed to initialize global options: %w", err)
}
return Option{
GlobalOption: gc,
ArtifactOption: option.NewArtifactOption(c),
DBOption: option.NewDBOption(c),
ImageOption: option.NewImageOption(c),
ReportOption: option.NewReportOption(c),
CacheOption: option.NewCacheOption(c),
onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),
autoRefresh: c.Bool("auto-refresh"),
}, nil
}
// Init initializes the artifact options
func (c *Option) Init() error {
if c.onlyUpdate != "" || c.refresh || c.autoRefresh {
c.Logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
}
if err := c.initPreScanOptions(); err != nil {
return err
}
// --clear-cache, --download-db-only and --reset don't conduct the scan
if c.skipScan() {
return nil
}
if err := c.ArtifactOption.Init(c.Context, c.Logger); err != nil {
return err
}
return nil
}
func (c *Option) initPreScanOptions() error {
if err := c.ReportOption.Init(c.Logger); err != nil {
return err
}
if err := c.DBOption.Init(); err != nil {
return err
}
if err := c.CacheOption.Init(); err != nil {
return err
}
return nil
}
func (c *Option) skipScan() bool {
if c.ClearCache || c.DownloadDBOnly || c.Reset {
return true
}
return false
}

View File

@@ -12,53 +12,48 @@ import (
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestConfig_Init(t *testing.T) {
func TestOption_Init(t *testing.T) {
tests := []struct {
name string
globalConfig config.GlobalConfig
dbConfig config.DBConfig
imageConfig config.ImageConfig
reportConfig config.ReportConfig
args []string
logs []string
want Config
wantErr string
name string
args []string
logs []string
want Option
wantErr string
}{
{
name: "happy path",
reportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
},
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
want: Config{
GlobalConfig: config.GlobalConfig{
want: Option{
GlobalOption: option.GlobalOption{
Quiet: true,
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.10",
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Output: os.Stdout,
},
},
},
{
name: "happy path: reset",
args: []string{"--reset"},
want: Config{
DBConfig: config.DBConfig{
want: Option{
DBOption: option.DBOption{
Reset: true,
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
},
@@ -68,13 +63,14 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "centos:7",
},
},
@@ -85,13 +81,14 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "debian:buster",
},
onlyUpdate: "alpine",
@@ -103,14 +100,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Template: "@contrib/gitlab.tpl",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
@@ -121,15 +119,16 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--template is ignored because --format json is specified. Use --template option with --format template option.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
Format: "json",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Template: "@contrib/gitlab.tpl",
Format: "json",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
@@ -140,14 +139,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
@@ -181,6 +181,7 @@ func TestConfig_Init(t *testing.T) {
set.Bool("auto-refresh", false, "")
set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "")
set.String("security-checks", "vuln", "")
set.String("only-update", "", "")
set.String("template", "", "")
set.String("format", "", "")
@@ -188,10 +189,10 @@ func TestConfig_Init(t *testing.T) {
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c, err := NewConfig(ctx)
c, err := NewOption(ctx)
require.NoError(t, err, err)
c.GlobalConfig.Logger = logger.Sugar()
c.GlobalOption.Logger = logger.Sugar()
err = c.Init()
// tests log messages
@@ -211,8 +212,8 @@ func TestConfig_Init(t *testing.T) {
assert.NoError(t, err, tt.name)
}
tt.want.GlobalConfig.Context = ctx
tt.want.GlobalConfig.Logger = logger.Sugar()
tt.want.GlobalOption.Context = ctx
tt.want.GlobalOption.Logger = logger.Sugar()
assert.Equal(t, tt.want, c, tt.name)
})
}

View File

@@ -8,14 +8,14 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner"
)
func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
_ time.Duration, disabled []analyzer.Type) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled)
_ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled, opt)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
@@ -24,15 +24,15 @@ func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
// RepositoryRun runs scan on repository
func RepositoryRun(ctx *cli.Context) error {
c, err := NewConfig(ctx)
opt, err := NewOption(ctx)
if err != nil {
return err
return xerrors.Errorf("option error: %w", err)
}
// initialize config
if err = c.Init(); err != nil {
// initialize options
if err = opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
return run(ctx.Context, c, repositoryScanner)
return Run(ctx.Context, opt, repositoryScanner, initFSCache)
}

View File

@@ -3,13 +3,13 @@ package artifact
import (
"context"
"errors"
l "log"
"os"
"time"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/commands/operation"
@@ -24,25 +24,25 @@ var errSkipScan = errors.New("skip subsequent processes")
// InitializeScanner type to define initialize function signature
type InitializeScanner func(context.Context, string, cache.ArtifactCache, cache.LocalArtifactCache, time.Duration,
[]analyzer.Type) (scanner.Scanner, func(), error)
[]analyzer.Type, config.ScannerOption) (scanner.Scanner, func(), error)
func run(ctx context.Context, conf Config, initializeScanner InitializeScanner) error {
ctx, cancel := context.WithTimeout(ctx, conf.Timeout)
// InitCache defines cache initializer
type InitCache func(c Option) (cache.Cache, error)
// Run performs artifact scanning
func Run(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error {
ctx, cancel := context.WithTimeout(ctx, opt.Timeout)
defer cancel()
err := runWithTimeout(ctx, conf, initializeScanner)
if xerrors.Is(err, context.DeadlineExceeded) {
log.Logger.Warn("Increase --timeout value")
}
return err
return runWithTimeout(ctx, opt, initializeScanner, initCache)
}
func runWithTimeout(ctx context.Context, conf Config, initializeScanner InitializeScanner) error {
if err := log.InitLogger(conf.Debug, conf.Quiet); err != nil {
l.Fatal(err)
func runWithTimeout(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error {
if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil {
return err
}
cacheClient, err := initCache(conf)
cacheClient, err := initCache(opt)
if err != nil {
if errors.Is(err, errSkipScan) {
return nil
@@ -51,34 +51,37 @@ func runWithTimeout(ctx context.Context, conf Config, initializeScanner Initiali
}
defer cacheClient.Close()
if err = initDB(conf); err != nil {
if errors.Is(err, errSkipScan) {
return nil
// When scanning config files, it doesn't need to download the vulnerability database.
if utils.StringInSlice(types.SecurityCheckVulnerability, opt.SecurityChecks) {
if err = initDB(opt); err != nil {
if errors.Is(err, errSkipScan) {
return nil
}
return xerrors.Errorf("DB error: %w", err)
}
return xerrors.Errorf("DB error: %w", err)
defer db.Close()
}
defer db.Close()
results, err := scan(ctx, conf, initializeScanner, cacheClient)
results, err := scan(ctx, opt, initializeScanner, cacheClient)
if err != nil {
return xerrors.Errorf("scan error: %w", err)
}
results, err = filter(ctx, conf, results)
results, err = filter(ctx, opt, results)
if err != nil {
return xerrors.Errorf("filter error: %w", err)
}
if err = report.WriteResults(conf.Format, conf.Output, conf.Severities, results, conf.Template, conf.Light); err != nil {
if err = report.WriteResults(opt.Format, opt.Output, opt.Severities, results, opt.Template, opt.Light); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
exit(conf, results)
exit(opt, results)
return nil
}
func initCache(c Config) (operation.Cache, error) {
func initFSCache(c Option) (cache.Cache, error) {
utils.SetCacheDir(c.CacheDir)
cache, err := operation.NewCache(c.CacheBackend)
if err != nil {
@@ -95,7 +98,7 @@ func initCache(c Config) (operation.Cache, error) {
}
if c.ClearCache {
defer cache.Close()
if err = cache.ClearImages(); err != nil {
if err = cache.ClearArtifacts(); err != nil {
return operation.Cache{}, xerrors.Errorf("cache clear error: %w", err)
}
return operation.Cache{}, errSkipScan
@@ -103,7 +106,7 @@ func initCache(c Config) (operation.Cache, error) {
return cache, nil
}
func initDB(c Config) error {
func initDB(c Option) error {
// download the database file
noProgress := c.Quiet || c.NoProgress
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.Light, c.SkipUpdate); err != nil {
@@ -120,29 +123,36 @@ func initDB(c Config) error {
return nil
}
func scan(ctx context.Context, conf Config, initializeScanner InitializeScanner, cacheClient cache.Cache) (
func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner, cacheClient cache.Cache) (
report.Results, error) {
target := conf.Target
if conf.Input != "" {
target = conf.Input
target := opt.Target
if opt.Input != "" {
target = opt.Input
}
scanOptions := types.ScanOptions{
VulnType: conf.VulnType,
ScanRemovedPackages: conf.ScanRemovedPkgs, // this is valid only for image subcommand
ListAllPackages: conf.ListAllPkgs,
SkipFiles: conf.SkipFiles,
SkipDirs: conf.SkipDirs,
VulnType: opt.VulnType,
SecurityChecks: opt.SecurityChecks,
ScanRemovedPackages: opt.ScanRemovedPkgs, // this is valid only for image subcommand
ListAllPackages: opt.ListAllPkgs,
SkipFiles: opt.SkipFiles,
SkipDirs: opt.SkipDirs,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
// It doesn't analyze apk commands by default.
disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand}
if conf.ScanRemovedPkgs {
if opt.ScanRemovedPkgs {
disabledAnalyzers = []analyzer.Type{}
}
s, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, conf.Timeout, disabledAnalyzers)
// TODO: fix the scanner option and enable config analyzers once we finalize the specification of config scanning.
configScannerOptions := config.ScannerOption{}
disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeYaml, analyzer.TypeTOML, analyzer.TypeJSON,
analyzer.TypeDockerfile, analyzer.TypeHCL)
s, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, opt.Timeout,
disabledAnalyzers, configScannerOptions)
if err != nil {
return nil, xerrors.Errorf("unable to initialize a scanner: %w", err)
}
@@ -155,12 +165,12 @@ func scan(ctx context.Context, conf Config, initializeScanner InitializeScanner,
return results, nil
}
func filter(ctx context.Context, conf Config, results report.Results) (report.Results, error) {
vulnClient := initializeVulnerabilityClient()
func filter(ctx context.Context, opt Option, results report.Results) (report.Results, error) {
resultClient := initializeResultClient()
for i := range results {
vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
conf.Severities, conf.IgnoreUnfixed, conf.IgnoreFile, conf.IgnorePolicy)
resultClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
vulns, err := resultClient.Filter(ctx, results[i].Vulnerabilities,
opt.Severities, opt.IgnoreUnfixed, opt.IgnoreFile, opt.IgnorePolicy)
if err != nil {
return nil, xerrors.Errorf("unable to filter vulnerabilities: %w", err)
}
@@ -169,12 +179,8 @@ func filter(ctx context.Context, conf Config, results report.Results) (report.Re
return results, nil
}
func exit(c Config, results report.Results) {
if c.ExitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(c.ExitCode)
}
}
func exit(c Option, results report.Results) {
if c.ExitCode != 0 && results.Failed() {
os.Exit(c.ExitCode)
}
}

View File

@@ -8,6 +8,7 @@ package artifact
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/applier"
image2 "github.com/aquasecurity/fanal/artifact/image"
local2 "github.com/aquasecurity/fanal/artifact/local"
@@ -25,7 +26,7 @@ import (
// Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
@@ -37,14 +38,18 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil {
return scanner.Scanner{}, nil, err
}
artifact := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers)
artifact, err := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers, configScannerOption)
if err != nil {
cleanup()
return scanner.Scanner{}, nil, err
}
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
cleanup()
}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (scanner.Scanner, error) {
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
@@ -52,26 +57,32 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
if err != nil {
return scanner.Scanner{}, err
}
artifact := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers)
artifact, err := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers, configScannerOption)
if err != nil {
return scanner.Scanner{}, err
}
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, nil
}
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
artifact := local2.NewArtifact(dir, artifactCache, disableAnalyzers)
artifact, err := local2.NewArtifact(dir, artifactCache, disableAnalyzers, configScannerOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
}, nil
}
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector)
artifact, cleanup, err := remote.NewArtifact(url, artifactCache, disableAnalyzers)
artifact, cleanup, err := remote.NewArtifact(url, artifactCache, disableAnalyzers, configScannerOption)
if err != nil {
return scanner.Scanner{}, nil, err
}
@@ -81,8 +92,8 @@ func initializeRepositoryScanner(ctx context.Context, url string, artifactCache
}, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
client := vulnerability.NewClient(config)
func initializeResultClient() vulnerability.Client {
dbConfig := db.Config{}
client := vulnerability.NewClient(dbConfig)
return client
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/google/wire"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
@@ -16,18 +17,20 @@ import (
)
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (
scanner.Scanner, func(), error) {
wire.Build(scanner.RemoteDockerSet)
return scanner.Scanner{}, nil, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, error) {
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type,
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
wire.Build(scanner.RemoteArchiveSet)
return scanner.Scanner{}, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
func initializeResultClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}
}

View File

@@ -7,15 +7,15 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
)
// Config holds the Trivy client config
type Config struct {
config.GlobalConfig
config.ArtifactConfig
config.ImageConfig
config.ReportConfig
// Option holds the Trivy client options
type Option struct {
option.GlobalOption
option.ArtifactOption
option.ImageOption
option.ReportOption
RemoteAddr string
token string
@@ -26,18 +26,18 @@ type Config struct {
CustomHeaders http.Header
}
// NewConfig is the factory method for Config
func NewConfig(c *cli.Context) (Config, error) {
gc, err := config.NewGlobalConfig(c)
// NewOption is the factory method for Option
func NewOption(c *cli.Context) (Option, error) {
gc, err := option.NewGlobalOption(c)
if err != nil {
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
return Option{}, xerrors.Errorf("failed to initialize global options: %w", err)
}
return Config{
GlobalConfig: gc,
ArtifactConfig: config.NewArtifactConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),
return Option{
GlobalOption: gc,
ArtifactOption: option.NewArtifactOption(c),
ImageOption: option.NewImageOption(c),
ReportOption: option.NewReportOption(c),
RemoteAddr: c.String("remote"),
token: c.String("token"),
tokenHeader: c.String("token-header"),
@@ -45,8 +45,8 @@ func NewConfig(c *cli.Context) (Config, error) {
}, nil
}
// Init initializes the config
func (c *Config) Init() (err error) {
// Init initializes the options
func (c *Option) Init() (err error) {
// --clear-cache doesn't conduct the scan
if c.ClearCache {
return nil
@@ -59,11 +59,11 @@ func (c *Config) Init() (err error) {
c.CustomHeaders.Set(c.tokenHeader, c.token)
}
if err := c.ReportConfig.Init(c.Logger); err != nil {
if err := c.ReportOption.Init(c.Logger); err != nil {
return err
}
if err := c.ArtifactConfig.Init(c.Context, c.Logger); err != nil {
if err := c.ArtifactOption.Init(c.Context, c.Logger); err != nil {
return err
}

View File

@@ -4,7 +4,6 @@ import (
"flag"
"net/http"
"os"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
@@ -14,38 +13,33 @@ import (
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestConfig_Init(t *testing.T) {
tests := []struct {
name string
globalConfig config.GlobalConfig
imageConfig config.ImageConfig
reportConfig config.ReportConfig
args []string
logs []string
want Config
wantErr string
name string
args []string
logs []string
want Option
wantErr string
}{
{
name: "happy path",
reportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
},
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
want: Config{
GlobalConfig: config.GlobalConfig{
want: Option{
GlobalOption: option.GlobalOption{
Quiet: true,
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.10",
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Output: os.Stdout,
},
CustomHeaders: http.Header{},
},
@@ -53,13 +47,14 @@ func TestConfig_Init(t *testing.T) {
{
name: "happy path with token and token header",
args: []string{"--token", "secret", "--token-header", "X-Trivy-Token", "alpine:3.11"},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.11",
},
token: "secret",
@@ -72,13 +67,14 @@ func TestConfig_Init(t *testing.T) {
{
name: "happy path with good custom headers",
args: []string{"--custom-headers", "foo:bar", "alpine:3.11"},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.11",
},
customHeaders: []string{"foo:bar"},
@@ -90,13 +86,14 @@ func TestConfig_Init(t *testing.T) {
{
name: "happy path with bad custom headers",
args: []string{"--custom-headers", "foobaz", "alpine:3.11"},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "alpine:3.11",
},
customHeaders: []string{"foobaz"},
@@ -109,13 +106,14 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{"os", "library"},
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "centos:7",
},
CustomHeaders: http.Header{},
@@ -127,14 +125,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Template: "@contrib/gitlab.tpl",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
@@ -146,15 +145,16 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--template is ignored because --format json is specified. Use --template option with --format template option.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
Format: "json",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Template: "@contrib/gitlab.tpl",
Format: "json",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
@@ -166,14 +166,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
@@ -185,14 +186,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
},
want: Config{
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{"os", "library"},
Format: "template",
want: Option{
ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout,
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "template",
},
ArtifactConfig: config.ArtifactConfig{
ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
CustomHeaders: http.Header{},
@@ -219,6 +221,7 @@ func TestConfig_Init(t *testing.T) {
set.Bool("clear-cache", false, "")
set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "")
set.String("security-checks", "vuln", "")
set.String("template", "", "")
set.String("format", "", "")
set.String("token", "", "")
@@ -228,10 +231,10 @@ func TestConfig_Init(t *testing.T) {
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c, err := NewConfig(ctx)
c, err := NewOption(ctx)
require.NoError(t, err, err)
c.GlobalConfig.Logger = logger.Sugar()
c.GlobalOption.Logger = logger.Sugar()
err = c.Init()
// tests log messages
@@ -251,8 +254,8 @@ func TestConfig_Init(t *testing.T) {
assert.NoError(t, err, tt.name)
}
tt.want.GlobalConfig.Context = ctx
tt.want.GlobalConfig.Logger = logger.Sugar()
tt.want.GlobalOption.Context = ctx
tt.want.GlobalOption.Logger = logger.Sugar()
assert.Equal(t, tt.want, c, tt.name)
})
}
@@ -280,9 +283,8 @@ func Test_splitCustomHeaders(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := splitCustomHeaders(tt.args.headers); !reflect.DeepEqual(got, tt.want) {
t.Errorf("splitCustomHeaders() = %v, want %v", got, tt.want)
}
got := splitCustomHeaders(tt.args.headers)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -8,6 +8,7 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
@@ -18,44 +19,42 @@ import (
)
// Run runs the scan
func Run(ctx *cli.Context) error {
c, err := NewConfig(ctx)
func Run(cliCtx *cli.Context) error {
opt, err := NewOption(cliCtx)
if err != nil {
return err
return xerrors.Errorf("option error: %w", err)
}
return run(ctx.Context, c)
}
func run(ctx context.Context, conf Config) error {
ctx, cancel := context.WithTimeout(context.Background(), conf.Timeout)
ctx, cancel := context.WithTimeout(cliCtx.Context, opt.Timeout)
defer cancel()
err := runWithTimeout(ctx, conf)
err = runWithTimeout(ctx, opt)
if xerrors.Is(err, context.DeadlineExceeded) {
log.Logger.Warn("Increase --timeout value")
}
return err
}
func runWithTimeout(ctx context.Context, conf Config) error {
if err := initialize(&conf); err != nil {
func runWithTimeout(ctx context.Context, opt Option) error {
if err := initialize(&opt); err != nil {
return xerrors.Errorf("initialize error: %w", err)
}
if conf.ClearCache {
if opt.ClearCache {
log.Logger.Warn("A client doesn't have image cache")
return nil
}
s, cleanup, err := initializeScanner(ctx, conf)
s, cleanup, err := initializeScanner(ctx, opt)
if err != nil {
return xerrors.Errorf("scanner initialize error: %w", err)
}
defer cleanup()
scanOptions := types.ScanOptions{
VulnType: conf.VulnType,
ScanRemovedPackages: conf.ScanRemovedPkgs,
VulnType: opt.VulnType,
SecurityChecks: opt.SecurityChecks,
ScanRemovedPackages: opt.ScanRemovedPkgs,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
@@ -64,56 +63,61 @@ func runWithTimeout(ctx context.Context, conf Config) error {
return xerrors.Errorf("error in image scan: %w", err)
}
vulnClient := initializeVulnerabilityClient()
resultClient := initializeResultClient()
for i := range results {
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
conf.Severities, conf.IgnoreUnfixed, conf.IgnoreFile, conf.IgnorePolicy)
vulns, err := resultClient.Filter(ctx, results[i].Vulnerabilities,
opt.Severities, opt.IgnoreUnfixed, opt.IgnoreFile, opt.IgnorePolicy)
if err != nil {
return err
return xerrors.Errorf("filter error: %w", err)
}
results[i].Vulnerabilities = vulns
}
if err = report.WriteResults(conf.Format, conf.Output, conf.Severities, results, conf.Template, false); err != nil {
if err = report.WriteResults(opt.Format, opt.Output, opt.Severities, results, opt.Template, false); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
exit(conf, results)
exit(opt, results)
return nil
}
func initialize(conf *Config) error {
func initialize(opt *Option) error {
// Initialize logger
if err := log.InitLogger(conf.Debug, conf.Quiet); err != nil {
if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil {
return xerrors.Errorf("failed to initialize a logger: %w", err)
}
// Initialize config
if err := conf.Init(); err != nil {
// Initialize options
if err := opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}
// configure cache dir
utils.SetCacheDir(conf.CacheDir)
utils.SetCacheDir(opt.CacheDir)
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
return nil
}
func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(), error) {
remoteCache := cache.NewRemoteCache(cache.RemoteURL(conf.RemoteAddr), conf.CustomHeaders)
func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func(), error) {
remoteCache := cache.NewRemoteCache(cache.RemoteURL(opt.RemoteAddr), opt.CustomHeaders)
// By default, apk commands are not analyzed.
disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand}
if conf.ScanRemovedPkgs {
if opt.ScanRemovedPkgs {
disabledAnalyzers = []analyzer.Type{}
}
if conf.Input != "" {
// TODO: fix the scanner option and enable config analyzers once we finalize the specification of config scanning.
configScannerOptions := config.ScannerOption{}
disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeYaml, analyzer.TypeTOML, analyzer.TypeJSON,
analyzer.TypeDockerfile, analyzer.TypeHCL)
if opt.Input != "" {
// Scan tar file
s, err := initializeArchiveScanner(ctx, conf.Input, remoteCache,
client.CustomHeaders(conf.CustomHeaders), client.RemoteURL(conf.RemoteAddr), conf.Timeout, disabledAnalyzers)
s, err := initializeArchiveScanner(ctx, opt.Input, remoteCache, client.CustomHeaders(opt.CustomHeaders),
client.RemoteURL(opt.RemoteAddr), opt.Timeout, disabledAnalyzers, configScannerOptions)
if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
@@ -121,8 +125,8 @@ func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(
}
// Scan an image in Docker Engine or Docker Registry
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, remoteCache,
client.CustomHeaders(conf.CustomHeaders), client.RemoteURL(conf.RemoteAddr), conf.Timeout, disabledAnalyzers)
s, cleanup, err := initializeDockerScanner(ctx, opt.Target, remoteCache, client.CustomHeaders(opt.CustomHeaders),
client.RemoteURL(opt.RemoteAddr), opt.Timeout, disabledAnalyzers, configScannerOptions)
if err != nil {
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err)
}
@@ -130,7 +134,7 @@ func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(
return s, cleanup, nil
}
func exit(c Config, results report.Results) {
func exit(c Option, results report.Results) {
if c.ExitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {

View File

@@ -8,6 +8,7 @@ package client
import (
"context"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
image2 "github.com/aquasecurity/fanal/artifact/image"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image"
@@ -21,7 +22,7 @@ import (
// Injectors from inject.go:
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
dockerOption, err := types.GetDockerOption(timeout)
@@ -32,27 +33,34 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil {
return scanner.Scanner{}, nil, err
}
artifact := image2.NewArtifact(imageImage, artifactCache, disabled)
artifact, err := image2.NewArtifact(imageImage, artifactCache, disabled, configScannerOption)
if err != nil {
cleanup()
return scanner.Scanner{}, nil, err
}
scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, func() {
cleanup()
}, nil
}
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, error) {
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner)
imageImage, err := image.NewArchiveImage(filePath)
if err != nil {
return scanner.Scanner{}, err
}
artifact := image2.NewArtifact(imageImage, artifactCache, disabled)
artifact, err := image2.NewArtifact(imageImage, artifactCache, disabled, configScannerOption)
if err != nil {
return scanner.Scanner{}, err
}
scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, nil
}
func initializeVulnerabilityClient() vulnerability.Client {
config := db.Config{}
vulnerabilityClient := vulnerability.NewClient(config)
func initializeResultClient() vulnerability.Client {
dbConfig := db.Config{}
vulnerabilityClient := vulnerability.NewClient(dbConfig)
return vulnerabilityClient
}

View File

@@ -1,19 +0,0 @@
package config
import (
"github.com/urfave/cli/v2"
)
// ImageConfig holds the config for scanning images
type ImageConfig struct {
ScanRemovedPkgs bool
ListAllPkgs bool
}
// NewImageConfig is the factory method to return imageConfig
func NewImageConfig(c *cli.Context) ImageConfig {
return ImageConfig{
ScanRemovedPkgs: c.Bool("removed-pkgs"),
ListAllPkgs: c.Bool("list-all-pkgs"),
}
}

View File

@@ -1,92 +0,0 @@
package config
import (
"os"
"strings"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
)
// ReportConfig holds the config for reporting scan results
type ReportConfig struct {
Format string
Template string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
IgnorePolicy string
// these variables are not exported
vulnType string
output string
severities string
// these variables are populated by Init()
VulnType []string
Output *os.File
Severities []dbTypes.Severity
}
// NewReportConfig is the factory method to return ReportConfig
func NewReportConfig(c *cli.Context) ReportConfig {
return ReportConfig{
output: c.String("output"),
Format: c.String("format"),
Template: c.String("template"),
IgnorePolicy: c.String("ignore-policy"),
vulnType: c.String("vuln-type"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
}
}
// Init initializes the ReportConfig
func (c *ReportConfig) Init(logger *zap.SugaredLogger) (err error) {
if c.Template != "" {
if c.Format == "" {
logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
} else if c.Format != "template" {
logger.Warnf("--template is ignored because --format %s is specified. Use --template option with --format template option.", c.Format)
}
}
if c.Format == "template" && c.Template == "" {
logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
}
c.Severities = c.splitSeverity(logger, c.severities)
c.VulnType = strings.Split(c.vulnType, ",")
// for testability
c.severities = ""
c.vulnType = ""
c.Output = os.Stdout
if c.output != "" {
if c.Output, err = os.Create(c.output); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
return nil
}
func (c *ReportConfig) splitSeverity(logger *zap.SugaredLogger, severity string) []dbTypes.Severity {
logger.Debugf("Severities: %s", severity)
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -1,162 +0,0 @@
package config
import (
"flag"
"os"
"testing"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
func TestReportReportConfig_Init(t *testing.T) {
type fields struct {
output string
Format string
Template string
vulnType string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
VulnType []string
Output *os.File
Severities []dbTypes.Severity
}
tests := []struct {
name string
fields fields
args []string
logs []string
want ReportConfig
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
},
args: []string{"alpine:3.10"},
want: ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"},
Output: os.Stdout,
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
vulnType: "os,library",
},
args: []string{"centos:7"},
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
VulnType: []string{"os", "library"},
Output: os.Stdout,
},
},
{
name: "invalid option combination: --template enabled without --format",
fields: fields{
Template: "@contrib/gitlab.tpl",
severities: "LOW",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
},
want: ReportConfig{
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Template: "@contrib/gitlab.tpl",
VulnType: []string{""},
},
},
{
name: "invalid option combination: --template and --format json",
fields: fields{
Format: "json",
Template: "@contrib/gitlab.tpl",
severities: "LOW",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--template is ignored because --format json is specified. Use --template option with --format template option.",
},
want: ReportConfig{
Format: "json",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Template: "@contrib/gitlab.tpl",
VulnType: []string{""},
},
},
{
name: "invalid option combination: --format template without --template",
fields: fields{
Format: "template",
severities: "LOW",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
},
want: ReportConfig{
Format: "template",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
VulnType: []string{""},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
core, obs := observer.New(zap.InfoLevel)
logger := zap.New(core)
set := flag.NewFlagSet("test", 0)
_ = set.Parse(tt.args)
c := &ReportConfig{
output: tt.fields.output,
Format: tt.fields.Format,
Template: tt.fields.Template,
vulnType: tt.fields.vulnType,
severities: tt.fields.severities,
IgnoreFile: tt.fields.IgnoreFile,
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
ExitCode: tt.fields.ExitCode,
Output: tt.fields.Output,
}
err := c.Init(logger.Sugar())
// tests log messages
var gotMessages []string
for _, entry := range obs.AllUntimed() {
gotMessages = append(gotMessages, entry.Message)
}
assert.Equal(t, tt.logs, gotMessages, tt.name)
// test the error
switch {
case tt.wantErr != "":
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
default:
assert.NoError(t, err, tt.name)
}
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

View File

@@ -51,8 +51,8 @@ func (c Cache) Reset() (err error) {
if err := c.ClearDB(); err != nil {
return xerrors.Errorf("failed to clear the database: %w", err)
}
if err := c.ClearImages(); err != nil {
return xerrors.Errorf("failed to clear the image cache: %w", err)
if err := c.ClearArtifacts(); err != nil {
return xerrors.Errorf("failed to clear the artifact cache: %w", err)
}
return nil
}
@@ -66,9 +66,9 @@ func (c Cache) ClearDB() (err error) {
return nil
}
// ClearImages clears the cache images
func (c Cache) ClearImages() error {
log.Logger.Info("Removing image caches...")
// ClearArtifacts clears the artifact cache
func (c Cache) ClearArtifacts() error {
log.Logger.Info("Removing artifact caches...")
if err := c.Clear(); err != nil {
return xerrors.Errorf("failed to remove the cache: %w", err)
}
@@ -96,7 +96,7 @@ func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
}
// for debug
if err := showDBInfo(cacheDir); err != nil {
if err = showDBInfo(cacheDir); err != nil {
return xerrors.Errorf("failed to show database info: %w", err)
}
return nil

View File

@@ -1,4 +1,4 @@
package config
package option
import (
"os"
@@ -9,8 +9,8 @@ import (
"golang.org/x/xerrors"
)
// ArtifactConfig holds the config for a artifact scanning
type ArtifactConfig struct {
// ArtifactOption holds the options for an artifact scanning
type ArtifactOption struct {
Input string
Timeout time.Duration
ClearCache bool
@@ -22,9 +22,9 @@ type ArtifactConfig struct {
Target string
}
// NewArtifactConfig is the factory method to return artifact config
func NewArtifactConfig(c *cli.Context) ArtifactConfig {
return ArtifactConfig{
// NewArtifactOption is the factory method to return artifact option
func NewArtifactOption(c *cli.Context) ArtifactOption {
return ArtifactOption{
Input: c.String("input"),
Timeout: c.Duration("timeout"),
ClearCache: c.Bool("clear-cache"),
@@ -34,7 +34,7 @@ func NewArtifactConfig(c *cli.Context) ArtifactConfig {
}
// Init initialize the CLI context for artifact scanning
func (c *ArtifactConfig) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
func (c *ArtifactOption) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
if c.Input == "" && ctx.Args().Len() == 0 {
logger.Debug(`trivy requires at least 1 argument or --input option`)
_ = cli.ShowSubcommandHelp(ctx) // nolint: errcheck

View File

@@ -1,10 +1,10 @@
package config_test
package option_test
import (
"flag"
"testing"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
@@ -12,18 +12,18 @@ import (
"go.uber.org/zap/zaptest/observer"
)
func TestArtifactConfig_Init(t *testing.T) {
func TestArtifactOption_Init(t *testing.T) {
tests := []struct {
name string
args []string
logs []string
want config.ArtifactConfig
want option.ArtifactOption
wantErr string
}{
{
name: "happy path",
args: []string{"alpine:3.10"},
want: config.ArtifactConfig{
want: option.ArtifactOption{
Target: "alpine:3.10",
},
},
@@ -46,7 +46,7 @@ func TestArtifactConfig_Init(t *testing.T) {
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
c := config.NewArtifactConfig(ctx)
c := option.NewArtifactOption(ctx)
err := c.Init(ctx, logger.Sugar())

View File

@@ -1,4 +1,4 @@
package config
package option
import (
"strings"
@@ -7,20 +7,20 @@ import (
"golang.org/x/xerrors"
)
// CacheConfig holds the config for cache
type CacheConfig struct {
// CacheOption holds the options for cache
type CacheOption struct {
CacheBackend string
}
// NewCacheConfig returns an instance of CacheConfig
func NewCacheConfig(c *cli.Context) CacheConfig {
return CacheConfig{
// NewCacheOption returns an instance of CacheOption
func NewCacheOption(c *cli.Context) CacheOption {
return CacheOption{
CacheBackend: c.String("cache-backend"),
}
}
// Init initialize the CacheConfig
func (c *CacheConfig) Init() error {
// Init initialize the CacheOption
func (c *CacheOption) Init() error {
// "redis://" or "fs" are allowed for now
// An empty value is also allowed for testability
if !strings.HasPrefix(c.CacheBackend, "redis://") &&

View File

@@ -1,4 +1,4 @@
package config_test
package option_test
import (
"flag"
@@ -7,26 +7,26 @@ import (
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
)
func TestNewCacheConfig(t *testing.T) {
func TestNewCacheOption(t *testing.T) {
tests := []struct {
name string
args []string
want config.CacheConfig
want option.CacheOption
}{
{
name: "happy path",
args: []string{"--cache-backend", "redis://localhost:6379"},
want: config.CacheConfig{
want: option.CacheOption{
CacheBackend: "redis://localhost:6379",
},
},
{
name: "default",
args: []string{},
want: config.CacheConfig{
want: option.CacheOption{
CacheBackend: "fs",
},
},
@@ -40,13 +40,13 @@ func TestNewCacheConfig(t *testing.T) {
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got := config.NewCacheConfig(c)
got := option.NewCacheOption(c)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestCacheConfig_Init(t *testing.T) {
func TestCacheOption_Init(t *testing.T) {
type fields struct {
backend string
}
@@ -77,7 +77,7 @@ func TestCacheConfig_Init(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &config.CacheConfig{
c := &option.CacheOption{
CacheBackend: tt.fields.backend,
}

View File

@@ -1,12 +1,12 @@
package config
package option
import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)
// DBConfig holds the config for trivy DB
type DBConfig struct {
// DBOption holds the options for trivy DB
type DBOption struct {
Reset bool
DownloadDBOnly bool
SkipUpdate bool
@@ -14,9 +14,9 @@ type DBConfig struct {
NoProgress bool
}
// NewDBConfig is the factory method to return the DBConfig
func NewDBConfig(c *cli.Context) DBConfig {
return DBConfig{
// NewDBOption is the factory method to return the DBOption
func NewDBOption(c *cli.Context) DBOption {
return DBOption{
Reset: c.Bool("reset"),
DownloadDBOnly: c.Bool("download-db-only"),
SkipUpdate: c.Bool("skip-update"),
@@ -25,8 +25,8 @@ func NewDBConfig(c *cli.Context) DBConfig {
}
}
// Init initialize the DBConfig
func (c *DBConfig) Init() (err error) {
// Init initialize the DBOption
func (c *DBOption) Init() (err error) {
if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("--skip-update and --download-db-only options can not be specified both")
}

View File

@@ -1,25 +1,24 @@
package config_test
package option_test
import (
"flag"
"testing"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
)
func TestNewDBConfig(t *testing.T) {
func TestNewDBOption(t *testing.T) {
tests := []struct {
name string
args []string
want config.DBConfig
want option.DBOption
}{
{
name: "happy path",
args: []string{"--reset", "--skip-update"},
want: config.DBConfig{
want: option.DBOption{
Reset: true,
SkipUpdate: true,
},
@@ -35,13 +34,13 @@ func TestNewDBConfig(t *testing.T) {
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got := config.NewDBConfig(c)
got := option.NewDBOption(c)
assert.Equal(t, tt.want, got, tt.name)
})
}
}
func TestDBConfig_Init(t *testing.T) {
func TestDBOption_Init(t *testing.T) {
type fields struct {
Reset bool
DownloadDBOnly bool
@@ -70,7 +69,7 @@ func TestDBConfig_Init(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &config.DBConfig{
c := &option.DBOption{
Reset: tt.fields.Reset,
DownloadDBOnly: tt.fields.DownloadDBOnly,
SkipUpdate: tt.fields.SkipUpdate,

View File

@@ -1,4 +1,4 @@
package config
package option
import (
"github.com/urfave/cli/v2"
@@ -8,8 +8,8 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)
// GlobalConfig holds the global config for trivy
type GlobalConfig struct {
// GlobalOption holds the global options for trivy
type GlobalOption struct {
Context *cli.Context
Logger *zap.SugaredLogger
@@ -19,16 +19,16 @@ type GlobalConfig struct {
CacheDir string
}
// NewGlobalConfig is the factory method to return GlobalConfig
func NewGlobalConfig(c *cli.Context) (GlobalConfig, error) {
// NewGlobalOption is the factory method to return GlobalOption
func NewGlobalOption(c *cli.Context) (GlobalOption, error) {
quiet := c.Bool("quiet")
debug := c.Bool("debug")
logger, err := log.NewLogger(debug, quiet)
if err != nil {
return GlobalConfig{}, xerrors.New("failed to create a logger")
return GlobalOption{}, xerrors.New("failed to create a logger")
}
return GlobalConfig{
return GlobalOption{
Context: c,
Logger: logger,

View File

@@ -1,26 +1,25 @@
package config_test
package option_test
import (
"flag"
"testing"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
)
func TestNewGlobalConfig(t *testing.T) {
tests := []struct {
name string
args []string
want config.GlobalConfig
want option.GlobalOption
}{
{
name: "happy path",
args: []string{"--quiet", "--debug"},
want: config.GlobalConfig{
want: option.GlobalOption{
Quiet: true,
Debug: true,
},
@@ -36,7 +35,7 @@ func TestNewGlobalConfig(t *testing.T) {
c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
got, err := config.NewGlobalConfig(c)
got, err := option.NewGlobalOption(c)
require.NoError(t, err, err)
assert.Equal(t, tt.want.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.Debug, got.Debug, tt.name)

View File

@@ -0,0 +1,19 @@
package option
import (
"github.com/urfave/cli/v2"
)
// ImageOption holds the options for scanning images
type ImageOption struct {
ScanRemovedPkgs bool
ListAllPkgs bool
}
// NewImageOption is the factory method to return ImageOption
func NewImageOption(c *cli.Context) ImageOption {
return ImageOption{
ScanRemovedPkgs: c.Bool("removed-pkgs"),
ListAllPkgs: c.Bool("list-all-pkgs"),
}
}

View File

@@ -0,0 +1,127 @@
package option
import (
"os"
"strings"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"golang.org/x/xerrors"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
)
// ReportOption holds the options for reporting scan results
type ReportOption struct {
Format string
Template string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
IgnorePolicy string
// these variables are not exported
vulnType string
securityChecks string
output string
severities string
// these variables are populated by Init()
VulnType []string
SecurityChecks []string
Output *os.File
Severities []dbTypes.Severity
}
// NewReportOption is the factory method to return ReportOption
func NewReportOption(c *cli.Context) ReportOption {
return ReportOption{
output: c.String("output"),
Format: c.String("format"),
Template: c.String("template"),
IgnorePolicy: c.String("ignore-policy"),
vulnType: c.String("vuln-type"),
securityChecks: c.String("security-checks"),
severities: c.String("severity"),
IgnoreFile: c.String("ignorefile"),
IgnoreUnfixed: c.Bool("ignore-unfixed"),
ExitCode: c.Int("exit-code"),
}
}
// Init initializes the ReportOption
func (c *ReportOption) Init(logger *zap.SugaredLogger) error {
var err error
if c.Template != "" {
if c.Format == "" {
logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
} else if c.Format != "template" {
logger.Warnf("--template is ignored because --format %s is specified. Use --template option with --format template option.", c.Format)
}
}
if c.Format == "template" && c.Template == "" {
logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
}
c.Severities = splitSeverity(logger, c.severities)
if err = c.populateVulnTypes(); err != nil {
return xerrors.Errorf("vuln type: %w", err)
}
if err = c.populateSecurityChecks(); err != nil {
return xerrors.Errorf("security checks: %w", err)
}
// for testability
c.severities = ""
c.vulnType = ""
c.securityChecks = ""
c.Output = os.Stdout
if c.output != "" {
if c.Output, err = os.Create(c.output); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
return nil
}
func (c *ReportOption) populateVulnTypes() error {
for _, v := range strings.Split(c.vulnType, ",") {
if types.NewVulnType(v) == types.VulnTypeUnknown {
return xerrors.Errorf("unknown vulnerability type (%s)", v)
}
c.VulnType = append(c.VulnType, v)
}
return nil
}
func (c *ReportOption) populateSecurityChecks() error {
for _, v := range strings.Split(c.securityChecks, ",") {
if types.NewSecurityCheck(v) == types.SecurityCheckUnknown {
return xerrors.Errorf("unknown security check (%s)", v)
}
c.SecurityChecks = append(c.SecurityChecks, v)
}
return nil
}
func splitSeverity(logger *zap.SugaredLogger, severity string) []dbTypes.Severity {
logger.Debugf("Severities: %s", severity)
var severities []dbTypes.Severity
for _, s := range strings.Split(severity, ",") {
severity, err := dbTypes.NewSeverity(s)
if err != nil {
logger.Warnf("unknown severity option: %s", err)
}
severities = append(severities, severity)
}
return severities
}

View File

@@ -0,0 +1,178 @@
package option
import (
"flag"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportReportConfig_Init(t *testing.T) {
type fields struct {
output string
Format string
Template string
vulnType string
securityChecks string
severities string
IgnoreFile string
IgnoreUnfixed bool
ExitCode int
VulnType []string
Output *os.File
Severities []dbTypes.Severity
}
tests := []struct {
name string
fields fields
args []string
logs []string
want ReportOption
wantErr string
}{
{
name: "happy path",
fields: fields{
severities: "CRITICAL",
vulnType: "os",
securityChecks: "vuln",
},
args: []string{"alpine:3.10"},
want: ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Output: os.Stdout,
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
vulnType: "os,library",
securityChecks: "vuln",
},
args: []string{"centos:7"},
logs: []string{
"unknown severity option: unknown severity: INVALID",
},
want: ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
Output: os.Stdout,
},
},
{
name: "invalid option combination: --template enabled without --format",
fields: fields{
Template: "@contrib/gitlab.tpl",
severities: "LOW",
vulnType: "os",
securityChecks: "vuln",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
},
want: ReportOption{
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Template: "@contrib/gitlab.tpl",
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
{
name: "invalid option combination: --template and --format json",
fields: fields{
Format: "json",
Template: "@contrib/gitlab.tpl",
severities: "LOW",
vulnType: "os",
securityChecks: "vuln",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--template is ignored because --format json is specified. Use --template option with --format template option.",
},
want: ReportOption{
Format: "json",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Template: "@contrib/gitlab.tpl",
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
{
name: "invalid option combination: --format template without --template",
fields: fields{
Format: "template",
severities: "LOW",
vulnType: "os",
securityChecks: "vuln",
},
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
},
want: ReportOption{
Format: "template",
Output: os.Stdout,
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
VulnType: []string{types.VulnTypeOS},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
core, obs := observer.New(zap.InfoLevel)
logger := zap.New(core)
set := flag.NewFlagSet("test", 0)
_ = set.Parse(tt.args)
c := &ReportOption{
output: tt.fields.output,
Format: tt.fields.Format,
Template: tt.fields.Template,
vulnType: tt.fields.vulnType,
securityChecks: tt.fields.securityChecks,
severities: tt.fields.severities,
IgnoreFile: tt.fields.IgnoreFile,
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
ExitCode: tt.fields.ExitCode,
Output: tt.fields.Output,
}
err := c.Init(logger.Sugar())
// tests log messages
var gotMessages []string
for _, entry := range obs.AllUntimed() {
gotMessages = append(gotMessages, entry.Message)
}
assert.Equal(t, tt.logs, gotMessages, tt.name)
// test the error
switch {
case tt.wantErr != "":
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
}
assert.NoError(t, err, tt.name)
assert.Equal(t, &tt.want, c, tt.name)
})
}
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/plugin"
)
@@ -105,7 +105,7 @@ func LoadCommands() cli.Commands {
}
func initLogger(ctx *cli.Context) error {
conf, err := config.NewGlobalConfig(ctx)
conf, err := option.NewGlobalOption(ctx)
if err != nil {
return xerrors.Errorf("config error: %w", err)
}

View File

@@ -3,14 +3,14 @@ package server
import (
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
)
// Config holds the Trivy config
type Config struct {
config.GlobalConfig
config.DBConfig
config.CacheConfig
option.GlobalOption
option.DBOption
option.CacheOption
Listen string
Token string
@@ -20,11 +20,11 @@ type Config struct {
// NewConfig is the factory method to return config
func NewConfig(c *cli.Context) Config {
// the error is ignored because logger is unnecessary
gc, _ := config.NewGlobalConfig(c) // nolint: errcheck
gc, _ := option.NewGlobalOption(c) // nolint: errcheck
return Config{
GlobalConfig: gc,
DBConfig: config.NewDBConfig(c),
CacheConfig: config.NewCacheConfig(c),
GlobalOption: gc,
DBOption: option.NewDBOption(c),
CacheOption: option.NewCacheOption(c),
Listen: c.String("listen"),
Token: c.String("token"),
@@ -34,10 +34,10 @@ func NewConfig(c *cli.Context) Config {
// Init initializes the config
func (c *Config) Init() (err error) {
if err := c.DBConfig.Init(); err != nil {
if err := c.DBOption.Init(); err != nil {
return err
}
if err := c.CacheConfig.Init(); err != nil {
if err := c.CacheOption.Init(); err != nil {
return err
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/aquasecurity/trivy/pkg/commands/server"
)
@@ -22,10 +22,10 @@ func TestNew(t *testing.T) {
name: "happy path",
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update", "--listen", "localhost:8080"},
want: server.Config{
GlobalConfig: config.GlobalConfig{
GlobalOption: option.GlobalOption{
Quiet: true,
},
DBConfig: config.DBConfig{
DBOption: option.DBOption{
Reset: true,
SkipUpdate: true,
NoProgress: true,
@@ -47,11 +47,11 @@ func TestNew(t *testing.T) {
ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args)
tt.want.GlobalConfig.Context = ctx
tt.want.GlobalOption.Context = ctx
got := server.NewConfig(ctx)
assert.Equal(t, tt.want.GlobalConfig.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.DBConfig, got.DBConfig, tt.name)
assert.Equal(t, tt.want.GlobalOption.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.DBOption, got.DBOption, tt.name)
assert.Equal(t, tt.want.Listen, got.Listen, tt.name)
})
}
@@ -60,8 +60,8 @@ func TestNew(t *testing.T) {
func TestConfig_Init(t *testing.T) {
tests := []struct {
name string
globalConfig config.GlobalConfig
dbConfig config.DBConfig
globalConfig option.GlobalOption
dbConfig option.DBOption
args []string
wantErr string
}{
@@ -71,14 +71,14 @@ func TestConfig_Init(t *testing.T) {
},
{
name: "happy path: reset",
dbConfig: config.DBConfig{
dbConfig: option.DBOption{
Reset: true,
},
args: []string{"alpine:3.10"},
},
{
name: "sad: skip and download db",
dbConfig: config.DBConfig{
dbConfig: option.DBOption{
SkipUpdate: true,
DownloadDBOnly: true,
},
@@ -89,7 +89,7 @@ func TestConfig_Init(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &server.Config{
DBConfig: tt.dbConfig,
DBOption: tt.dbConfig,
}
err := c.Init()

View File

@@ -3,7 +3,7 @@ package library
import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer/library"
ftypes "github.com/aquasecurity/fanal/types"
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
@@ -25,21 +25,21 @@ type advisory interface {
func NewDriver(libType string) (Driver, error) {
var driver Driver
switch libType {
case library.Bundler:
case ftypes.Bundler:
driver = newRubyGemsDriver()
case library.Cargo:
case ftypes.Cargo:
driver = newCargoDriver()
case library.Composer:
case ftypes.Composer:
driver = newComposerDriver()
case library.Npm, library.Yarn:
case ftypes.Npm, ftypes.Yarn:
driver = newNpmDriver()
case library.Pipenv, library.Poetry:
case ftypes.Pipenv, ftypes.Poetry:
driver = newPipDriver()
case library.NuGet:
case ftypes.NuGet:
driver = newNugetDriver()
case library.Jar:
case ftypes.Jar:
driver = newMavenDriver()
case library.GoBinary, library.GoMod:
case ftypes.GoBinary, ftypes.GoMod:
driver = Driver{
ecosystem: vulnerability.Go,
advisories: []advisory{NewAdvisory(vulnerability.Go, comparer.GenericComparer{})},

View File

@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
lib "github.com/aquasecurity/fanal/analyzer/library"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library"
@@ -29,7 +29,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "happy path",
fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer,
libType: ftypes.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.2.6",
@@ -46,7 +46,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "non-prefix buckets",
fixtures: []string{"testdata/fixtures/php-without-prefix.yaml"},
libType: lib.Composer,
libType: ftypes.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.2.6",
@@ -63,7 +63,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no patched versions in the advisory",
fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer,
libType: ftypes.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.4.6",
@@ -80,7 +80,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no vulnerable versions in the advisory",
fixtures: []string{"testdata/fixtures/ruby.yaml"},
libType: lib.Bundler,
libType: ftypes.Bundler,
args: args{
pkgName: "activesupport",
pkgVer: "4.1.1",
@@ -97,7 +97,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no vulnerability",
fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer,
libType: ftypes.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.4.7",

View File

@@ -0,0 +1,56 @@
package downloader
import (
"context"
"os"
getter "github.com/hashicorp/go-getter"
"golang.org/x/xerrors"
)
// DownloadToTempDir downloads the configured source to a temp dir.
func DownloadToTempDir(ctx context.Context, url string) (string, error) {
tempDir, err := os.MkdirTemp("", "trivy-plugin")
if err != nil {
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
}
pwd, err := os.Getwd()
if err != nil {
return "", xerrors.Errorf("unable to get the current dir: %w", err)
}
if err = Download(ctx, url, tempDir, pwd); err != nil {
return "", xerrors.Errorf("download error: %w", err)
}
return tempDir, nil
}
// Download downloads the configured source to the destination.
func Download(ctx context.Context, src, dst, pwd string) error {
// go-getter doesn't allow the dst directory already exists if the src is directory.
_ = os.RemoveAll(dst)
var opts []getter.ClientOption
// Overwrite the file getter so that a file will be copied
getter.Getters["file"] = &getter.FileGetter{Copy: true}
// Build the client
client := &getter.Client{
Ctx: ctx,
Src: src,
Dst: dst,
Pwd: pwd,
Getters: getter.Getters,
Mode: getter.ClientModeAny,
Options: opts,
}
if err := client.Get(); err != nil {
return xerrors.Errorf("failed to download: %w", err)
}
return nil
}

View File

@@ -10,7 +10,7 @@ import (
"strconv"
"strings"
"github.com/google/go-github/v28/github"
"github.com/google/go-github/v33/github"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
@@ -44,7 +44,7 @@ func (r Repository) ListReleases(ctx context.Context, opt *github.ListOptions) (
// DownloadAsset returns reader object of downloaded object
func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id)
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id, nil)
}
// Operation defines the file operations
@@ -113,7 +113,7 @@ func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser,
return nil, 0, xerrors.New("DB file not found")
}
func (c Client) downloadAsset(ctx context.Context, asset github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
func (c Client) downloadAsset(ctx context.Context, asset *github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
log.Logger.Debugf("asset name: %s", asset.GetName())
if asset.GetName() != fileName {
return nil, 0, xerrors.New("file name doesn't match")

View File

@@ -13,12 +13,10 @@ import (
"testing"
"time"
"golang.org/x/xerrors"
"github.com/google/go-github/v33/github"
"github.com/stretchr/testify/assert"
"github.com/google/go-github/v28/github"
"github.com/stretchr/testify/mock"
"golang.org/x/xerrors"
)
type MockRepository struct {
@@ -96,7 +94,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 1, 1, 1, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
@@ -109,7 +107,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
@@ -143,7 +141,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
@@ -177,7 +175,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
@@ -191,7 +189,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(300),
Name: github.String("trivy.db.gz"),
@@ -204,7 +202,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
@@ -255,7 +253,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(200),
Name: github.String("trivy.db.gz"),
@@ -310,7 +308,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
@@ -350,7 +348,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),
@@ -385,7 +383,7 @@ func TestClient_DownloadDB(t *testing.T) {
PublishedAt: &github.Timestamp{
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
},
Assets: []github.ReleaseAsset{
Assets: []*github.ReleaseAsset{
{
ID: github.Int64(100),
Name: github.String("trivy.db.gz"),

View File

@@ -7,7 +7,8 @@ import (
"go.uber.org/zap/zapcore"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/log"
flog "github.com/aquasecurity/fanal/log"
dlog "github.com/aquasecurity/go-dep-parser/pkg/log"
)
var (
@@ -30,7 +31,10 @@ func InitLogger(debug, disable bool) (err error) {
}
// Set logger for go-dep-parser
log.SetLogger(Logger)
dlog.SetLogger(Logger)
// Set logger for fanal
flog.SetLogger(Logger)
return nil

View File

@@ -7,10 +7,10 @@ import (
"path/filepath"
"runtime"
getter "github.com/hashicorp/go-getter"
"golang.org/x/xerrors"
yaml "gopkg.in/yaml.v3"
"github.com/aquasecurity/trivy/pkg/downloader"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
)
@@ -116,7 +116,7 @@ func (p Plugin) install(ctx context.Context, dst, pwd string) error {
}
log.Logger.Debugf("Downloading the execution file from %s...", platform.URI)
if err = download(ctx, platform.URI, dst, pwd); err != nil {
if err = downloader.Download(ctx, platform.URI, dst, pwd); err != nil {
return xerrors.Errorf("unable to download the execution file (%s): %w", platform.URI, err)
}
return nil
@@ -147,7 +147,7 @@ func Install(ctx context.Context, url string, force bool) (Plugin, error) {
}
log.Logger.Infof("Installing the plugin from %s...", url)
tempDir, err := downloadToTempDir(ctx, url)
tempDir, err := downloader.DownloadToTempDir(ctx, url)
if err != nil {
return Plugin{}, xerrors.Errorf("download failed: %w", err)
}
@@ -182,51 +182,6 @@ func Uninstall(name string) error {
return os.RemoveAll(pluginDir)
}
func downloadToTempDir(ctx context.Context, url string) (string, error) {
tempDir, err := os.MkdirTemp("", "trivy-plugin")
if err != nil {
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
}
pwd, err := os.Getwd()
if err != nil {
return "", xerrors.Errorf("unable to get the current dir: %w", err)
}
if err = download(ctx, url, tempDir, pwd); err != nil {
return "", xerrors.Errorf("download error: %w", err)
}
return tempDir, nil
}
func download(ctx context.Context, src, dst, pwd string) error {
// go-getter doesn't allow the dst directory already exists if the src is directory.
_ = os.RemoveAll(dst)
var opts []getter.ClientOption
// Overwrite the file getter so that a file will be copied
getter.Getters["file"] = &getter.FileGetter{Copy: true}
// Build the client
client := &getter.Client{
Ctx: ctx,
Src: src,
Dst: dst,
Pwd: pwd,
Getters: getter.Getters,
Mode: getter.ClientModeAny,
Options: opts,
}
if err := client.Get(); err != nil {
return xerrors.Errorf("failed to download: %w", err)
}
return nil
}
// LoadAll loads all plugins
func LoadAll() ([]Plugin, error) {
pluginsDir := dir()

27
pkg/report/json.go Normal file
View File

@@ -0,0 +1,27 @@
package report
import (
"encoding/json"
"fmt"
"io"
"golang.org/x/xerrors"
)
// JSONWriter implements result Writer
type JSONWriter struct {
Output io.Writer
}
// Write writes the results in JSON format
func (jw JSONWriter) Write(results Results) error {
output, err := json.MarshalIndent(results, "", " ")
if err != nil {
return xerrors.Errorf("failed to marshal json: %w", err)
}
if _, err = fmt.Fprint(jw.Output, string(output)); err != nil {
return xerrors.Errorf("failed to write json: %w", err)
}
return nil
}

82
pkg/report/json_test.go Normal file
View File

@@ -0,0 +1,82 @@
package report_test
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportWriter_JSON(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
expectedJSON report.Results
}{
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
expectedJSON: report.Results{
report.Result{
Target: "foojson",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
jw := report.JSONWriter{}
jsonWritten := bytes.Buffer{}
jw.Output = &jsonWritten
inputResults := report.Results{
{
Target: "foojson",
Vulnerabilities: tc.detectedVulns,
},
}
err := report.WriteResults("json", &jsonWritten, nil, inputResults, "", false)
assert.NoError(t, err)
writtenResults := report.Results{}
err = json.Unmarshal([]byte(jsonWritten.String()), &writtenResults)
assert.NoError(t, err, "invalid json written", tc.name)
assert.Equal(t, tc.expectedJSON, writtenResults, tc.name)
})
}
}

113
pkg/report/table.go Normal file
View File

@@ -0,0 +1,113 @@
package report
import (
"fmt"
"io"
"os"
"strings"
"github.com/olekukonko/tablewriter"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
)
// TableWriter implements Writer and output in tabular form
type TableWriter struct {
Severities []dbTypes.Severity
Output io.Writer
Light bool
}
// Write writes the result on standard output
func (tw TableWriter) Write(results Results) error {
for _, result := range results {
// Skip zero vulnerabilities on Java archives (JAR/WAR/EAR)
if result.Type == ftypes.Jar && len(result.Vulnerabilities) == 0 {
continue
}
tw.write(result)
}
return nil
}
func (tw TableWriter) write(result Result) {
table := tablewriter.NewWriter(tw.Output)
total, severityCount := tw.writeVulnerabilities(table, result.Vulnerabilities)
var severities []string
for _, sev := range tw.Severities {
severities = append(severities, sev.String())
}
var results []string
for _, severity := range dbTypes.SeverityNames {
if !utils.StringInSlice(severity, severities) {
continue
}
r := fmt.Sprintf("%s: %d", severity, severityCount[severity])
results = append(results, r)
}
fmt.Printf("\n%s\n", result.Target)
fmt.Println(strings.Repeat("=", len(result.Target)))
fmt.Printf("Total: %d (%s)\n\n", total, strings.Join(results, ", "))
if len(result.Vulnerabilities) == 0 {
return
}
table.SetAutoMergeCells(true)
table.SetRowLine(true)
table.Render()
return
}
func (tw TableWriter) writeVulnerabilities(table *tablewriter.Table, vulns []types.DetectedVulnerability) (int, map[string]int) {
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
if !tw.Light {
header = append(header, "Title")
}
table.SetHeader(header)
severityCount := tw.setVulnerabilityRows(table, vulns)
return len(vulns), severityCount
}
func (tw TableWriter) setVulnerabilityRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
severityCount := map[string]int{}
for _, v := range vulns {
severityCount[v.Severity]++
title := v.Title
if title == "" {
title = v.Description
}
splitTitle := strings.Split(title, " ")
if len(splitTitle) >= 12 {
title = strings.Join(splitTitle[:12], " ") + "..."
}
if len(v.PrimaryURL) > 0 {
r := strings.NewReplacer("https://", "", "http://", "")
title = fmt.Sprintf("%s -->%s", title, r.Replace(v.PrimaryURL))
}
var row []string
if tw.Output == os.Stdout {
row = []string{v.PkgName, v.VulnerabilityID, dbTypes.ColorizeSeverity(v.Severity),
v.InstalledVersion, v.FixedVersion}
} else {
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion}
}
if !tw.Light {
row = append(row, strings.TrimSpace(title))
}
table.Append(row)
}
return severityCount
}

145
pkg/report/table_test.go Normal file
View File

@@ -0,0 +1,145 @@
package report_test
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportWriter_Table(t *testing.T) {
testCases := []struct {
name string
results report.Results
expectedOutput string
light bool
}{
{
name: "happy path full",
results: report.Results{
{
Target: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
`,
},
{
name: "happy path light",
light: true,
results: report.Results{
{
Target: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
+---------+------------------+----------+-------------------+---------------+
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 |
+---------+------------------+----------+-------------------+---------------+
`,
},
{
name: "no title for vuln and missing primary link",
results: report.Results{
{
Target: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Description: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------+
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
+---------+------------------+----------+-------------------+---------------+--------+
`,
},
{
name: "long title for vuln",
results: report.Results{
{
Target: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-1234",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-1234",
Vulnerability: dbTypes.Vulnerability{
Title: "a b c d e f g h i j k l m n o p q r s t u v",
Description: "foobar",
Severity: "HIGH",
},
},
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| foo | CVE-2020-1234 | HIGH | 1.2.3 | 3.4.5 | a b c d e f g h i j k l... |
| | | | | | -->avd.aquasec.com/nvd/cve-2020-1234 |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
`,
},
{
name: "no vulns",
expectedOutput: ``,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tableWritten := bytes.Buffer{}
assert.NoError(t, report.WriteResults("table", &tableWritten, nil, tc.results, "", tc.light), tc.name)
assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name)
})
}
}

121
pkg/report/template.go Normal file
View File

@@ -0,0 +1,121 @@
package report
import (
"bytes"
"encoding/xml"
"fmt"
"html"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
"text/template"
"time"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/Masterminds/sprig"
"golang.org/x/xerrors"
)
// regex to extract file path in case string includes (distro:version)
var re = regexp.MustCompile(`(?P<path>.+?)(?:\s*\((?:.*?)\).*?)?$`)
// TemplateWriter write result in custom format defined by user's template
type TemplateWriter struct {
Output io.Writer
Template *template.Template
}
// NewTemplateWriter is the factory method to return TemplateWriter object
func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter, error) {
if strings.HasPrefix(outputTemplate, "@") {
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
if err != nil {
return nil, xerrors.Errorf("error retrieving template from path: %w", err)
}
outputTemplate = string(buf)
}
var templateFuncMap template.FuncMap
templateFuncMap = sprig.GenericFuncMap()
templateFuncMap["escapeXML"] = func(input string) string {
escaped := &bytes.Buffer{}
if err := xml.EscapeText(escaped, []byte(input)); err != nil {
fmt.Printf("error while escapeString to XML: %v", err.Error())
return input
}
return escaped.String()
}
templateFuncMap["toSarifErrorLevel"] = toSarifErrorLevel
templateFuncMap["toSarifRuleName"] = toSarifRuleName
templateFuncMap["endWithPeriod"] = func(input string) string {
if !strings.HasSuffix(input, ".") {
input += "."
}
return input
}
templateFuncMap["toLower"] = func(input string) string {
return strings.ToLower(input)
}
templateFuncMap["escapeString"] = func(input string) string {
return html.EscapeString(input)
}
templateFuncMap["toPathUri"] = func(input string) string {
var matches = re.FindStringSubmatch(input)
if matches != nil {
input = matches[re.SubexpIndex("path")]
}
input = strings.ReplaceAll(input, "\\", "/")
return input
}
templateFuncMap["getEnv"] = func(key string) string {
return os.Getenv(key)
}
templateFuncMap["getCurrentTime"] = func() string {
return Now().UTC().Format(time.RFC3339Nano)
}
tmpl, err := template.New("output template").Funcs(templateFuncMap).Parse(outputTemplate)
if err != nil {
return nil, xerrors.Errorf("error parsing template: %w", err)
}
return &TemplateWriter{Output: output, Template: tmpl}, nil
}
// Write writes result
func (tw TemplateWriter) Write(results Results) error {
err := tw.Template.Execute(tw.Output, results)
if err != nil {
return xerrors.Errorf("failed to write with template: %w", err)
}
return nil
}
func toSarifRuleName(vulnerabilityType string) string {
var ruleName string
switch vulnerabilityType {
case vulnerability.Ubuntu, vulnerability.Alpine, vulnerability.RedHat, vulnerability.RedHatOVAL,
vulnerability.Debian, vulnerability.DebianOVAL, vulnerability.Fedora, vulnerability.Amazon,
vulnerability.OracleOVAL, vulnerability.SuseCVRF, vulnerability.OpenSuseCVRF, vulnerability.Photon,
vulnerability.CentOS:
ruleName = "OS Package Vulnerability"
case "npm", "yarn", "nuget", "pipenv", "poetry", "bundler", "cargo", "composer":
ruleName = "Programming Language Vulnerability"
default:
ruleName = "Other Vulnerability"
}
return fmt.Sprintf("%s (%s)", ruleName, strings.Title(vulnerabilityType))
}
func toSarifErrorLevel(severity string) string {
switch severity {
case "CRITICAL", "HIGH":
return "error"
case "MEDIUM":
return "warning"
case "LOW", "UNKNOWN":
return "note"
default:
return "none"
}
}

211
pkg/report/template_test.go Normal file
View File

@@ -0,0 +1,211 @@
package report_test
import (
"bytes"
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportWriter_Template(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
template string
expected string
}{
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String()},
},
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "baz",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
},
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
},
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "123",
PkgName: `foo \ test`,
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
Description: `curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
Severity: "HIGH",
},
},
},
template: `<testsuites>
{{- range . -}}
{{- $failures := len .Vulnerabilities }}
<testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{ .Target }}" errors="0" skipped="0" time="">
{{- if not (eq .Type "") }}
<properties>
<property name="type" value="{{ .Type }}"></property>
</properties>
{{- end -}}
{{ range .Vulnerabilities }}
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
<failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
</testcase>
{{- end }}
</testsuite>
{{- end }}
</testsuites>`,
expected: `<testsuites>
<testsuite tests="1" failures="1" name="foojunit" errors="0" skipped="0" time="">
<properties>
<property name="type" value="test"></property>
</properties>
<testcase classname="foo \ test-1.2.3" name="[HIGH] 123" time="">
<failure message="gcc: POWER9 &#34;DARN&#34; RNG intrinsic produces repeated output" type="description">curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl &lt; 7.20.0 and curl &gt;= 7.60.0.</failure>
</testcase>
</testsuite>
</testsuites>`,
},
{
name: "happy path with/without period description should return with period",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
},
},
},
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."CVE-2019-0000 "with period and unescaped string curl: Use-after-free when closing &#39;easy&#39; handle in Curl_close()."`,
},
{
name: "Calculate using sprig",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
Severity: dbTypes.SeverityHigh.String(),
},
},
},
template: `{{ $high := 0 }}{{ $critical := 0 }}{{ range . }}{{ range .Vulnerabilities}}{{ if eq .Severity "HIGH" }}{{ $high = add $high 1 }}{{ end }}{{ if eq .Severity "CRITICAL" }}{{ $critical = add $critical 1 }}{{ end }}{{ end }}Critical: {{ $critical }}, High: {{ $high }}{{ end }}`,
expected: `Critical: 2, High: 1`,
},
{
name: "happy path: env var parsing and getCurrentTime",
detectedVulns: []types.DetectedVulnerability{},
template: `{{ toLower (getEnv "AWS_ACCOUNT_ID") }} {{ getCurrentTime }}`,
expected: `123456789012 2020-08-10T07:28:17.000958601Z`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
report.Now = func() time.Time {
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
}
os.Setenv("AWS_ACCOUNT_ID", "123456789012")
tmplWritten := bytes.Buffer{}
inputResults := report.Results{
{
Target: "foojunit",
Type: "test",
Vulnerabilities: tc.detectedVulns,
},
}
assert.NoError(t, report.WriteResults("template", &tmplWritten, nil, inputResults, tc.template, false))
assert.Equal(t, tc.expected, tmplWritten.String())
})
}
}
func TestReportWriter_Template_SARIF(t *testing.T) {
testCases := []struct {
name string
target string
detectedVulns []types.DetectedVulnerability
want string
}{
//TODO: refactor tests
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
templateFile := "../../contrib/sarif.tpl"
got := bytes.Buffer{}
template, err := ioutil.ReadFile(templateFile)
require.NoError(t, err, tc.name)
inputResults := report.Results{
report.Result{
Target: tc.target,
Type: "footype",
Vulnerabilities: tc.detectedVulns,
},
}
assert.NoError(t, report.WriteResults("template", &got, nil, inputResults, string(template), false), tc.name)
assert.JSONEq(t, tc.want, got.String(), tc.name)
})
}
}

View File

@@ -1,37 +1,19 @@
package report
import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"html"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
"text/template"
"time"
"github.com/Masterminds/sprig"
"github.com/olekukonko/tablewriter"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer/library"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
)
// Now returns the current time
var Now = time.Now
// regex to extract file path in case string includes (distro:version)
var re = regexp.MustCompile(`(?P<path>.+?)(?:\s*\((?:.*?)\).*?)?$`)
// Results to hold list of Result
type Results []Result
@@ -40,7 +22,17 @@ type Result struct {
Target string `json:"Target"`
Type string `json:"Type,omitempty"`
Packages []ftypes.Package `json:"Packages,omitempty"`
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities"`
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities,omitempty"`
}
// Failed returns whether the result includes any vulnerabilities
func (results Results) Failed() bool {
for _, r := range results {
if len(r.Vulnerabilities) > 0 {
return true
}
}
return false
}
// WriteResults writes the result to output, format as passed in argument
@@ -70,211 +62,3 @@ func WriteResults(format string, output io.Writer, severities []dbTypes.Severity
type Writer interface {
Write(Results) error
}
// TableWriter implements Writer and output in tabular form
type TableWriter struct {
Severities []dbTypes.Severity
Output io.Writer
Light bool
}
// Write writes the result on standard output
func (tw TableWriter) Write(results Results) error {
for _, result := range results {
// Skip zero vulnerabilities on Java archives (JAR/WAR/EAR)
if result.Type == library.Jar && len(result.Vulnerabilities) == 0 {
continue
}
tw.write(result)
}
return nil
}
func (tw TableWriter) write(result Result) {
table := tablewriter.NewWriter(tw.Output)
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
if !tw.Light {
header = append(header, "Title")
}
table.SetHeader(header)
severityCount := tw.setRows(table, result.Vulnerabilities)
var results []string
var severities []string
for _, sev := range tw.Severities {
severities = append(severities, sev.String())
}
for _, severity := range dbTypes.SeverityNames {
if !utils.StringInSlice(severity, severities) {
continue
}
r := fmt.Sprintf("%s: %d", severity, severityCount[severity])
results = append(results, r)
}
fmt.Printf("\n%s\n", result.Target)
fmt.Println(strings.Repeat("=", len(result.Target)))
fmt.Printf("Total: %d (%s)\n\n", len(result.Vulnerabilities), strings.Join(results, ", "))
if len(result.Vulnerabilities) == 0 {
return
}
table.SetAutoMergeCells(true)
table.SetRowLine(true)
table.Render()
return
}
func (tw TableWriter) setRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
severityCount := map[string]int{}
for _, v := range vulns {
severityCount[v.Severity]++
title := v.Title
if title == "" {
title = v.Description
}
splitTitle := strings.Split(title, " ")
if len(splitTitle) >= 12 {
title = strings.Join(splitTitle[:12], " ") + "..."
}
if len(v.PrimaryURL) > 0 {
r := strings.NewReplacer("https://", "", "http://", "")
title = fmt.Sprintf("%s -->%s", title, r.Replace(v.PrimaryURL))
}
var row []string
if tw.Output == os.Stdout {
row = []string{v.PkgName, v.VulnerabilityID, dbTypes.ColorizeSeverity(v.Severity),
v.InstalledVersion, v.FixedVersion}
} else {
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion}
}
if !tw.Light {
row = append(row, strings.TrimSpace(title))
}
table.Append(row)
}
return severityCount
}
// JSONWriter implements result Writer
type JSONWriter struct {
Output io.Writer
}
// Write writes the results in JSON format
func (jw JSONWriter) Write(results Results) error {
output, err := json.MarshalIndent(results, "", " ")
if err != nil {
return xerrors.Errorf("failed to marshal json: %w", err)
}
if _, err = fmt.Fprint(jw.Output, string(output)); err != nil {
return xerrors.Errorf("failed to write json: %w", err)
}
return nil
}
// TemplateWriter write result in custom format defined by user's template
type TemplateWriter struct {
Output io.Writer
Template *template.Template
}
// NewTemplateWriter is the factory method to return TemplateWriter object
func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter, error) {
if strings.HasPrefix(outputTemplate, "@") {
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
if err != nil {
return nil, xerrors.Errorf("error retrieving template from path: %w", err)
}
outputTemplate = string(buf)
}
var templateFuncMap template.FuncMap
templateFuncMap = sprig.GenericFuncMap()
templateFuncMap["escapeXML"] = func(input string) string {
escaped := &bytes.Buffer{}
if err := xml.EscapeText(escaped, []byte(input)); err != nil {
fmt.Printf("error while escapeString to XML: %v", err.Error())
return input
}
return escaped.String()
}
templateFuncMap["toSarifErrorLevel"] = toSarifErrorLevel
templateFuncMap["toSarifRuleName"] = toSarifRuleName
templateFuncMap["endWithPeriod"] = func(input string) string {
if !strings.HasSuffix(input, ".") {
input += "."
}
return input
}
templateFuncMap["toLower"] = func(input string) string {
return strings.ToLower(input)
}
templateFuncMap["escapeString"] = func(input string) string {
return html.EscapeString(input)
}
templateFuncMap["toPathUri"] = func(input string) string {
var matches = re.FindStringSubmatch(input)
if matches != nil {
input = matches[re.SubexpIndex("path")]
}
input = strings.ReplaceAll(input, "\\", "/")
return input
}
templateFuncMap["getEnv"] = func(key string) string {
return os.Getenv(key)
}
templateFuncMap["getCurrentTime"] = func() string {
return Now().UTC().Format(time.RFC3339Nano)
}
tmpl, err := template.New("output template").Funcs(templateFuncMap).Parse(outputTemplate)
if err != nil {
return nil, xerrors.Errorf("error parsing template: %w", err)
}
return &TemplateWriter{Output: output, Template: tmpl}, nil
}
// Write writes result
func (tw TemplateWriter) Write(results Results) error {
err := tw.Template.Execute(tw.Output, results)
if err != nil {
return xerrors.Errorf("failed to write with template: %w", err)
}
return nil
}
func toSarifRuleName(vulnerabilityType string) string {
var ruleName string
switch vulnerabilityType {
case vulnerability.Ubuntu, vulnerability.Alpine, vulnerability.RedHat, vulnerability.RedHatOVAL,
vulnerability.Debian, vulnerability.DebianOVAL, vulnerability.Fedora, vulnerability.Amazon,
vulnerability.OracleOVAL, vulnerability.SuseCVRF, vulnerability.OpenSuseCVRF, vulnerability.Photon,
vulnerability.CentOS:
ruleName = "OS Package Vulnerability"
case "npm", "yarn", "nuget", "pipenv", "poetry", "bundler", "cargo", "composer":
ruleName = "Programming Language Vulnerability"
default:
ruleName = "Other Vulnerability"
}
return fmt.Sprintf("%s (%s)", ruleName, strings.Title(vulnerabilityType))
}
func toSarifErrorLevel(severity string) string {
switch severity {
case "CRITICAL", "HIGH":
return "error"
case "MEDIUM":
return "warning"
case "LOW", "UNKNOWN":
return "note"
default:
return "none"
}
}

View File

@@ -1,570 +1,50 @@
package report_test
import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/assert"
)
func TestReportWriter_Table(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
expectedOutput string
light bool
func TestResults_Failed(t *testing.T) {
tests := []struct {
name string
results report.Results
want bool
}{
{
name: "happy path full",
detectedVulns: []types.DetectedVulnerability{
name: "no vulnerabilities and misconfigurations",
results: report.Results{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
Target: "test",
Type: "test",
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
`,
want: false,
},
{
name: "happy path light",
light: true,
detectedVulns: []types.DetectedVulnerability{
name: "vulnerabilities found",
results: report.Results{
{
VulnerabilityID: "123",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
+---------+------------------+----------+-------------------+---------------+
| foo | 123 | HIGH | 1.2.3 | 3.4.5 |
+---------+------------------+----------+-------------------+---------------+
`,
},
{
name: "no title for vuln and missing primary link",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "123",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Description: "foobar",
Severity: "HIGH",
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------+
| foo | 123 | HIGH | 1.2.3 | 3.4.5 | foobar |
+---------+------------------+----------+-------------------+---------------+--------+
`,
},
{
name: "long title for vuln",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-1234",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "a b c d e f g h i j k l m n o p q r s t u v",
Severity: "HIGH",
},
},
},
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
| foo | CVE-2020-1234 | HIGH | 1.2.3 | 3.4.5 | a b c d e f g h i j k l... |
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
`,
},
{
name: "no vulns",
detectedVulns: []types.DetectedVulnerability{},
expectedOutput: ``,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
inputResults := report.Results{
{
Target: "foo",
Vulnerabilities: tc.detectedVulns,
},
}
tableWritten := bytes.Buffer{}
assert.NoError(t, report.WriteResults("table", &tableWritten, nil, inputResults, "", tc.light), tc.name)
assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name)
})
}
}
func TestReportWriter_JSON(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
expectedJSON report.Results
}{
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
},
},
expectedJSON: report.Results{
report.Result{
Target: "foojson",
Target: "test",
Type: "test",
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
},
VulnerabilityID: "CVE-2021-0001",
PkgName: "test",
},
},
},
},
want: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
jw := report.JSONWriter{}
jsonWritten := bytes.Buffer{}
jw.Output = &jsonWritten
inputResults := report.Results{
{
Target: "foojson",
Vulnerabilities: tc.detectedVulns,
},
}
assert.NoError(t, report.WriteResults("json", &jsonWritten, nil, inputResults, "", false), tc.name)
writtenResults := report.Results{}
errJson := json.Unmarshal([]byte(jsonWritten.String()), &writtenResults)
assert.NoError(t, errJson, "invalid json written", tc.name)
assert.Equal(t, tc.expectedJSON, writtenResults, tc.name)
})
}
}
func TestReportWriter_Template(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
template string
expected string
}{
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String()},
},
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "baz",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
},
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
},
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "123",
PkgName: `foo \ test`,
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
Description: `curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
Severity: "HIGH",
},
},
},
template: `<testsuites>
{{- range . -}}
{{- $failures := len .Vulnerabilities }}
<testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{ .Target }}" errors="0" skipped="0" time="">
{{- if not (eq .Type "") }}
<properties>
<property name="type" value="{{ .Type }}"></property>
</properties>
{{- end -}}
{{ range .Vulnerabilities }}
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
<failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
</testcase>
{{- end }}
</testsuite>
{{- end }}
</testsuites>`,
expected: `<testsuites>
<testsuite tests="1" failures="1" name="foojunit" errors="0" skipped="0" time="">
<properties>
<property name="type" value="test"></property>
</properties>
<testcase classname="foo \ test-1.2.3" name="[HIGH] 123" time="">
<failure message="gcc: POWER9 &#34;DARN&#34; RNG intrinsic produces repeated output" type="description">curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl &lt; 7.20.0 and curl &gt;= 7.60.0.</failure>
</testcase>
</testsuite>
</testsuites>`,
},
{
name: "happy path with/without period description should return with period",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
},
},
},
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."CVE-2019-0000 "with period and unescaped string curl: Use-after-free when closing &#39;easy&#39; handle in Curl_close()."`,
},
{
name: "Calculate using sprig",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
Severity: dbTypes.SeverityHigh.String(),
},
},
},
template: `{{ $high := 0 }}{{ $critical := 0 }}{{ range . }}{{ range .Vulnerabilities}}{{ if eq .Severity "HIGH" }}{{ $high = add $high 1 }}{{ end }}{{ if eq .Severity "CRITICAL" }}{{ $critical = add $critical 1 }}{{ end }}{{ end }}Critical: {{ $critical }}, High: {{ $high }}{{ end }}`,
expected: `Critical: 2, High: 1`,
},
{
name: "happy path: env var parsing and getCurrentTime",
detectedVulns: []types.DetectedVulnerability{},
template: `{{ toLower (getEnv "AWS_ACCOUNT_ID") }} {{ getCurrentTime }}`,
expected: `123456789012 2020-08-10T07:28:17.000958601Z`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
report.Now = func() time.Time {
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
}
os.Setenv("AWS_ACCOUNT_ID", "123456789012")
tmplWritten := bytes.Buffer{}
inputResults := report.Results{
{
Target: "foojunit",
Type: "test",
Vulnerabilities: tc.detectedVulns,
},
}
assert.NoError(t, report.WriteResults("template", &tmplWritten, nil, inputResults, tc.template, false))
assert.Equal(t, tc.expected, tmplWritten.String())
})
}
}
func TestReportWriter_Template_SARIF(t *testing.T) {
testCases := []struct {
name string
target string
detectedVulns []types.DetectedVulnerability
want string
}{
{
name: "no primary url",
target: "foo/target/alpine-310.tar.gz (alpine 3.10.2)",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-1234-5678",
PkgName: "foopackage",
InstalledVersion: "1.2.3",
FixedVersion: "4.5.6",
SeveritySource: "NVD",
PrimaryURL: "",
Vulnerability: dbTypes.Vulnerability{
Title: "foovuln",
Description: "foodesc",
Severity: "CRITICAL",
},
},
},
want: `{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "Trivy",
"informationUri": "https://github.com/aquasecurity/trivy",
"fullName": "Trivy Vulnerability Scanner",
"version": "0.15.0",
"rules": [
{
"id": "foo/target/alpine-310.tar.gz (alpine 3.10.2): foopackage-1.2.3 CVE-1234-5678",
"name": "Other Vulnerability (Footype)",
"shortDescription": {
"text": "CVE-1234-5678 Package: foopackage"
},
"fullDescription": {
"text": "foovuln."
},
"defaultConfiguration": {
"level": "error"
},
"help": {
"text": "Vulnerability CVE-1234-5678\nSeverity: CRITICAL\nPackage: foopackage\nInstalled Version: 1.2.3\nFixed Version: 4.5.6\nLink: [CVE-1234-5678]()",
"markdown": "**Vulnerability CVE-1234-5678**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|CRITICAL|foopackage|1.2.3|4.5.6|[CVE-1234-5678]()|\n"
},
"properties": {
"tags": [
"vulnerability",
"CRITICAL",
"foopackage"
],
"precision": "very-high"
}
}]
}
},
"results": [
{
"ruleId": "foo/target/alpine-310.tar.gz (alpine 3.10.2): foopackage-1.2.3 CVE-1234-5678",
"ruleIndex": 0,
"level": "error",
"message": {
"text": "foodesc."
},
"locations": [{
"physicalLocation": {
"artifactLocation": {
"uri": "foo/target/alpine-310.tar.gz",
"uriBaseId": "ROOTPATH"
}
}
}]
}],
"columnKind": "utf16CodeUnits",
"originalUriBaseIds": {
"ROOTPATH": {
"uri": "/"
}
}
}
]
}`,
},
{
name: "with primary url",
target: "rust-app\\Cargo.lock",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-1234-5678",
PkgName: "foopackage",
InstalledVersion: "1.2.3",
FixedVersion: "4.5.6",
SeveritySource: "NVD",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-1234-5678",
Vulnerability: dbTypes.Vulnerability{
Title: "foovuln",
Description: "foodesc",
Severity: "CRITICAL",
},
},
},
want: `{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "Trivy",
"informationUri": "https://github.com/aquasecurity/trivy",
"fullName": "Trivy Vulnerability Scanner",
"version": "0.15.0",
"rules": [
{
"id": "rust-app\\Cargo.lock: foopackage-1.2.3 CVE-1234-5678",
"name": "Other Vulnerability (Footype)",
"shortDescription": {
"text": "CVE-1234-5678 Package: foopackage"
},
"fullDescription": {
"text": "foovuln."
},
"defaultConfiguration": {
"level": "error"
},
"helpUri": "https://avd.aquasec.com/nvd/cve-1234-5678",
"help": {
"text": "Vulnerability CVE-1234-5678\nSeverity: CRITICAL\nPackage: foopackage\nInstalled Version: 1.2.3\nFixed Version: 4.5.6\nLink: [CVE-1234-5678](https://avd.aquasec.com/nvd/cve-1234-5678)",
"markdown": "**Vulnerability CVE-1234-5678**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|CRITICAL|foopackage|1.2.3|4.5.6|[CVE-1234-5678](https://avd.aquasec.com/nvd/cve-1234-5678)|\n"
},
"properties": {
"tags": [
"vulnerability",
"CRITICAL",
"foopackage"
],
"precision": "very-high"
}
}]
}
},
"results": [
{
"ruleId": "rust-app\\Cargo.lock: foopackage-1.2.3 CVE-1234-5678",
"ruleIndex": 0,
"level": "error",
"message": {
"text": "foodesc."
},
"locations": [{
"physicalLocation": {
"artifactLocation": {
"uri": "rust-app/Cargo.lock",
"uriBaseId": "ROOTPATH"
}
}
}]
}],
"columnKind": "utf16CodeUnits",
"originalUriBaseIds": {
"ROOTPATH": {
"uri": "/"
}
}
}
]
}`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
templateFile := "../../contrib/sarif.tpl"
got := bytes.Buffer{}
template, err := ioutil.ReadFile(templateFile)
require.NoError(t, err, tc.name)
inputResults := report.Results{
report.Result{
Target: tc.target,
Type: "footype",
Vulnerabilities: tc.detectedVulns,
},
}
assert.NoError(t, report.WriteResults("template", &got, nil, inputResults, string(template), false), tc.name)
assert.JSONEq(t, tc.want, got.String(), tc.name)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.results.Failed()
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -56,7 +56,8 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
ArtifactId: imageID,
BlobIds: layerIDs,
Options: &rpc.ScanOptions{
VulnType: options.VulnType,
VulnType: options.VulnType,
SecurityChecks: options.SecurityChecks,
},
})
return err

View File

@@ -117,10 +117,7 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
Description: vuln.Description,
Severity: common.Severity(severity),
References: vuln.References,
Layer: &common.Layer{
Digest: vuln.Layer.Digest,
DiffId: vuln.Layer.DiffID,
},
Layer: ConvertToRPCLayer(vuln.Layer),
Cvss: cvssMap,
SeveritySource: vuln.SeveritySource,
CweIds: vuln.CweIDs,
@@ -132,65 +129,83 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
return rpcVulns
}
// ConvertToRPCLayer returns common.Layer
func ConvertToRPCLayer(layer ftypes.Layer) *common.Layer {
return &common.Layer{
Digest: layer.Digest,
DiffId: layer.DiffID,
}
}
// ConvertFromRPCResults converts scanner.Result to report.Result
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)
cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
for vendor, vendorSeverity := range vuln.Cvss {
cvssMap[vendor] = dbTypes.CVSS{
V2Vector: vendorSeverity.V2Vector,
V3Vector: vendorSeverity.V3Vector,
V2Score: vendorSeverity.V2Score,
V3Score: vendorSeverity.V3Score,
}
}
var lastModifiedDate, publishedDate *time.Time
if vuln.LastModifiedDate != nil {
t, _ := ptypes.Timestamp(vuln.LastModifiedDate) // nolint: errcheck
lastModifiedDate = &t
}
if vuln.PublishedDate != nil {
t, _ := ptypes.Timestamp(vuln.PublishedDate) // nolint: errcheck
publishedDate = &t
}
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(),
CVSS: cvssMap,
References: vuln.References,
CweIDs: vuln.CweIds,
LastModifiedDate: lastModifiedDate,
PublishedDate: publishedDate,
},
Layer: ftypes.Layer{
Digest: vuln.Layer.Digest,
DiffID: vuln.Layer.DiffId,
},
SeveritySource: vuln.SeveritySource,
PrimaryURL: vuln.PrimaryUrl,
})
}
results = append(results, report.Result{
Target: result.Target,
Vulnerabilities: vulns,
Vulnerabilities: ConvertFromRPCVulns(result.Vulnerabilities),
Type: result.Type,
})
}
return results
}
// ConvertFromRPCVulns converts []*common.Vulnerability to []types.DetectedVulnerability
func ConvertFromRPCVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
var vulns []types.DetectedVulnerability
for _, vuln := range rpcVulns {
severity := dbTypes.Severity(vuln.Severity)
cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
for vendor, vendorSeverity := range vuln.Cvss {
cvssMap[vendor] = dbTypes.CVSS{
V2Vector: vendorSeverity.V2Vector,
V3Vector: vendorSeverity.V3Vector,
V2Score: vendorSeverity.V2Score,
V3Score: vendorSeverity.V3Score,
}
}
var lastModifiedDate, publishedDate *time.Time
if vuln.LastModifiedDate != nil {
t, _ := ptypes.Timestamp(vuln.LastModifiedDate) // nolint: errcheck
lastModifiedDate = &t
}
if vuln.PublishedDate != nil {
t, _ := ptypes.Timestamp(vuln.PublishedDate) // nolint: errcheck
publishedDate = &t
}
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(),
CVSS: cvssMap,
References: vuln.References,
CweIDs: vuln.CweIds,
LastModifiedDate: lastModifiedDate,
PublishedDate: publishedDate,
},
Layer: ConvertFromRPCLayer(vuln.Layer),
SeveritySource: vuln.SeveritySource,
PrimaryURL: vuln.PrimaryUrl,
})
}
return vulns
}
// ConvertFromRPCLayer converts *common.Layer to fanal.Layer
func ConvertFromRPCLayer(rpcLayer *common.Layer) ftypes.Layer {
return ftypes.Layer{
Digest: rpcLayer.Digest,
DiffID: rpcLayer.DiffId,
}
}
// ConvertFromRPCOS converts common.OS to fanal.OS
func ConvertFromRPCOS(rpcOS *common.OS) *ftypes.OS {
if rpcOS == nil {
@@ -286,9 +301,9 @@ func ConvertToRPCArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *ca
}
// ConvertToRPCBlobInfo returns PutBlobRequest
func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
func ConvertToRPCBlobInfo(diffID string, blobInfo ftypes.BlobInfo) *cache.PutBlobRequest {
var packageInfos []*common.PackageInfo
for _, pkgInfo := range layerInfo.PackageInfos {
for _, pkgInfo := range blobInfo.PackageInfos {
packageInfos = append(packageInfos, &common.PackageInfo{
FilePath: pkgInfo.FilePath,
Packages: ConvertToRPCPkgs(pkgInfo.Packages),
@@ -296,7 +311,7 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
}
var applications []*common.Application
for _, app := range layerInfo.Applications {
for _, app := range blobInfo.Applications {
var libs []*common.Library
for _, lib := range app.Libraries {
libs = append(libs, &common.Library{
@@ -315,13 +330,13 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
DiffId: diffID,
BlobInfo: &cache.BlobInfo{
SchemaVersion: ftypes.BlobJSONSchemaVersion,
Digest: layerInfo.Digest,
DiffId: layerInfo.DiffID,
Os: ConvertToRPCOS(layerInfo.OS),
Digest: blobInfo.Digest,
DiffId: blobInfo.DiffID,
Os: ConvertToRPCOS(blobInfo.OS),
PackageInfos: packageInfos,
Applications: applications,
OpaqueDirs: layerInfo.OpaqueDirs,
WhiteoutFiles: layerInfo.WhiteoutFiles,
OpaqueDirs: blobInfo.OpaqueDirs,
WhiteoutFiles: blobInfo.WhiteoutFiles,
},
}
}
@@ -346,8 +361,8 @@ func ConvertToRPCScanResponse(results report.Results, os *ftypes.OS, eosl bool)
for _, result := range results {
rpcResults = append(rpcResults, &scanner.Result{
Target: result.Target,
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
Type: result.Type,
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
})
}

View File

@@ -28,24 +28,27 @@ var ScanSuperSet = wire.NewSet(
// ScanServer implements the scanner
type ScanServer struct {
localScanner scanner.Driver
vulnClient vulnerability.Operation
resultClient vulnerability.Client
}
// NewScanServer is the factory method for scanner
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Operation) *ScanServer {
return &ScanServer{localScanner: s, vulnClient: vulnClient}
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Client) *ScanServer {
return &ScanServer{localScanner: s, resultClient: vulnClient}
}
// Scan scans and return response
func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
options := types.ScanOptions{VulnType: in.Options.VulnType}
options := types.ScanOptions{
VulnType: in.Options.VulnType,
SecurityChecks: in.Options.SecurityChecks,
}
results, os, eosl, err := s.localScanner.Scan(in.Target, in.ArtifactId, in.BlobIds, options)
if err != nil {
return nil, xerrors.Errorf("failed scan, %s: %w", in.Target, err)
}
for i := range results {
s.vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
s.resultClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
}
return rpc.ConvertToRPCScanResponse(results, os, eosl), nil
}

View File

@@ -16,8 +16,10 @@ import (
"github.com/aquasecurity/fanal/cache"
ftypes "github.com/aquasecurity/fanal/types"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
@@ -37,15 +39,16 @@ func TestScanServer_Scan(t *testing.T) {
in *rpcScanner.ScanRequest
}
tests := []struct {
name string
args args
scanExpectation scanner.DriverScanExpectation
fillInfoExpectation vulnerability.OperationFillInfoExpectation
want *rpcScanner.ScanResponse
wantErr string
name string
fixtures []string
args args
scanExpectation scanner.DriverScanExpectation
want *rpcScanner.ScanResponse
wantErr string
}{
{
name: "happy path",
name: "happy path",
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{
in: &rpcScanner.ScanRequest{
Target: "alpine:3.11",
@@ -70,7 +73,6 @@ func TestScanServer_Scan(t *testing.T) {
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
SeveritySource: "nvd",
Vulnerability: dbTypes.Vulnerability{
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
@@ -86,24 +88,6 @@ func TestScanServer_Scan(t *testing.T) {
},
},
},
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
Args: vulnerability.OperationFillInfoArgs{
Vulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Vulnerability: dbTypes.Vulnerability{
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
},
SeveritySource: "nvd",
},
},
ReportType: "alpine",
},
},
want: &rpcScanner.ScanResponse{
Os: &common.OS{
Family: "alpine",
@@ -119,9 +103,14 @@ func TestScanServer_Scan(t *testing.T) {
PkgName: "musl",
InstalledVersion: "1.2.3",
FixedVersion: "1.2.4",
Severity: common.Severity_MEDIUM,
SeveritySource: "nvd",
Layer: &common.Layer{},
Cvss: make(map[string]*common.CVSS),
Cvss: map[string]*common.CVSS{},
PrimaryUrl: "https://avd.aquasec.com/nvd/cve-2019-0001",
Title: "dos",
Description: "dos vulnerability",
References: []string{"http://example.com"},
LastModifiedDate: &timestamp.Timestamp{
Seconds: 1577840460,
},
@@ -161,13 +150,13 @@ func TestScanServer_Scan(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dbtest.InitDB(t, tt.fixtures)
defer db.Close()
mockDriver := new(scanner.MockDriver)
mockDriver.ApplyScanExpectation(tt.scanExpectation)
mockVulnClient := new(vulnerability.MockOperation)
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
s := NewScanServer(mockDriver, mockVulnClient)
s := NewScanServer(mockDriver, vulnerability.NewClient(db.Config{}))
got, err := s.Scan(context.Background(), tt.args.in)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)

View File

@@ -0,0 +1,13 @@
- bucket: vulnerability
pairs:
- key: CVE-2019-0001
value:
Title: dos
Description: dos vulnerability
Severity: MEDIUM
VendorSeverity:
nvd: 2
References:
- http://example.com
LastModifiedDate: "2020-01-01T01:01:00Z"
PublishedDate: "2001-01-01T01:01:00Z"

View File

@@ -13,28 +13,7 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer"
_ "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/gobinary"
_ "github.com/aquasecurity/fanal/analyzer/library/gomod"
_ "github.com/aquasecurity/fanal/analyzer/library/jar"
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
_ "github.com/aquasecurity/fanal/analyzer/library/nuget"
_ "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/debian"
_ "github.com/aquasecurity/fanal/analyzer/os/photon"
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
_ "github.com/aquasecurity/fanal/analyzer/os/ubuntu"
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpm"
_ "github.com/aquasecurity/fanal/analyzer/all"
"github.com/aquasecurity/fanal/applier"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/detector/library"
@@ -81,7 +60,7 @@ func (s Scanner) Scan(target, versionedArtifactID string, versionedBlobIDs []str
artifactDetail, err := s.applier.ApplyLayers(versionedArtifactID, versionedBlobIDs)
switch {
case errors.Is(err, analyzer.ErrUnknownOS):
log.Logger.Warn("OS is not detected and vulnerabilities in OS packages are not detected.")
log.Logger.Debug("OS is not detected and vulnerabilities in OS packages are not detected.")
case errors.Is(err, analyzer.ErrNoPkgsDetected):
log.Logger.Warn("No OS package is detected. Make sure you haven't deleted any files that contain information about the installed packages.")
log.Logger.Warn(`e.g. files under "/lib/apk/db/", "/var/lib/dpkg/" and "/var/lib/rpm"`)
@@ -92,29 +71,53 @@ func (s Scanner) Scan(target, versionedArtifactID string, versionedBlobIDs []str
var eosl bool
var results report.Results
if utils.StringInSlice("os", options.VulnType) && artifactDetail.OS != nil {
var result *report.Result
result, eosl, err = s.scanOSPkgs(target, artifactDetail, options)
// Scan OS packages and programming language dependencies
if utils.StringInSlice(types.SecurityCheckVulnerability, options.SecurityChecks) {
var vulnResults report.Results
vulnResults, eosl, err = s.checkVulnerabilities(target, artifactDetail, options)
if err != nil {
return nil, nil, false, xerrors.Errorf("unable to scan OS packages: %w", err)
} else if result != nil {
results = append(results, *result)
return nil, nil, false, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
}
if utils.StringInSlice("library", options.VulnType) {
libResults, err := s.scanLibrary(artifactDetail.Applications, options)
if err != nil {
return nil, nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
}
results = append(results, libResults...)
results = append(results, vulnResults...)
}
return results, artifactDetail.OS, eosl, nil
}
func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
report.Results, bool, error) {
var eosl bool
var results report.Results
if utils.StringInSlice(types.VulnTypeOS, options.VulnType) {
result, detectedEosl, err := s.scanOSPkgs(target, detail, options)
if err != nil {
return nil, false, xerrors.Errorf("unable to scan OS packages: %w", err)
} else if result != nil {
results = append(results, *result)
}
eosl = detectedEosl
}
if utils.StringInSlice(types.VulnTypeLibrary, options.VulnType) {
libResults, err := s.scanLibrary(detail.Applications, options)
if err != nil {
return nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
}
results = append(results, libResults...)
}
return results, eosl, nil
}
func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
*report.Result, bool, error) {
if detail.OS == nil {
log.Logger.Infof("Detected OS: unknown")
return nil, false, nil
}
log.Logger.Infof("Detected OS: %s", detail.OS.Family)
pkgs := detail.Packages
if options.ScanRemovedPackages {
pkgs = mergePkgs(pkgs, detail.HistoryPackages)
@@ -158,8 +161,8 @@ func (s Scanner) detectVulnsInOSPkgs(target, osFamily, osName string, pkgs []fty
}
func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOptions) (report.Results, error) {
log.Logger.Infof("Number of PL dependency files: %d", len(apps))
if len(apps) == 0 {
log.Logger.Info("Trivy skips scanning programming language libraries because no supported file was detected")
return nil, nil
}
@@ -213,6 +216,7 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
}
func skipped(filePath string, skipFiles, skipDirs []string) bool {
filePath = strings.TrimLeft(filepath.Clean(filePath), string(os.PathSeparator))
for _, skipFile := range skipFiles {
skipFile = strings.TrimLeft(filepath.Clean(skipFile), string(os.PathSeparator))
if filePath == skipFile {

View File

@@ -4,19 +4,15 @@ import (
"errors"
"testing"
"github.com/aquasecurity/fanal/analyzer/library"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/fanal/analyzer"
ftypes "github.com/aquasecurity/fanal/types"
dtypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/dbtest"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
@@ -44,7 +40,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -68,7 +67,7 @@ func TestScanner_Scan(t *testing.T) {
},
Applications: []ftypes.Application{
{
Type: library.Bundler,
Type: ftypes.Bundler,
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
@@ -156,7 +155,11 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}, ListAllPackages: true},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
ListAllPackages: true,
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -307,7 +310,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -358,7 +364,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -431,7 +440,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "fedora:27",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -499,7 +511,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "busybox:latest",
layerIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -521,7 +536,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -609,8 +627,9 @@ func TestScanner_Scan(t *testing.T) {
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{
VulnType: []string{"library"},
SkipDirs: []string{"/usr/lib/ruby/gems"},
VulnType: []string{types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
SkipDirs: []string{"/usr/lib/ruby/gems", "/app/k8s"},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
@@ -683,7 +702,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -701,7 +723,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
@@ -753,7 +778,10 @@ func TestScanner_Scan(t *testing.T) {
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
options: types.ScanOptions{
VulnType: []string{types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
},
},
fixtures: []string{"testdata/fixtures/sad.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{

View File

@@ -10,7 +10,6 @@ import (
aimage "github.com/aquasecurity/fanal/artifact/image"
flocal "github.com/aquasecurity/fanal/artifact/local"
"github.com/aquasecurity/fanal/artifact/remote"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
@@ -83,7 +82,8 @@ type Scanner struct {
// Driver defines operations of scanner
type Driver interface {
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (results report.Results, osFound *ftypes.OS, eols bool, err error)
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (
results report.Results, osFound *ftypes.OS, eols bool, err error)
}
// NewScanner is the factory method of Scanner
@@ -98,14 +98,6 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (r
return nil, xerrors.Errorf("failed analysis: %w", err)
}
// Debug information
var blobIDs []string
for _, b := range artifactInfo.BlobIDs {
blobIDs = append(blobIDs, cache.TrimVersionSuffix(b))
}
log.Logger.Debugf("Artifact ID: %s", cache.TrimVersionSuffix(artifactInfo.ID))
log.Logger.Debugf("Blob IDs: %v", blobIDs)
results, osFound, eosl, err := s.driver.Scan(artifactInfo.Name, artifactInfo.ID, artifactInfo.BlobIDs, options)
if err != nil {
return nil, xerrors.Errorf("scan failed: %w", err)

View File

@@ -3,6 +3,7 @@ package types
// ScanOptions holds the attributes for scanning vulnerabilities
type ScanOptions struct {
VulnType []string
SecurityChecks []string
ScanRemovedPackages bool
ListAllPackages bool
SkipFiles []string

47
pkg/types/target.go Normal file
View File

@@ -0,0 +1,47 @@
package types
import "github.com/aquasecurity/trivy/pkg/utils"
// VulnType represents vulnerability type
type VulnType = string
// SecurityCheck represents the type of security check
type SecurityCheck = string
const (
// VulnTypeUnknown is a vulnerability type of unknown
VulnTypeUnknown = VulnType("unknown")
// VulnTypeOS is a vulnerability type of OS packages
VulnTypeOS = VulnType("os")
// VulnTypeLibrary is a vulnerability type of programming language dependencies
VulnTypeLibrary = VulnType("library")
// SecurityCheckUnknown is a security check of unknown
SecurityCheckUnknown = SecurityCheck("unknown")
// SecurityCheckVulnerability is a security check of vulnerabilities
SecurityCheckVulnerability = SecurityCheck("vuln")
)
var (
vulnTypes = []string{VulnTypeOS, VulnTypeLibrary}
securityChecks = []string{SecurityCheckVulnerability}
)
// NewVulnType returns an instance of VulnType
func NewVulnType(s string) VulnType {
if utils.StringInSlice(s, vulnTypes) {
return s
}
return VulnTypeUnknown
}
// NewSecurityCheck returns an instance of SecurityCheck
func NewSecurityCheck(s string) SecurityCheck {
if utils.StringInSlice(s, securityChecks) {
return s
}
return SecurityCheckUnknown
}

View File

@@ -28,7 +28,7 @@ func CacheDir() string {
return cacheDir
}
// SetCacheDir sets the tricy cacheDir
// SetCacheDir sets the trivy cacheDir
func SetCacheDir(dir string) {
cacheDir = dir
}

View File

@@ -1,143 +0,0 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package vulnerability
import (
context "context"
pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
mock "github.com/stretchr/testify/mock"
types "github.com/aquasecurity/trivy/pkg/types"
)
// MockOperation is an autogenerated mock type for the Operation type
type MockOperation struct {
mock.Mock
}
type OperationFillInfoArgs struct {
Vulns []types.DetectedVulnerability
VulnsAnything bool
ReportType string
ReportTypeAnything bool
}
type OperationFillInfoExpectation struct {
Args OperationFillInfoArgs
}
func (_m *MockOperation) ApplyFillInfoExpectation(e OperationFillInfoExpectation) {
var args []interface{}
if e.Args.VulnsAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Vulns)
}
if e.Args.ReportTypeAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.ReportType)
}
_m.On("FillInfo", args...)
}
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []OperationFillInfoExpectation) {
for _, e := range expectations {
_m.ApplyFillInfoExpectation(e)
}
}
// FillInfo provides a mock function with given fields: vulns, reportType
func (_m *MockOperation) FillInfo(vulns []types.DetectedVulnerability, reportType string) {
_m.Called(vulns, reportType)
}
type OperationFilterArgs struct {
Ctx context.Context
CtxAnything bool
Vulns []types.DetectedVulnerability
VulnsAnything bool
Severities []pkgtypes.Severity
SeveritiesAnything bool
IgnoreUnfixed bool
IgnoreUnfixedAnything bool
IgnoreFile string
IgnoreFileAnything bool
Policy string
PolicyAnything bool
}
type OperationFilterReturns struct {
_a0 []types.DetectedVulnerability
_a1 error
}
type OperationFilterExpectation struct {
Args OperationFilterArgs
Returns OperationFilterReturns
}
func (_m *MockOperation) ApplyFilterExpectation(e OperationFilterExpectation) {
var args []interface{}
if e.Args.CtxAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Ctx)
}
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)
}
if e.Args.PolicyAnything {
args = append(args, mock.Anything)
} else {
args = append(args, e.Args.Policy)
}
_m.On("Filter", args...).Return(e.Returns._a0, e.Returns._a1)
}
func (_m *MockOperation) ApplyFilterExpectations(expectations []OperationFilterExpectation) {
for _, e := range expectations {
_m.ApplyFilterExpectation(e)
}
}
// Filter provides a mock function with given fields: ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy
func (_m *MockOperation) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error) {
ret := _m.Called(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
var r0 []types.DetectedVulnerability
if rf, ok := ret.Get(0).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) []types.DetectedVulnerability); ok {
r0 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.DetectedVulnerability)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) error); ok {
r1 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@@ -1,3 +1,6 @@
# test
# vulnerabilities
CVE-2019-0001
CVE-2019-0002
# misconfigurations
ID100

View File

@@ -0,0 +1,59 @@
- bucket: vulnerability
pairs:
- key: CVE-2019-0001
value:
Title: dos
Description: dos vulnerability
Severity: MEDIUM
References:
- http://example.com
LastModifiedDate: "2020-01-01T01:01:00Z"
PublishedDate: "2001-01-01T01:01:00Z"
- key: CVE-2019-0002
value:
Title: dos
Description: dos vulnerability
Severity: UNKNOWN
VendorSeverity:
nvd: 1
References:
- http://example.com
LastModifiedDate: "2020-01-01T01:01:00Z"
PublishedDate: "2001-01-01T01:01:00Z"
- key: CVE-2019-0003
value:
Title: dos
Description: dos vulnerability
References:
- http://example.com
- key: CVE-2019-0004
value:
Title: dos
Description: dos vulnerability
Severity: MEDIUM
VendorSeverity:
redhat: 1
CVSS:
nvd:
V2Vector: AV:N/AC:L/Au:N/C:P/I:P/A:P
V2Score: 4.5
V3Vector: CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H
V3Score: 5.6
redhat:
V2Vector: AV:N/AC:M/Au:N/C:N/I:P/A:N
V2Score: 7.8
V3Vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
V3Score: 9.8
References:
- http://example.com
CweIDs:
- CWE-311
- key: CVE-2019-0005
value:
Title: COVID-19
Description: a nasty virus vulnerability for humans
Severity: MEDIUM
VendorSeverity:
python-safety-db: 4
References:
- "https://www.who.int/emergencies/diseases/novel-coronavirus-2019"

View File

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

View File

@@ -0,0 +1,6 @@
- bucket: vulnerability
pairs:
- key: CVE-2019-0001
value:
Title:
- aaa

View File

@@ -4,48 +4,32 @@ import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/assert"
)
func TestClient_FillInfo(t *testing.T) {
func TestClient_FillVulnerabilityInfo(t *testing.T) {
type args struct {
vulns []types.DetectedVulnerability
reportType string
}
tests := []struct {
name string
getSeverity []db.OperationGetSeverityExpectation
getVulnerability []db.OperationGetVulnerabilityExpectation
fixtures []string
args args
expectedVulnerabilities []types.DetectedVulnerability
}{
{
name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityMedium.String(),
References: []string{"http://example.com"},
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
},
},
},
},
name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
@@ -68,36 +52,17 @@ func TestClient_FillInfo(t *testing.T) {
},
},
{
name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityUnknown.String(),
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.Nvd: dbTypes.SeverityLow,
},
References: []string{"http://example.com"},
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
},
},
},
},
name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
{VulnerabilityID: "CVE-2019-0002"},
},
reportType: vulnerability.Ubuntu,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
VulnerabilityID: "CVE-2019-0002",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
@@ -107,89 +72,44 @@ func TestClient_FillInfo(t *testing.T) {
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
},
SeveritySource: vulnerability.Nvd,
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0002",
},
},
},
{
name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
References: []string{"http://example.com"},
},
},
},
},
name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
{VulnerabilityID: "CVE-2019-0003"},
},
reportType: vulnerability.Ubuntu,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
VulnerabilityID: "CVE-2019-0003",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityUnknown.String(),
References: []string{"http://example.com"},
},
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0003",
},
},
},
{
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
Severity: dbTypes.SeverityMedium.String(),
CweIDs: []string{"CWE-311"},
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.RedHat: dbTypes.SeverityLow, // CentOS uses RedHat
},
CVSS: map[string]dbTypes.CVSS{
vulnerability.Nvd: {
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
V2Score: 4.5,
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 5.6,
},
vulnerability.RedHat: {
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V2Score: 7.8,
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 9.8,
},
},
References: []string{"http://example.com"},
},
},
},
},
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
{VulnerabilityID: "CVE-2019-0004"},
},
reportType: vulnerability.CentOS,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
VulnerabilityID: "CVE-2019-0004",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
@@ -198,7 +118,7 @@ func TestClient_FillInfo(t *testing.T) {
References: []string{"http://example.com"},
CVSS: map[string]dbTypes.CVSS{
vulnerability.Nvd: {
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
V2Vector: "AV:N/AC:L/Au:N/C:P/I:P/A:P",
V2Score: 4.5,
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 5.6,
@@ -212,104 +132,63 @@ func TestClient_FillInfo(t *testing.T) {
},
},
SeveritySource: vulnerability.RedHat,
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0004",
},
},
},
{
name: "happy path light db, with only OS vulnerability, yes vendor severity",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityMedium.String(),
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.Ubuntu: dbTypes.SeverityLow,
},
},
},
},
},
name: "happy path light db, with only OS vulnerability, yes vendor severity",
fixtures: []string{"testdata/fixtures/light.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"},
{VulnerabilityID: "CVE-2020-0001"},
},
reportType: vulnerability.Ubuntu,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
VulnerabilityID: "CVE-2020-0001",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityLow.String(),
},
SeveritySource: vulnerability.Ubuntu,
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
},
},
},
{
name: "happy path light db, with only OS vulnerability, no vendor severity",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2020-28928",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
VendorSeverity: dbTypes.VendorSeverity{},
},
},
},
},
name: "happy path light db, with only OS vulnerability, no vendor severity",
fixtures: []string{"testdata/fixtures/light.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-28928"},
{VulnerabilityID: "CVE-2020-0002"},
},
reportType: vulnerability.Alpine,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-28928",
VulnerabilityID: "CVE-2020-0002",
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityUnknown.String(),
},
SeveritySource: "",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-28928",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0002",
},
},
},
{
name: "happy path, with only library vulnerability",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2020-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "COVID-19",
Description: "a nasty virus vulnerability for humans",
Severity: dbTypes.SeverityMedium.String(),
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.PythonSafetyDB: dbTypes.SeverityCritical,
},
References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
},
},
},
},
name: "happy path, with only library vulnerability",
fixtures: []string{"testdata/fixtures/full.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-0001"},
{VulnerabilityID: "CVE-2019-0005"},
},
reportType: "poetry",
reportType: ftypes.Poetry,
},
expectedVulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
VulnerabilityID: "CVE-2019-0005",
Vulnerability: dbTypes.Vulnerability{
Title: "COVID-19",
Description: "a nasty virus vulnerability for humans",
@@ -317,22 +196,13 @@ func TestClient_FillInfo(t *testing.T) {
References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
},
SeveritySource: vulnerability.PythonSafetyDB,
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0005",
},
},
},
{
name: "GetVulnerability returns an error",
getVulnerability: []db.OperationGetVulnerabilityExpectation{
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0004",
},
Returns: db.OperationGetVulnerabilityReturns{
Err: xerrors.New("failed"),
},
},
},
name: "GetVulnerability returns an error",
fixtures: []string{"testdata/fixtures/sad.yaml"},
args: args{
vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0004"},
@@ -346,17 +216,15 @@ func TestClient_FillInfo(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockDBConfig := new(db.MockOperation)
mockDBConfig.ApplyGetSeverityExpectations(tt.getSeverity)
mockDBConfig.ApplyGetVulnerabilityExpectations(tt.getVulnerability)
dbtest.InitDB(t, tt.fixtures)
defer db.Close()
c := Client{
dbc: mockDBConfig,
dbc: db.Config{},
}
c.FillInfo(tt.args.vulns, tt.args.reportType)
assert.Equal(t, tt.expectedVulnerabilities, tt.args.vulns, tt.name)
mockDBConfig.AssertExpectations(t)
})
}
}
@@ -452,9 +320,9 @@ func TestClient_Filter(t *testing.T) {
policyFile string
}
tests := []struct {
name string
args args
want []types.DetectedVulnerability
name string
args args
wantVulns []types.DetectedVulnerability
}{
{
name: "happy path",
@@ -509,7 +377,7 @@ func TestClient_Filter(t *testing.T) {
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false,
},
want: []types.DetectedVulnerability{
wantVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "bar",
@@ -613,7 +481,7 @@ func TestClient_Filter(t *testing.T) {
ignoreUnfixed: false,
ignoreFile: "testdata/.trivyignore",
},
want: []types.DetectedVulnerability{
wantVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0003",
PkgName: "foo",
@@ -663,7 +531,7 @@ func TestClient_Filter(t *testing.T) {
ignoreUnfixed: false,
policyFile: "./testdata/test.rego",
},
want: []types.DetectedVulnerability{
wantVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "foo",
@@ -755,7 +623,7 @@ func TestClient_Filter(t *testing.T) {
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false,
},
want: []types.DetectedVulnerability{
wantVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2018-0001",
PkgName: "bar",
@@ -807,10 +675,9 @@ func TestClient_Filter(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := Client{}
got, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities,
tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
gotVulns, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities, tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
require.NoError(t, err)
assert.Equal(t, tt.want, got, tt.name)
assert.Equal(t, tt.wantVulns, gotVulns)
})
}
}

View File

@@ -7,8 +7,8 @@ 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"
_ "google.golang.org/protobuf/types/known/emptypb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
math "math"
)
@@ -24,15 +24,15 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ArtifactInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
Created *timestamp.Timestamp `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
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:"-"`
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 *timestamppb.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 *ArtifactInfo) Reset() { *m = ArtifactInfo{} }
@@ -74,7 +74,7 @@ func (m *ArtifactInfo) GetArchitecture() string {
return ""
}
func (m *ArtifactInfo) GetCreated() *timestamp.Timestamp {
func (m *ArtifactInfo) GetCreated() *timestamppb.Timestamp {
if m != nil {
return m.Created
}

View File

@@ -17,23 +17,19 @@ import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
import twirp "github.com/twitchtv/twirp"
import ctxsetters "github.com/twitchtv/twirp/ctxsetters"
import google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
import google_protobuf1 "google.golang.org/protobuf/types/known/emptypb"
// Imports only used by utility functions:
import io "io"
import json "encoding/json"
import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// ===============
// Cache Interface
// ===============
@@ -420,16 +416,6 @@ func (s *cacheServer) servePutArtifactProtobuf(ctx context.Context, resp http.Re
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -559,16 +545,6 @@ func (s *cacheServer) servePutBlobProtobuf(ctx context.Context, resp http.Respon
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -698,16 +674,6 @@ func (s *cacheServer) serveMissingBlobsProtobuf(ctx context.Context, resp http.R
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -866,7 +832,6 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -1119,15 +1084,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp)
}
r := resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
r, err = gzip.NewReader(r)
if err != nil {
return wrapInternal(err, "invalid gzip")
}
}
respBodyBytes, err := ioutil.ReadAll(r)
respBodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
@@ -1232,36 +1189,6 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
// compressWithGzip compresses the data with gzip
func compressWithGzip(data []byte) ([]byte, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
defer gz.Close()
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isGZipAcceptable(request *http.Request) bool {
for _, encoding := range request.Header["Accept-Encoding"] {
if encoding == "gzip" {
return true
}
}
return false
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return

View File

@@ -6,7 +6,7 @@ package common
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
math "math"
)
@@ -359,24 +359,24 @@ func (m *Library) GetVersion() string {
}
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"`
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
Cvss map[string]*CVSS `protobuf:"bytes,12,rep,name=cvss,proto3" json:"cvss,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
CweIds []string `protobuf:"bytes,13,rep,name=cwe_ids,json=cweIds,proto3" json:"cwe_ids,omitempty"`
PrimaryUrl string `protobuf:"bytes,14,opt,name=primary_url,json=primaryUrl,proto3" json:"primary_url,omitempty"`
PublishedDate *timestamp.Timestamp `protobuf:"bytes,15,opt,name=published_date,json=publishedDate,proto3" json:"published_date,omitempty"`
LastModifiedDate *timestamp.Timestamp `protobuf:"bytes,16,opt,name=last_modified_date,json=lastModifiedDate,proto3" json:"last_modified_date,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
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"`
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
Cvss map[string]*CVSS `protobuf:"bytes,12,rep,name=cvss,proto3" json:"cvss,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
CweIds []string `protobuf:"bytes,13,rep,name=cwe_ids,json=cweIds,proto3" json:"cwe_ids,omitempty"`
PrimaryUrl string `protobuf:"bytes,14,opt,name=primary_url,json=primaryUrl,proto3" json:"primary_url,omitempty"`
PublishedDate *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=published_date,json=publishedDate,proto3" json:"published_date,omitempty"`
LastModifiedDate *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=last_modified_date,json=lastModifiedDate,proto3" json:"last_modified_date,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
@@ -495,14 +495,14 @@ func (m *Vulnerability) GetPrimaryUrl() string {
return ""
}
func (m *Vulnerability) GetPublishedDate() *timestamp.Timestamp {
func (m *Vulnerability) GetPublishedDate() *timestamppb.Timestamp {
if m != nil {
return m.PublishedDate
}
return nil
}
func (m *Vulnerability) GetLastModifiedDate() *timestamp.Timestamp {
func (m *Vulnerability) GetLastModifiedDate() *timestamppb.Timestamp {
if m != nil {
return m.LastModifiedDate
}

View File

@@ -86,6 +86,7 @@ func (m *ScanRequest) GetOptions() *ScanOptions {
type ScanOptions struct {
VulnType []string `protobuf:"bytes,1,rep,name=vuln_type,json=vulnType,proto3" json:"vuln_type,omitempty"`
SecurityChecks []string `protobuf:"bytes,2,rep,name=security_checks,json=securityChecks,proto3" json:"security_checks,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -123,6 +124,13 @@ func (m *ScanOptions) GetVulnType() []string {
return nil
}
func (m *ScanOptions) GetSecurityChecks() []string {
if m != nil {
return m.SecurityChecks
}
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"`
@@ -244,29 +252,30 @@ func init() {
func init() { proto.RegisterFile("rpc/scanner/service.proto", fileDescriptor_60d0e837512b18d4) }
var fileDescriptor_60d0e837512b18d4 = []byte{
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
// 395 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8f, 0x9b, 0x30,
0x10, 0x85, 0x05, 0x89, 0x42, 0x18, 0xaa, 0xee, 0xca, 0x87, 0x8a, 0xdd, 0x55, 0x5b, 0xc4, 0xa5,
0x39, 0x81, 0x4a, 0xa5, 0xf6, 0xde, 0x2a, 0x87, 0x9c, 0x52, 0x99, 0xaa, 0x87, 0x5e, 0x90, 0x31,
0x6e, 0x62, 0x95, 0x60, 0x62, 0x1b, 0x54, 0xd4, 0x7f, 0xd2, 0x5f, 0x5b, 0x61, 0x83, 0xd4, 0x64,
0x95, 0xdb, 0xf8, 0xcd, 0x63, 0x78, 0xdf, 0xd8, 0xf0, 0x20, 0x5b, 0x9a, 0x2a, 0x4a, 0x9a, 0x86,
0xc9, 0x54, 0x31, 0xd9, 0x73, 0xca, 0x92, 0x56, 0x0a, 0x2d, 0xd0, 0xbd, 0x96, 0xbc, 0x1f, 0x92,
0xa9, 0x99, 0xf4, 0xef, 0x1f, 0x3f, 0x1e, 0xb8, 0x3e, 0x76, 0x65, 0x42, 0xc5, 0x29, 0x25, 0xe7,
0x8e, 0x28, 0x46, 0x3b, 0xc9, 0xf5, 0x90, 0x1a, 0x67, 0x3a, 0x8e, 0xa2, 0xe2, 0x74, 0x12, 0xcd,
0xe5, 0xa4, 0xf8, 0xaf, 0x03, 0x41, 0x4e, 0x49, 0x83, 0xd9, 0xb9, 0x63, 0x4a, 0xa3, 0x57, 0xb0,
0xd2, 0x44, 0x1e, 0x98, 0x0e, 0x9d, 0xc8, 0xd9, 0xf8, 0x78, 0x3a, 0xa1, 0xb7, 0x10, 0x10, 0xa9,
0xf9, 0x4f, 0x42, 0x75, 0xc1, 0xab, 0xd0, 0x35, 0x4d, 0x98, 0xa5, 0x5d, 0x85, 0x1e, 0x60, 0x5d,
0xd6, 0xa2, 0x2c, 0x78, 0xa5, 0xc2, 0x45, 0xb4, 0xd8, 0xf8, 0xd8, 0x1b, 0xcf, 0xbb, 0x4a, 0xa1,
0x4f, 0xe0, 0x89, 0x56, 0x73, 0xd1, 0xa8, 0x70, 0x19, 0x39, 0x9b, 0x20, 0x7b, 0x9d, 0x5c, 0xe7,
0x4f, 0xc6, 0x0c, 0x7b, 0x6b, 0xc2, 0xb3, 0x3b, 0xce, 0x6d, 0xb6, 0x49, 0x47, 0x4f, 0xe0, 0xf7,
0x5d, 0xdd, 0x14, 0x7a, 0x68, 0x59, 0xe8, 0x98, 0x7f, 0xac, 0x47, 0xe1, 0xdb, 0xd0, 0x32, 0xf4,
0x0e, 0xee, 0x66, 0xe6, 0x82, 0x1e, 0x19, 0xfd, 0xa5, 0x42, 0xd7, 0x58, 0x5e, 0xce, 0xf2, 0x17,
0xa3, 0xc6, 0xbf, 0xe1, 0x85, 0x05, 0x56, 0xad, 0x68, 0x14, 0x43, 0x11, 0xb8, 0x42, 0x19, 0xda,
0x20, 0xbb, 0x9f, 0x82, 0xd9, 0x55, 0x25, 0xfb, 0x1c, 0xbb, 0x42, 0x21, 0x04, 0x4b, 0x26, 0x54,
0x6d, 0xa0, 0xd7, 0xd8, 0xd4, 0x28, 0x03, 0x4f, 0x32, 0xd5, 0xd5, 0xda, 0xd2, 0x06, 0x59, 0xf8,
0x9c, 0x09, 0x1b, 0x03, 0x9e, 0x8d, 0xf1, 0x1f, 0x58, 0x59, 0xe9, 0xe6, 0x96, 0xb7, 0x70, 0x37,
0x02, 0x31, 0x49, 0x4a, 0x5e, 0x73, 0xcd, 0x99, 0x85, 0x08, 0xb2, 0xa7, 0xcb, 0x60, 0xdf, 0xff,
0x33, 0x0d, 0xf8, 0xfa, 0x9b, 0x31, 0xb0, 0xd9, 0xd1, 0xc2, 0x0c, 0x37, 0x75, 0xf6, 0x15, 0xbc,
0xdc, 0x46, 0x43, 0x5b, 0x58, 0x8e, 0x25, 0xba, 0x71, 0x0d, 0xd3, 0x53, 0x78, 0x7c, 0x73, 0xab,
0x6d, 0x17, 0xf7, 0xd9, 0xff, 0xe1, 0x4d, 0xad, 0x72, 0x65, 0x1e, 0xd3, 0x87, 0x7f, 0x01, 0x00,
0x00, 0xff, 0xff, 0xfd, 0x72, 0xf0, 0x54, 0xb3, 0x02, 0x00, 0x00,
}

View File

@@ -17,7 +17,8 @@ message ScanRequest {
}
message ScanOptions {
repeated string vuln_type = 1;
repeated string vuln_type = 1;
repeated string security_checks = 2;
}
message ScanResponse {

View File

@@ -17,7 +17,6 @@ import fmt "fmt"
import ioutil "io/ioutil"
import http "net/http"
import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto"
@@ -29,9 +28,6 @@ import io "io"
import json "encoding/json"
import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// =================
// Scanner Interface
// =================
@@ -324,16 +320,6 @@ func (s *scannerServer) serveScanProtobuf(ctx context.Context, resp http.Respons
return
}
// Compress the response if the size exceeds the threshold
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
respBytes, err = compressWithGzip(respBytes)
if err != nil {
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
return
}
resp.Header().Set("Content-Encoding", "gzip")
}
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -492,7 +478,6 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
}
req.Header.Set("Accept", contentType)
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil
}
@@ -745,15 +730,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp)
}
r := resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
r, err = gzip.NewReader(r)
if err != nil {
return wrapInternal(err, "invalid gzip")
}
}
respBodyBytes, err := ioutil.ReadAll(r)
respBodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return wrapInternal(err, "failed to read response body")
}
@@ -858,36 +835,6 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
return h.Error(ctx, err)
}
// compressWithGzip compresses the data with gzip
func compressWithGzip(data []byte) ([]byte, error) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
defer gz.Close()
if _, err := gz.Write(data); err != nil {
return nil, err
}
if err := gz.Flush(); err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isGZipAcceptable(request *http.Request) bool {
for _, encoding := range request.Header["Accept-Encoding"] {
if encoding == "gzip" {
return true
}
}
return false
}
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil {
return
@@ -910,29 +857,30 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
}
var twirpFileDescriptor0 = []byte{
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
// 395 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8f, 0x9b, 0x30,
0x10, 0x85, 0x05, 0x89, 0x42, 0x18, 0xaa, 0xee, 0xca, 0x87, 0x8a, 0xdd, 0x55, 0x5b, 0xc4, 0xa5,
0x39, 0x81, 0x4a, 0xa5, 0xf6, 0xde, 0x2a, 0x87, 0x9c, 0x52, 0x99, 0xaa, 0x87, 0x5e, 0x90, 0x31,
0x6e, 0x62, 0x95, 0x60, 0x62, 0x1b, 0x54, 0xd4, 0x7f, 0xd2, 0x5f, 0x5b, 0x61, 0x83, 0xd4, 0x64,
0x95, 0xdb, 0xf8, 0xcd, 0x63, 0x78, 0xdf, 0xd8, 0xf0, 0x20, 0x5b, 0x9a, 0x2a, 0x4a, 0x9a, 0x86,
0xc9, 0x54, 0x31, 0xd9, 0x73, 0xca, 0x92, 0x56, 0x0a, 0x2d, 0xd0, 0xbd, 0x96, 0xbc, 0x1f, 0x92,
0xa9, 0x99, 0xf4, 0xef, 0x1f, 0x3f, 0x1e, 0xb8, 0x3e, 0x76, 0x65, 0x42, 0xc5, 0x29, 0x25, 0xe7,
0x8e, 0x28, 0x46, 0x3b, 0xc9, 0xf5, 0x90, 0x1a, 0x67, 0x3a, 0x8e, 0xa2, 0xe2, 0x74, 0x12, 0xcd,
0xe5, 0xa4, 0xf8, 0xaf, 0x03, 0x41, 0x4e, 0x49, 0x83, 0xd9, 0xb9, 0x63, 0x4a, 0xa3, 0x57, 0xb0,
0xd2, 0x44, 0x1e, 0x98, 0x0e, 0x9d, 0xc8, 0xd9, 0xf8, 0x78, 0x3a, 0xa1, 0xb7, 0x10, 0x10, 0xa9,
0xf9, 0x4f, 0x42, 0x75, 0xc1, 0xab, 0xd0, 0x35, 0x4d, 0x98, 0xa5, 0x5d, 0x85, 0x1e, 0x60, 0x5d,
0xd6, 0xa2, 0x2c, 0x78, 0xa5, 0xc2, 0x45, 0xb4, 0xd8, 0xf8, 0xd8, 0x1b, 0xcf, 0xbb, 0x4a, 0xa1,
0x4f, 0xe0, 0x89, 0x56, 0x73, 0xd1, 0xa8, 0x70, 0x19, 0x39, 0x9b, 0x20, 0x7b, 0x9d, 0x5c, 0xe7,
0x4f, 0xc6, 0x0c, 0x7b, 0x6b, 0xc2, 0xb3, 0x3b, 0xce, 0x6d, 0xb6, 0x49, 0x47, 0x4f, 0xe0, 0xf7,
0x5d, 0xdd, 0x14, 0x7a, 0x68, 0x59, 0xe8, 0x98, 0x7f, 0xac, 0x47, 0xe1, 0xdb, 0xd0, 0x32, 0xf4,
0x0e, 0xee, 0x66, 0xe6, 0x82, 0x1e, 0x19, 0xfd, 0xa5, 0x42, 0xd7, 0x58, 0x5e, 0xce, 0xf2, 0x17,
0xa3, 0xc6, 0xbf, 0xe1, 0x85, 0x05, 0x56, 0xad, 0x68, 0x14, 0x43, 0x11, 0xb8, 0x42, 0x19, 0xda,
0x20, 0xbb, 0x9f, 0x82, 0xd9, 0x55, 0x25, 0xfb, 0x1c, 0xbb, 0x42, 0x21, 0x04, 0x4b, 0x26, 0x54,
0x6d, 0xa0, 0xd7, 0xd8, 0xd4, 0x28, 0x03, 0x4f, 0x32, 0xd5, 0xd5, 0xda, 0xd2, 0x06, 0x59, 0xf8,
0x9c, 0x09, 0x1b, 0x03, 0x9e, 0x8d, 0xf1, 0x1f, 0x58, 0x59, 0xe9, 0xe6, 0x96, 0xb7, 0x70, 0x37,
0x02, 0x31, 0x49, 0x4a, 0x5e, 0x73, 0xcd, 0x99, 0x85, 0x08, 0xb2, 0xa7, 0xcb, 0x60, 0xdf, 0xff,
0x33, 0x0d, 0xf8, 0xfa, 0x9b, 0x31, 0xb0, 0xd9, 0xd1, 0xc2, 0x0c, 0x37, 0x75, 0xf6, 0x15, 0xbc,
0xdc, 0x46, 0x43, 0x5b, 0x58, 0x8e, 0x25, 0xba, 0x71, 0x0d, 0xd3, 0x53, 0x78, 0x7c, 0x73, 0xab,
0x6d, 0x17, 0xf7, 0xd9, 0xff, 0xe1, 0x4d, 0xad, 0x72, 0x65, 0x1e, 0xd3, 0x87, 0x7f, 0x01, 0x00,
0x00, 0xff, 0xff, 0xfd, 0x72, 0xf0, 0x54, 0xb3, 0x02, 0x00, 0x00,
}