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 - name: Lint
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
with: with:
version: v1.31 version: v1.39
args: --deadline=30m args: --deadline=30m
- name: Run unit tests - name: Run unit tests

View File

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

6
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible github.com/Masterminds/sprig v2.22.0+incompatible
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 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-dep-parser v0.0.0-20210427143403-3c97ccc53976
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 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/docker v20.10.3+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d // indirect 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/go-redis/redis/v8 v8.4.0
github.com/goccy/go-yaml v1.8.2 // indirect github.com/goccy/go-yaml v1.8.2 // indirect
github.com/golang/protobuf v1.4.3 github.com/golang/protobuf v1.4.3
github.com/google/go-containerregistry v0.1.2 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/google/wire v0.4.0
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/hashicorp/go-getter v1.5.2 github.com/hashicorp/go-getter v1.5.2
@@ -46,6 +47,7 @@ require (
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.16.0 go.uber.org/zap v1.16.0
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 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 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.25.0 google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect 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/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/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/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/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/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= 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/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/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-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 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/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 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8= 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-20210519050514-051631be3f69 h1:J7uGm7PSBI4yAACkaxH4/PTdfhQYIQy7tSlwUknr4U8=
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70/go.mod h1:cPTOJcf8bdP24oXhBsPVVczcjkICcApAwAOsn6CpHTI= github.com/aquasecurity/fanal v0.0.0-20210519050514-051631be3f69/go.mod h1://8XzKt4IvM6QUSIYpY5KURCEArOzscy6R0FsmVLEBo=
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/go-dep-parser v0.0.0-20210427143403-3c97ccc53976 h1:ypl/IDxujzEymmwtzGJqQyboI2oZr1se+OoYaGqgBzQ= 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-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= 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 v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= 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 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/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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-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-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/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= 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-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 h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 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/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-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 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 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.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.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/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 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/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/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= 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/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/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.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/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.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/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-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 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 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/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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= 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 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.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/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 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/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/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= 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 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= 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/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/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 h1:zTQuUMvB5xkYixKB9LFVbUd7DcUt1jfS0QKTo+/Vfyc=
github.com/open-policy-agent/opa v0.25.2/go.mod h1:iGThTRECCfKQKICueOZkXUi0opN7BR3qiAnIrNHCmlI= 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/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-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/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/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.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/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 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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.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 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= 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= 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/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/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.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= 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.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/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)", "Target": "testdata/fixtures/opensuse-leap-423.tar.gz (opensuse.leap 42.3)",
"Type": "opensuse.leap", "Type": "opensuse.leap"
"Vulnerabilities": null
} }
] ]

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 // PutArtifact sends artifact to remote client
func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) error { func (c RemoteCache) PutArtifact(imageID string, artifactInfo types.ArtifactInfo) error {
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, imageInfo)) _, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, artifactInfo))
if err != nil { if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err) 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 // PutBlob sends blobInfo to remote client
func (c RemoteCache) PutBlob(diffID string, layerInfo types.BlobInfo) error { func (c RemoteCache) PutBlob(diffID string, blobInfo types.BlobInfo) error {
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, layerInfo)) _, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, blobInfo))
if err != nil { if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err) 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/urfave/cli/v2"
"github.com/aquasecurity/trivy-db/pkg/db" "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/artifact"
"github.com/aquasecurity/trivy/pkg/commands/client" "github.com/aquasecurity/trivy/pkg/commands/client"
"github.com/aquasecurity/trivy/pkg/commands/plugin" "github.com/aquasecurity/trivy/pkg/commands/plugin"
"github.com/aquasecurity/trivy/pkg/commands/server" "github.com/aquasecurity/trivy/pkg/commands/server"
tdb "github.com/aquasecurity/trivy/pkg/db" tdb "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils" "github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnerability" "github.com/aquasecurity/trivy/pkg/vulnerability"
) )
@@ -56,7 +57,7 @@ var (
severityFlag = cli.StringFlag{ severityFlag = cli.StringFlag{
Name: "severity", Name: "severity",
Aliases: []string{"s"}, Aliases: []string{"s"},
Value: strings.Join(types.SeverityNames, ","), Value: strings.Join(dbTypes.SeverityNames, ","),
Usage: "severities of vulnerabilities to be displayed (comma separated)", Usage: "severities of vulnerabilities to be displayed (comma separated)",
EnvVars: []string{"TRIVY_SEVERITY"}, EnvVars: []string{"TRIVY_SEVERITY"},
} }
@@ -134,11 +135,19 @@ var (
vulnTypeFlag = cli.StringFlag{ vulnTypeFlag = cli.StringFlag{
Name: "vuln-type", Name: "vuln-type",
Value: "os,library", Value: strings.Join([]string{types.VulnTypeOS, types.VulnTypeLibrary}, ","),
Usage: "comma-separated list of vulnerability types (os,library)", Usage: "comma-separated list of vulnerability types (os,library)",
EnvVars: []string{"TRIVY_VULN_TYPE"}, 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{ cacheDirFlag = cli.StringFlag{
Name: "cache-dir", Name: "cache-dir",
Value: utils.DefaultCacheDir(), Value: utils.DefaultCacheDir(),
@@ -231,6 +240,7 @@ var (
&ignoreUnfixedFlag, &ignoreUnfixedFlag,
&removedPkgsFlag, &removedPkgsFlag,
&vulnTypeFlag, &vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&timeoutFlag, &timeoutFlag,
&lightFlag, &lightFlag,
@@ -405,6 +415,7 @@ func NewFilesystemCommand() *cli.Command {
&ignoreUnfixedFlag, &ignoreUnfixedFlag,
&removedPkgsFlag, &removedPkgsFlag,
&vulnTypeFlag, &vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&cacheBackendFlag, &cacheBackendFlag,
&timeoutFlag, &timeoutFlag,
@@ -437,6 +448,7 @@ func NewRepositoryCommand() *cli.Command {
&ignoreUnfixedFlag, &ignoreUnfixedFlag,
&removedPkgsFlag, &removedPkgsFlag,
&vulnTypeFlag, &vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&cacheBackendFlag, &cacheBackendFlag,
&timeoutFlag, &timeoutFlag,
@@ -468,6 +480,7 @@ func NewClientCommand() *cli.Command {
&ignoreUnfixedFlag, &ignoreUnfixedFlag,
&removedPkgsFlag, &removedPkgsFlag,
&vulnTypeFlag, &vulnTypeFlag,
&securityChecksFlag,
&ignoreFileFlag, &ignoreFileFlag,
&timeoutFlag, &timeoutFlag,
&ignorePolicy, &ignorePolicy,
@@ -519,9 +532,10 @@ func NewServerCommand() *cli.Command {
// NewPluginCommand is the factory method to add plugin command // NewPluginCommand is the factory method to add plugin command
func NewPluginCommand() *cli.Command { func NewPluginCommand() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "plugin", Name: "plugin",
Aliases: []string{"p"}, Aliases: []string{"p"},
Usage: "manage plugins", ArgsUsage: "plugin_uri",
Usage: "manage plugins",
Subcommands: cli.Commands{ Subcommands: cli.Commands{
{ {
Name: "install", 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" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/scanner"
) )
func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
_ time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) { _ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled) s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled, opt)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err) 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 // FilesystemRun runs scan on filesystem
func FilesystemRun(ctx *cli.Context) error { func FilesystemRun(ctx *cli.Context) error {
c, err := NewConfig(ctx) opt, err := NewOption(ctx)
if err != nil { if err != nil {
return err return xerrors.Errorf("option error: %w", err)
} }
// initialize config // initialize options
if err = c.Init(); err != nil { if err = opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err) 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" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/scanner"
) )
func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) { timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled) s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled, opt)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err) 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, func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
timeout time.Duration, disabled []analyzer.Type) ( timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
scanner.Scanner, func(), error) { s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled, opt)
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err) 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 // ImageRun runs scan on docker image
func ImageRun(ctx *cli.Context) error { func ImageRun(ctx *cli.Context) error {
c, err := NewConfig(ctx) opt, err := NewOption(ctx)
if err != nil { if err != nil {
return err return xerrors.Errorf("option error: %w", err)
} }
// initialize config // initialize options
if err = c.Init(); err != nil { if err = opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err) return xerrors.Errorf("option initialize error: %w", err)
} }
if c.Input != "" { if opt.Input != "" {
// scan tar file // 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/google/wire"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/vulnerability" "github.com/aquasecurity/trivy/pkg/vulnerability"
) )
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) ( localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
scanner.Scanner, func(), error) { configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet) wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) ( localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
scanner.Scanner, error) { configScannerOption config.ScannerOption) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet) wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil return scanner.Scanner{}, nil
} }
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, 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) wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, 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) wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil return scanner.Scanner{}, nil, nil
} }
func initializeVulnerabilityClient() vulnerability.Client { func initializeResultClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet) wire.Build(vulnerability.SuperSet)
return vulnerability.Client{} 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" "go.uber.org/zap/zaptest/observer"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types" 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 { tests := []struct {
name string name string
globalConfig config.GlobalConfig args []string
dbConfig config.DBConfig logs []string
imageConfig config.ImageConfig want Option
reportConfig config.ReportConfig wantErr string
args []string
logs []string
want Config
wantErr string
}{ }{
{ {
name: "happy path", 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"}, args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
want: Config{ want: Option{
GlobalConfig: config.GlobalConfig{ GlobalOption: option.GlobalOption{
Quiet: true, Quiet: true,
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "alpine:3.10", Target: "alpine:3.10",
}, },
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical}, Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
VulnType: []string{"os"}, VulnType: []string{types.VulnTypeOS},
Output: os.Stdout, SecurityChecks: []string{types.SecurityCheckVulnerability},
Output: os.Stdout,
}, },
}, },
}, },
{ {
name: "happy path: reset", name: "happy path: reset",
args: []string{"--reset"}, args: []string{"--reset"},
want: Config{ want: Option{
DBConfig: config.DBConfig{ DBOption: option.DBOption{
Reset: true, Reset: true,
}, },
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical}, Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
}, },
}, },
}, },
@@ -68,13 +63,14 @@ func TestConfig_Init(t *testing.T) {
logs: []string{ logs: []string{
"unknown severity option: unknown severity: INVALID", "unknown severity option: unknown severity: INVALID",
}, },
want: Config{ want: Option{
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown}, Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "centos:7", Target: "centos:7",
}, },
}, },
@@ -85,13 +81,14 @@ func TestConfig_Init(t *testing.T) {
logs: []string{ logs: []string{
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.", "--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
}, },
want: Config{ want: Option{
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityLow}, Severities: []dbTypes.Severity{dbTypes.SeverityLow},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
SecurityChecks: []string{types.SecurityCheckVulnerability},
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "debian:buster", Target: "debian:buster",
}, },
onlyUpdate: "alpine", onlyUpdate: "alpine",
@@ -103,14 +100,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{ logs: []string{
"--template is ignored because --format template is not specified. Use --template option with --format template option.", "--template is ignored because --format template is not specified. Use --template option with --format template option.",
}, },
want: Config{ want: Option{
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical}, Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
Template: "@contrib/gitlab.tpl", SecurityChecks: []string{types.SecurityCheckVulnerability},
Template: "@contrib/gitlab.tpl",
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0", Target: "gitlab/gitlab-ce:12.7.2-ce.0",
}, },
}, },
@@ -121,15 +119,16 @@ func TestConfig_Init(t *testing.T) {
logs: []string{ logs: []string{
"--template is ignored because --format json is specified. Use --template option with --format template option.", "--template is ignored because --format json is specified. Use --template option with --format template option.",
}, },
want: Config{ want: Option{
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical}, Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
Template: "@contrib/gitlab.tpl", SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "json", Template: "@contrib/gitlab.tpl",
Format: "json",
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0", Target: "gitlab/gitlab-ce:12.7.2-ce.0",
}, },
}, },
@@ -140,14 +139,15 @@ func TestConfig_Init(t *testing.T) {
logs: []string{ logs: []string{
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.", "--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
}, },
want: Config{ want: Option{
ReportConfig: config.ReportConfig{ ReportOption: option.ReportOption{
Severities: []dbTypes.Severity{dbTypes.SeverityMedium}, Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
Output: os.Stdout, Output: os.Stdout,
VulnType: []string{"os", "library"}, VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
Format: "template", SecurityChecks: []string{types.SecurityCheckVulnerability},
Format: "template",
}, },
ArtifactConfig: config.ArtifactConfig{ ArtifactOption: option.ArtifactOption{
Target: "gitlab/gitlab-ce:12.7.2-ce.0", 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.Bool("auto-refresh", false, "")
set.String("severity", "CRITICAL", "") set.String("severity", "CRITICAL", "")
set.String("vuln-type", "os,library", "") set.String("vuln-type", "os,library", "")
set.String("security-checks", "vuln", "")
set.String("only-update", "", "") set.String("only-update", "", "")
set.String("template", "", "") set.String("template", "", "")
set.String("format", "", "") set.String("format", "", "")
@@ -188,10 +189,10 @@ func TestConfig_Init(t *testing.T) {
ctx := cli.NewContext(app, set, nil) ctx := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args) _ = set.Parse(tt.args)
c, err := NewConfig(ctx) c, err := NewOption(ctx)
require.NoError(t, err, err) require.NoError(t, err, err)
c.GlobalConfig.Logger = logger.Sugar() c.GlobalOption.Logger = logger.Sugar()
err = c.Init() err = c.Init()
// tests log messages // tests log messages
@@ -211,8 +212,8 @@ func TestConfig_Init(t *testing.T) {
assert.NoError(t, err, tt.name) assert.NoError(t, err, tt.name)
} }
tt.want.GlobalConfig.Context = ctx tt.want.GlobalOption.Context = ctx
tt.want.GlobalConfig.Logger = logger.Sugar() tt.want.GlobalOption.Logger = logger.Sugar()
assert.Equal(t, tt.want, c, tt.name) assert.Equal(t, tt.want, c, tt.name)
}) })
} }

View File

@@ -8,14 +8,14 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/scanner"
) )
func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
_ time.Duration, disabled []analyzer.Type) ( _ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
scanner.Scanner, func(), error) { s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled, opt)
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled)
if err != nil { if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err) 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 // RepositoryRun runs scan on repository
func RepositoryRun(ctx *cli.Context) error { func RepositoryRun(ctx *cli.Context) error {
c, err := NewConfig(ctx) opt, err := NewOption(ctx)
if err != nil { if err != nil {
return err return xerrors.Errorf("option error: %w", err)
} }
// initialize config // initialize options
if err = c.Init(); err != nil { if err = opt.Init(); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err) 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 ( import (
"context" "context"
"errors" "errors"
l "log"
"os" "os"
"time" "time"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/commands/operation" "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 // InitializeScanner type to define initialize function signature
type InitializeScanner func(context.Context, string, cache.ArtifactCache, cache.LocalArtifactCache, time.Duration, 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 { // InitCache defines cache initializer
ctx, cancel := context.WithTimeout(ctx, conf.Timeout) 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() defer cancel()
err := runWithTimeout(ctx, conf, initializeScanner) return runWithTimeout(ctx, opt, initializeScanner, initCache)
if xerrors.Is(err, context.DeadlineExceeded) {
log.Logger.Warn("Increase --timeout value")
}
return err
} }
func runWithTimeout(ctx context.Context, conf Config, initializeScanner InitializeScanner) error { func runWithTimeout(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error {
if err := log.InitLogger(conf.Debug, conf.Quiet); err != nil { if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil {
l.Fatal(err) return err
} }
cacheClient, err := initCache(conf) cacheClient, err := initCache(opt)
if err != nil { if err != nil {
if errors.Is(err, errSkipScan) { if errors.Is(err, errSkipScan) {
return nil return nil
@@ -51,34 +51,37 @@ func runWithTimeout(ctx context.Context, conf Config, initializeScanner Initiali
} }
defer cacheClient.Close() defer cacheClient.Close()
if err = initDB(conf); err != nil { // When scanning config files, it doesn't need to download the vulnerability database.
if errors.Is(err, errSkipScan) { if utils.StringInSlice(types.SecurityCheckVulnerability, opt.SecurityChecks) {
return nil 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 { if err != nil {
return xerrors.Errorf("scan error: %w", err) return xerrors.Errorf("scan error: %w", err)
} }
results, err = filter(ctx, conf, results) results, err = filter(ctx, opt, results)
if err != nil { if err != nil {
return xerrors.Errorf("filter error: %w", err) 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) return xerrors.Errorf("unable to write results: %w", err)
} }
exit(conf, results) exit(opt, results)
return nil return nil
} }
func initCache(c Config) (operation.Cache, error) { func initFSCache(c Option) (cache.Cache, error) {
utils.SetCacheDir(c.CacheDir) utils.SetCacheDir(c.CacheDir)
cache, err := operation.NewCache(c.CacheBackend) cache, err := operation.NewCache(c.CacheBackend)
if err != nil { if err != nil {
@@ -95,7 +98,7 @@ func initCache(c Config) (operation.Cache, error) {
} }
if c.ClearCache { if c.ClearCache {
defer cache.Close() 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{}, xerrors.Errorf("cache clear error: %w", err)
} }
return operation.Cache{}, errSkipScan return operation.Cache{}, errSkipScan
@@ -103,7 +106,7 @@ func initCache(c Config) (operation.Cache, error) {
return cache, nil return cache, nil
} }
func initDB(c Config) error { func initDB(c Option) error {
// download the database file // download the database file
noProgress := c.Quiet || c.NoProgress noProgress := c.Quiet || c.NoProgress
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.Light, c.SkipUpdate); err != nil { 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 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) { report.Results, error) {
target := conf.Target target := opt.Target
if conf.Input != "" { if opt.Input != "" {
target = conf.Input target = opt.Input
} }
scanOptions := types.ScanOptions{ scanOptions := types.ScanOptions{
VulnType: conf.VulnType, VulnType: opt.VulnType,
ScanRemovedPackages: conf.ScanRemovedPkgs, // this is valid only for image subcommand SecurityChecks: opt.SecurityChecks,
ListAllPackages: conf.ListAllPkgs, ScanRemovedPackages: opt.ScanRemovedPkgs, // this is valid only for image subcommand
SkipFiles: conf.SkipFiles, ListAllPackages: opt.ListAllPkgs,
SkipDirs: conf.SkipDirs, SkipFiles: opt.SkipFiles,
SkipDirs: opt.SkipDirs,
} }
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType) log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
// It doesn't analyze apk commands by default. // It doesn't analyze apk commands by default.
disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand} disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand}
if conf.ScanRemovedPkgs { if opt.ScanRemovedPkgs {
disabledAnalyzers = []analyzer.Type{} 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 { if err != nil {
return nil, xerrors.Errorf("unable to initialize a scanner: %w", err) 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 return results, nil
} }
func filter(ctx context.Context, conf Config, results report.Results) (report.Results, error) { func filter(ctx context.Context, opt Option, results report.Results) (report.Results, error) {
vulnClient := initializeVulnerabilityClient() resultClient := initializeResultClient()
for i := range results { for i := range results {
vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type) resultClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities, vulns, err := resultClient.Filter(ctx, results[i].Vulnerabilities,
conf.Severities, conf.IgnoreUnfixed, conf.IgnoreFile, conf.IgnorePolicy) opt.Severities, opt.IgnoreUnfixed, opt.IgnoreFile, opt.IgnorePolicy)
if err != nil { if err != nil {
return nil, xerrors.Errorf("unable to filter vulnerabilities: %w", err) 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 return results, nil
} }
func exit(c Config, results report.Results) { func exit(c Option, results report.Results) {
if c.ExitCode != 0 { if c.ExitCode != 0 && results.Failed() {
for _, result := range results { os.Exit(c.ExitCode)
if len(result.Vulnerabilities) > 0 {
os.Exit(c.ExitCode)
}
}
} }
} }

View File

@@ -8,6 +8,7 @@ package artifact
import ( import (
"context" "context"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
"github.com/aquasecurity/fanal/applier" "github.com/aquasecurity/fanal/applier"
image2 "github.com/aquasecurity/fanal/artifact/image" image2 "github.com/aquasecurity/fanal/artifact/image"
local2 "github.com/aquasecurity/fanal/artifact/local" local2 "github.com/aquasecurity/fanal/artifact/local"
@@ -25,7 +26,7 @@ import (
// Injectors from inject.go: // 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector) localScanner := local.NewScanner(applierApplier, detector)
@@ -37,14 +38,18 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err 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) scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() { return scannerScanner, func() {
cleanup() cleanup()
}, nil }, 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, detector) localScanner := local.NewScanner(applierApplier, detector)
@@ -52,26 +57,32 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
if err != nil { if err != nil {
return scanner.Scanner{}, err 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) scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, nil 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, 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) scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() { return scannerScanner, func() {
}, nil }, 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) applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{} detector := ospkg.Detector{}
localScanner := local.NewScanner(applierApplier, 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 { if err != nil {
return scanner.Scanner{}, nil, err return scanner.Scanner{}, nil, err
} }
@@ -81,8 +92,8 @@ func initializeRepositoryScanner(ctx context.Context, url string, artifactCache
}, nil }, nil
} }
func initializeVulnerabilityClient() vulnerability.Client { func initializeResultClient() vulnerability.Client {
config := db.Config{} dbConfig := db.Config{}
client := vulnerability.NewClient(config) client := vulnerability.NewClient(dbConfig)
return client return client
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ package client
import ( import (
"context" "context"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/analyzer/config"
image2 "github.com/aquasecurity/fanal/artifact/image" image2 "github.com/aquasecurity/fanal/artifact/image"
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image" "github.com/aquasecurity/fanal/image"
@@ -21,7 +22,7 @@ import (
// Injectors from inject.go: // 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) scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner) clientScanner := client.NewScanner(customHeaders, scannerScanner)
dockerOption, err := types.GetDockerOption(timeout) dockerOption, err := types.GetDockerOption(timeout)
@@ -32,27 +33,34 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
if err != nil { if err != nil {
return scanner.Scanner{}, nil, err 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) scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, func() { return scanner2, func() {
cleanup() cleanup()
}, 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) {
scannerScanner := client.NewProtobufClient(url) scannerScanner := client.NewProtobufClient(url)
clientScanner := client.NewScanner(customHeaders, scannerScanner) clientScanner := client.NewScanner(customHeaders, scannerScanner)
imageImage, err := image.NewArchiveImage(filePath) imageImage, err := image.NewArchiveImage(filePath)
if err != nil { if err != nil {
return scanner.Scanner{}, err 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) scanner2 := scanner.NewScanner(clientScanner, artifact)
return scanner2, nil return scanner2, nil
} }
func initializeVulnerabilityClient() vulnerability.Client { func initializeResultClient() vulnerability.Client {
config := db.Config{} dbConfig := db.Config{}
vulnerabilityClient := vulnerability.NewClient(config) vulnerabilityClient := vulnerability.NewClient(dbConfig)
return vulnerabilityClient 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 { if err := c.ClearDB(); err != nil {
return xerrors.Errorf("failed to clear the database: %w", err) return xerrors.Errorf("failed to clear the database: %w", err)
} }
if err := c.ClearImages(); err != nil { if err := c.ClearArtifacts(); err != nil {
return xerrors.Errorf("failed to clear the image cache: %w", err) return xerrors.Errorf("failed to clear the artifact cache: %w", err)
} }
return nil return nil
} }
@@ -66,9 +66,9 @@ func (c Cache) ClearDB() (err error) {
return nil return nil
} }
// ClearImages clears the cache images // ClearArtifacts clears the artifact cache
func (c Cache) ClearImages() error { func (c Cache) ClearArtifacts() error {
log.Logger.Info("Removing image caches...") log.Logger.Info("Removing artifact caches...")
if err := c.Clear(); err != nil { if err := c.Clear(); err != nil {
return xerrors.Errorf("failed to remove the cache: %w", err) 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 // 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 xerrors.Errorf("failed to show database info: %w", err)
} }
return nil return nil

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
package config package option
import ( import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
// DBConfig holds the config for trivy DB // DBOption holds the options for trivy DB
type DBConfig struct { type DBOption struct {
Reset bool Reset bool
DownloadDBOnly bool DownloadDBOnly bool
SkipUpdate bool SkipUpdate bool
@@ -14,9 +14,9 @@ type DBConfig struct {
NoProgress bool NoProgress bool
} }
// NewDBConfig is the factory method to return the DBConfig // NewDBOption is the factory method to return the DBOption
func NewDBConfig(c *cli.Context) DBConfig { func NewDBOption(c *cli.Context) DBOption {
return DBConfig{ return DBOption{
Reset: c.Bool("reset"), Reset: c.Bool("reset"),
DownloadDBOnly: c.Bool("download-db-only"), DownloadDBOnly: c.Bool("download-db-only"),
SkipUpdate: c.Bool("skip-update"), SkipUpdate: c.Bool("skip-update"),
@@ -25,8 +25,8 @@ func NewDBConfig(c *cli.Context) DBConfig {
} }
} }
// Init initialize the DBConfig // Init initialize the DBOption
func (c *DBConfig) Init() (err error) { func (c *DBOption) Init() (err error) {
if c.SkipUpdate && c.DownloadDBOnly { if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("--skip-update and --download-db-only options can not be specified both") 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 ( import (
"flag" "flag"
"testing" "testing"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
) )
func TestNewDBConfig(t *testing.T) { func TestNewDBOption(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []string args []string
want config.DBConfig want option.DBOption
}{ }{
{ {
name: "happy path", name: "happy path",
args: []string{"--reset", "--skip-update"}, args: []string{"--reset", "--skip-update"},
want: config.DBConfig{ want: option.DBOption{
Reset: true, Reset: true,
SkipUpdate: true, SkipUpdate: true,
}, },
@@ -35,13 +34,13 @@ func TestNewDBConfig(t *testing.T) {
c := cli.NewContext(app, set, nil) c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args) _ = set.Parse(tt.args)
got := config.NewDBConfig(c) got := option.NewDBOption(c)
assert.Equal(t, tt.want, got, tt.name) assert.Equal(t, tt.want, got, tt.name)
}) })
} }
} }
func TestDBConfig_Init(t *testing.T) { func TestDBOption_Init(t *testing.T) {
type fields struct { type fields struct {
Reset bool Reset bool
DownloadDBOnly bool DownloadDBOnly bool
@@ -70,7 +69,7 @@ func TestDBConfig_Init(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := &config.DBConfig{ c := &option.DBOption{
Reset: tt.fields.Reset, Reset: tt.fields.Reset,
DownloadDBOnly: tt.fields.DownloadDBOnly, DownloadDBOnly: tt.fields.DownloadDBOnly,
SkipUpdate: tt.fields.SkipUpdate, SkipUpdate: tt.fields.SkipUpdate,

View File

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

View File

@@ -1,26 +1,25 @@
package config_test package option_test
import ( import (
"flag" "flag"
"testing" "testing"
"github.com/aquasecurity/trivy/pkg/commands/option"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/pkg/commands/config"
) )
func TestNewGlobalConfig(t *testing.T) { func TestNewGlobalConfig(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []string args []string
want config.GlobalConfig want option.GlobalOption
}{ }{
{ {
name: "happy path", name: "happy path",
args: []string{"--quiet", "--debug"}, args: []string{"--quiet", "--debug"},
want: config.GlobalConfig{ want: option.GlobalOption{
Quiet: true, Quiet: true,
Debug: true, Debug: true,
}, },
@@ -36,7 +35,7 @@ func TestNewGlobalConfig(t *testing.T) {
c := cli.NewContext(app, set, nil) c := cli.NewContext(app, set, nil)
_ = set.Parse(tt.args) _ = set.Parse(tt.args)
got, err := config.NewGlobalConfig(c) got, err := option.NewGlobalOption(c)
require.NoError(t, err, err) require.NoError(t, err, err)
assert.Equal(t, tt.want.Quiet, got.Quiet, tt.name) assert.Equal(t, tt.want.Quiet, got.Quiet, tt.name)
assert.Equal(t, tt.want.Debug, got.Debug, 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" "github.com/urfave/cli/v2"
"golang.org/x/xerrors" "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/log"
"github.com/aquasecurity/trivy/pkg/plugin" "github.com/aquasecurity/trivy/pkg/plugin"
) )
@@ -105,7 +105,7 @@ func LoadCommands() cli.Commands {
} }
func initLogger(ctx *cli.Context) error { func initLogger(ctx *cli.Context) error {
conf, err := config.NewGlobalConfig(ctx) conf, err := option.NewGlobalOption(ctx)
if err != nil { if err != nil {
return xerrors.Errorf("config error: %w", err) return xerrors.Errorf("config error: %w", err)
} }

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/dbtest" "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library" "github.com/aquasecurity/trivy/pkg/detector/library"
@@ -29,7 +29,7 @@ func TestDriver_Detect(t *testing.T) {
{ {
name: "happy path", name: "happy path",
fixtures: []string{"testdata/fixtures/php.yaml"}, fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer, libType: ftypes.Composer,
args: args{ args: args{
pkgName: "symfony/symfony", pkgName: "symfony/symfony",
pkgVer: "4.2.6", pkgVer: "4.2.6",
@@ -46,7 +46,7 @@ func TestDriver_Detect(t *testing.T) {
{ {
name: "non-prefix buckets", name: "non-prefix buckets",
fixtures: []string{"testdata/fixtures/php-without-prefix.yaml"}, fixtures: []string{"testdata/fixtures/php-without-prefix.yaml"},
libType: lib.Composer, libType: ftypes.Composer,
args: args{ args: args{
pkgName: "symfony/symfony", pkgName: "symfony/symfony",
pkgVer: "4.2.6", pkgVer: "4.2.6",
@@ -63,7 +63,7 @@ func TestDriver_Detect(t *testing.T) {
{ {
name: "no patched versions in the advisory", name: "no patched versions in the advisory",
fixtures: []string{"testdata/fixtures/php.yaml"}, fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer, libType: ftypes.Composer,
args: args{ args: args{
pkgName: "symfony/symfony", pkgName: "symfony/symfony",
pkgVer: "4.4.6", pkgVer: "4.4.6",
@@ -80,7 +80,7 @@ func TestDriver_Detect(t *testing.T) {
{ {
name: "no vulnerable versions in the advisory", name: "no vulnerable versions in the advisory",
fixtures: []string{"testdata/fixtures/ruby.yaml"}, fixtures: []string{"testdata/fixtures/ruby.yaml"},
libType: lib.Bundler, libType: ftypes.Bundler,
args: args{ args: args{
pkgName: "activesupport", pkgName: "activesupport",
pkgVer: "4.1.1", pkgVer: "4.1.1",
@@ -97,7 +97,7 @@ func TestDriver_Detect(t *testing.T) {
{ {
name: "no vulnerability", name: "no vulnerability",
fixtures: []string{"testdata/fixtures/php.yaml"}, fixtures: []string{"testdata/fixtures/php.yaml"},
libType: lib.Composer, libType: ftypes.Composer,
args: args{ args: args{
pkgName: "symfony/symfony", pkgName: "symfony/symfony",
pkgVer: "4.4.7", 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" "strconv"
"strings" "strings"
"github.com/google/go-github/v28/github" "github.com/google/go-github/v33/github"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/xerrors" "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 // DownloadAsset returns reader object of downloaded object
func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) { 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 // 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") 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()) log.Logger.Debugf("asset name: %s", asset.GetName())
if asset.GetName() != fileName { if asset.GetName() != fileName {
return nil, 0, xerrors.New("file name doesn't match") return nil, 0, xerrors.New("file name doesn't match")

View File

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

View File

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

View File

@@ -7,10 +7,10 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
getter "github.com/hashicorp/go-getter"
"golang.org/x/xerrors" "golang.org/x/xerrors"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
"github.com/aquasecurity/trivy/pkg/downloader"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils" "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) 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 xerrors.Errorf("unable to download the execution file (%s): %w", platform.URI, err)
} }
return nil 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) log.Logger.Infof("Installing the plugin from %s...", url)
tempDir, err := downloadToTempDir(ctx, url) tempDir, err := downloader.DownloadToTempDir(ctx, url)
if err != nil { if err != nil {
return Plugin{}, xerrors.Errorf("download failed: %w", err) return Plugin{}, xerrors.Errorf("download failed: %w", err)
} }
@@ -182,51 +182,6 @@ func Uninstall(name string) error {
return os.RemoveAll(pluginDir) 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 // LoadAll loads all plugins
func LoadAll() ([]Plugin, error) { func LoadAll() ([]Plugin, error) {
pluginsDir := dir() 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 package report
import ( import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"html"
"io" "io"
"io/ioutil"
"os"
"regexp"
"strings"
"text/template"
"time" "time"
"github.com/Masterminds/sprig"
"github.com/olekukonko/tablewriter"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer/library"
ftypes "github.com/aquasecurity/fanal/types" ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/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/types"
"github.com/aquasecurity/trivy/pkg/utils"
) )
// Now returns the current time // Now returns the current time
var Now = time.Now 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 // Results to hold list of Result
type Results []Result type Results []Result
@@ -40,7 +22,17 @@ type Result struct {
Target string `json:"Target"` Target string `json:"Target"`
Type string `json:"Type,omitempty"` Type string `json:"Type,omitempty"`
Packages []ftypes.Package `json:"Packages,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 // 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 { type Writer interface {
Write(Results) error 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 package report_test
import ( import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"testing" "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/report"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
"github.com/stretchr/testify/assert"
) )
func TestReportWriter_Table(t *testing.T) { func TestResults_Failed(t *testing.T) {
testCases := []struct { tests := []struct {
name string name string
detectedVulns []types.DetectedVulnerability results report.Results
expectedOutput string want bool
light bool
}{ }{
{ {
name: "happy path full", name: "no vulnerabilities and misconfigurations",
detectedVulns: []types.DetectedVulnerability{ results: report.Results{
{ {
VulnerabilityID: "CVE-2020-0001", Target: "test",
PkgName: "foo", Type: "test",
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: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+ want: false,
| 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", name: "vulnerabilities found",
light: true, results: report.Results{
detectedVulns: []types.DetectedVulnerability{
{ {
VulnerabilityID: "123", Target: "test",
PkgName: "foo", Type: "test",
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",
Vulnerabilities: []types.DetectedVulnerability{ Vulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2021-0001",
PkgName: "foo", PkgName: "test",
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",
},
}, },
}, },
}, },
}, },
want: true,
}, },
} }
for _, tt := range tests {
for _, tc := range testCases { t.Run(tt.name, func(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { got := tt.results.Failed()
assert.Equal(t, tt.want, got)
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)
}) })
} }
} }

View File

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

View File

@@ -117,10 +117,7 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
Description: vuln.Description, Description: vuln.Description,
Severity: common.Severity(severity), Severity: common.Severity(severity),
References: vuln.References, References: vuln.References,
Layer: &common.Layer{ Layer: ConvertToRPCLayer(vuln.Layer),
Digest: vuln.Layer.Digest,
DiffId: vuln.Layer.DiffID,
},
Cvss: cvssMap, Cvss: cvssMap,
SeveritySource: vuln.SeveritySource, SeveritySource: vuln.SeveritySource,
CweIds: vuln.CweIDs, CweIds: vuln.CweIDs,
@@ -132,65 +129,83 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
return rpcVulns 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 // ConvertFromRPCResults converts scanner.Result to report.Result
func ConvertFromRPCResults(rpcResults []*scanner.Result) []report.Result { func ConvertFromRPCResults(rpcResults []*scanner.Result) []report.Result {
var results []report.Result var results []report.Result
for _, result := range rpcResults { 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{ results = append(results, report.Result{
Target: result.Target, Target: result.Target,
Vulnerabilities: vulns, Vulnerabilities: ConvertFromRPCVulns(result.Vulnerabilities),
Type: result.Type, Type: result.Type,
}) })
} }
return results 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 // ConvertFromRPCOS converts common.OS to fanal.OS
func ConvertFromRPCOS(rpcOS *common.OS) *ftypes.OS { func ConvertFromRPCOS(rpcOS *common.OS) *ftypes.OS {
if rpcOS == nil { if rpcOS == nil {
@@ -286,9 +301,9 @@ func ConvertToRPCArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *ca
} }
// ConvertToRPCBlobInfo returns PutBlobRequest // 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 var packageInfos []*common.PackageInfo
for _, pkgInfo := range layerInfo.PackageInfos { for _, pkgInfo := range blobInfo.PackageInfos {
packageInfos = append(packageInfos, &common.PackageInfo{ packageInfos = append(packageInfos, &common.PackageInfo{
FilePath: pkgInfo.FilePath, FilePath: pkgInfo.FilePath,
Packages: ConvertToRPCPkgs(pkgInfo.Packages), Packages: ConvertToRPCPkgs(pkgInfo.Packages),
@@ -296,7 +311,7 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
} }
var applications []*common.Application var applications []*common.Application
for _, app := range layerInfo.Applications { for _, app := range blobInfo.Applications {
var libs []*common.Library var libs []*common.Library
for _, lib := range app.Libraries { for _, lib := range app.Libraries {
libs = append(libs, &common.Library{ libs = append(libs, &common.Library{
@@ -315,13 +330,13 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
DiffId: diffID, DiffId: diffID,
BlobInfo: &cache.BlobInfo{ BlobInfo: &cache.BlobInfo{
SchemaVersion: ftypes.BlobJSONSchemaVersion, SchemaVersion: ftypes.BlobJSONSchemaVersion,
Digest: layerInfo.Digest, Digest: blobInfo.Digest,
DiffId: layerInfo.DiffID, DiffId: blobInfo.DiffID,
Os: ConvertToRPCOS(layerInfo.OS), Os: ConvertToRPCOS(blobInfo.OS),
PackageInfos: packageInfos, PackageInfos: packageInfos,
Applications: applications, Applications: applications,
OpaqueDirs: layerInfo.OpaqueDirs, OpaqueDirs: blobInfo.OpaqueDirs,
WhiteoutFiles: layerInfo.WhiteoutFiles, WhiteoutFiles: blobInfo.WhiteoutFiles,
}, },
} }
} }
@@ -346,8 +361,8 @@ func ConvertToRPCScanResponse(results report.Results, os *ftypes.OS, eosl bool)
for _, result := range results { for _, result := range results {
rpcResults = append(rpcResults, &scanner.Result{ rpcResults = append(rpcResults, &scanner.Result{
Target: result.Target, Target: result.Target,
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
Type: result.Type, Type: result.Type,
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
}) })
} }

View File

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

View File

@@ -16,8 +16,10 @@ import (
"github.com/aquasecurity/fanal/cache" "github.com/aquasecurity/fanal/cache"
ftypes "github.com/aquasecurity/fanal/types" ftypes "github.com/aquasecurity/fanal/types"
deptypes "github.com/aquasecurity/go-dep-parser/pkg/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" dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils" "github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/report" "github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
@@ -37,15 +39,16 @@ func TestScanServer_Scan(t *testing.T) {
in *rpcScanner.ScanRequest in *rpcScanner.ScanRequest
} }
tests := []struct { tests := []struct {
name string name string
args args fixtures []string
scanExpectation scanner.DriverScanExpectation args args
fillInfoExpectation vulnerability.OperationFillInfoExpectation scanExpectation scanner.DriverScanExpectation
want *rpcScanner.ScanResponse want *rpcScanner.ScanResponse
wantErr string wantErr string
}{ }{
{ {
name: "happy path", name: "happy path",
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
args: args{ args: args{
in: &rpcScanner.ScanRequest{ in: &rpcScanner.ScanRequest{
Target: "alpine:3.11", Target: "alpine:3.11",
@@ -70,7 +73,6 @@ func TestScanServer_Scan(t *testing.T) {
PkgName: "musl", PkgName: "musl",
InstalledVersion: "1.2.3", InstalledVersion: "1.2.3",
FixedVersion: "1.2.4", FixedVersion: "1.2.4",
SeveritySource: "nvd",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"), LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
PublishedDate: utils.MustTimeParse("2001-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{ want: &rpcScanner.ScanResponse{
Os: &common.OS{ Os: &common.OS{
Family: "alpine", Family: "alpine",
@@ -119,9 +103,14 @@ func TestScanServer_Scan(t *testing.T) {
PkgName: "musl", PkgName: "musl",
InstalledVersion: "1.2.3", InstalledVersion: "1.2.3",
FixedVersion: "1.2.4", FixedVersion: "1.2.4",
Severity: common.Severity_MEDIUM,
SeveritySource: "nvd", SeveritySource: "nvd",
Layer: &common.Layer{}, 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{ LastModifiedDate: &timestamp.Timestamp{
Seconds: 1577840460, Seconds: 1577840460,
}, },
@@ -161,13 +150,13 @@ func TestScanServer_Scan(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
dbtest.InitDB(t, tt.fixtures)
defer db.Close()
mockDriver := new(scanner.MockDriver) mockDriver := new(scanner.MockDriver)
mockDriver.ApplyScanExpectation(tt.scanExpectation) mockDriver.ApplyScanExpectation(tt.scanExpectation)
mockVulnClient := new(vulnerability.MockOperation) s := NewScanServer(mockDriver, vulnerability.NewClient(db.Config{}))
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
s := NewScanServer(mockDriver, mockVulnClient)
got, err := s.Scan(context.Background(), tt.args.in) got, err := s.Scan(context.Background(), tt.args.in)
if tt.wantErr != "" { if tt.wantErr != "" {
require.NotNil(t, err, tt.name) 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" "golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/fanal/analyzer"
_ "github.com/aquasecurity/fanal/analyzer/command/apk" _ "github.com/aquasecurity/fanal/analyzer/all"
_ "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/applier" "github.com/aquasecurity/fanal/applier"
ftypes "github.com/aquasecurity/fanal/types" ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/detector/library" "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) artifactDetail, err := s.applier.ApplyLayers(versionedArtifactID, versionedBlobIDs)
switch { switch {
case errors.Is(err, analyzer.ErrUnknownOS): 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): 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("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"`) 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 eosl bool
var results report.Results var results report.Results
if utils.StringInSlice("os", options.VulnType) && artifactDetail.OS != nil { // Scan OS packages and programming language dependencies
var result *report.Result if utils.StringInSlice(types.SecurityCheckVulnerability, options.SecurityChecks) {
result, eosl, err = s.scanOSPkgs(target, artifactDetail, options) var vulnResults report.Results
vulnResults, eosl, err = s.checkVulnerabilities(target, artifactDetail, options)
if err != nil { if err != nil {
return nil, nil, false, xerrors.Errorf("unable to scan OS packages: %w", err) return nil, nil, false, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
} else if result != nil {
results = append(results, *result)
} }
} results = append(results, vulnResults...)
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...)
} }
return results, artifactDetail.OS, eosl, nil 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) ( func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
*report.Result, bool, error) { *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 pkgs := detail.Packages
if options.ScanRemovedPackages { if options.ScanRemovedPackages {
pkgs = mergePkgs(pkgs, detail.HistoryPackages) 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) { 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 { if len(apps) == 0 {
log.Logger.Info("Trivy skips scanning programming language libraries because no supported file was detected")
return nil, nil 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 { func skipped(filePath string, skipFiles, skipDirs []string) bool {
filePath = strings.TrimLeft(filepath.Clean(filePath), string(os.PathSeparator))
for _, skipFile := range skipFiles { for _, skipFile := range skipFiles {
skipFile = strings.TrimLeft(filepath.Clean(skipFile), string(os.PathSeparator)) skipFile = strings.TrimLeft(filepath.Clean(skipFile), string(os.PathSeparator))
if filePath == skipFile { if filePath == skipFile {

View File

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

View File

@@ -10,7 +10,6 @@ import (
aimage "github.com/aquasecurity/fanal/artifact/image" aimage "github.com/aquasecurity/fanal/artifact/image"
flocal "github.com/aquasecurity/fanal/artifact/local" flocal "github.com/aquasecurity/fanal/artifact/local"
"github.com/aquasecurity/fanal/artifact/remote" "github.com/aquasecurity/fanal/artifact/remote"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image" "github.com/aquasecurity/fanal/image"
ftypes "github.com/aquasecurity/fanal/types" ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
@@ -83,7 +82,8 @@ type Scanner struct {
// Driver defines operations of scanner // Driver defines operations of scanner
type Driver interface { 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 // 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) 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) results, osFound, eosl, err := s.driver.Scan(artifactInfo.Name, artifactInfo.ID, artifactInfo.BlobIDs, options)
if err != nil { if err != nil {
return nil, xerrors.Errorf("scan failed: %w", err) return nil, xerrors.Errorf("scan failed: %w", err)

View File

@@ -3,6 +3,7 @@ package types
// ScanOptions holds the attributes for scanning vulnerabilities // ScanOptions holds the attributes for scanning vulnerabilities
type ScanOptions struct { type ScanOptions struct {
VulnType []string VulnType []string
SecurityChecks []string
ScanRemovedPackages bool ScanRemovedPackages bool
ListAllPackages bool ListAllPackages bool
SkipFiles []string 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 return cacheDir
} }
// SetCacheDir sets the tricy cacheDir // SetCacheDir sets the trivy cacheDir
func SetCacheDir(dir string) { func SetCacheDir(dir string) {
cacheDir = dir 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-0001
CVE-2019-0002 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" "context"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/db"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types" dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils" "github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/types" "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 { type args struct {
vulns []types.DetectedVulnerability vulns []types.DetectedVulnerability
reportType string reportType string
} }
tests := []struct { tests := []struct {
name string name string
getSeverity []db.OperationGetSeverityExpectation fixtures []string
getVulnerability []db.OperationGetVulnerabilityExpectation
args args args args
expectedVulnerabilities []types.DetectedVulnerability expectedVulnerabilities []types.DetectedVulnerability
}{ }{
{ {
name: "happy path, with only OS vulnerability but no vendor severity, no NVD", name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/full.yaml"},
{
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"),
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"}, {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", name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/full.yaml"},
{
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"),
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"}, {VulnerabilityID: "CVE-2019-0002"},
}, },
reportType: vulnerability.Ubuntu, reportType: vulnerability.Ubuntu,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0001", VulnerabilityID: "CVE-2019-0002",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "dos", Title: "dos",
Description: "dos vulnerability", Description: "dos vulnerability",
@@ -107,89 +72,44 @@ func TestClient_FillInfo(t *testing.T) {
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"), PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
}, },
SeveritySource: vulnerability.Nvd, 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", name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/full.yaml"},
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Description: "dos vulnerability",
References: []string{"http://example.com"},
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"}, {VulnerabilityID: "CVE-2019-0003"},
}, },
reportType: vulnerability.Ubuntu, reportType: vulnerability.Ubuntu,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0001", VulnerabilityID: "CVE-2019-0003",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "dos", Title: "dos",
Description: "dos vulnerability", Description: "dos vulnerability",
Severity: dbTypes.SeverityUnknown.String(), Severity: dbTypes.SeverityUnknown.String(),
References: []string{"http://example.com"}, 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", name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/full.yaml"},
{
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"},
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"}, {VulnerabilityID: "CVE-2019-0004"},
}, },
reportType: vulnerability.CentOS, reportType: vulnerability.CentOS,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0001", VulnerabilityID: "CVE-2019-0004",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "dos", Title: "dos",
Description: "dos vulnerability", Description: "dos vulnerability",
@@ -198,7 +118,7 @@ func TestClient_FillInfo(t *testing.T) {
References: []string{"http://example.com"}, References: []string{"http://example.com"},
CVSS: map[string]dbTypes.CVSS{ CVSS: map[string]dbTypes.CVSS{
vulnerability.Nvd: { 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, V2Score: 4.5,
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H", V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 5.6, V3Score: 5.6,
@@ -212,104 +132,63 @@ func TestClient_FillInfo(t *testing.T) {
}, },
}, },
SeveritySource: vulnerability.RedHat, 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", name: "happy path light db, with only OS vulnerability, yes vendor severity",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/light.yaml"},
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0001",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityMedium.String(),
VendorSeverity: dbTypes.VendorSeverity{
vulnerability.Ubuntu: dbTypes.SeverityLow,
},
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0001"}, {VulnerabilityID: "CVE-2020-0001"},
}, },
reportType: vulnerability.Ubuntu, reportType: vulnerability.Ubuntu,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0001", VulnerabilityID: "CVE-2020-0001",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityLow.String(), Severity: dbTypes.SeverityLow.String(),
}, },
SeveritySource: vulnerability.Ubuntu, 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", name: "happy path light db, with only OS vulnerability, no vendor severity",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/light.yaml"},
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2020-28928",
},
Returns: db.OperationGetVulnerabilityReturns{
Vulnerability: dbTypes.Vulnerability{
VendorSeverity: dbTypes.VendorSeverity{},
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-28928"}, {VulnerabilityID: "CVE-2020-0002"},
}, },
reportType: vulnerability.Alpine, reportType: vulnerability.Alpine,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-28928", VulnerabilityID: "CVE-2020-0002",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "dos",
Severity: dbTypes.SeverityUnknown.String(), Severity: dbTypes.SeverityUnknown.String(),
}, },
SeveritySource: "", PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0002",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-28928",
}, },
}, },
}, },
{ {
name: "happy path, with only library vulnerability", name: "happy path, with only library vulnerability",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/full.yaml"},
{
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"},
},
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2020-0001"}, {VulnerabilityID: "CVE-2019-0005"},
}, },
reportType: "poetry", reportType: ftypes.Poetry,
}, },
expectedVulnerabilities: []types.DetectedVulnerability{ expectedVulnerabilities: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2020-0001", VulnerabilityID: "CVE-2019-0005",
Vulnerability: dbTypes.Vulnerability{ Vulnerability: dbTypes.Vulnerability{
Title: "COVID-19", Title: "COVID-19",
Description: "a nasty virus vulnerability for humans", 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"}, References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
}, },
SeveritySource: vulnerability.PythonSafetyDB, 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", name: "GetVulnerability returns an error",
getVulnerability: []db.OperationGetVulnerabilityExpectation{ fixtures: []string{"testdata/fixtures/sad.yaml"},
{
Args: db.OperationGetVulnerabilityArgs{
VulnerabilityID: "CVE-2019-0004",
},
Returns: db.OperationGetVulnerabilityReturns{
Err: xerrors.New("failed"),
},
},
},
args: args{ args: args{
vulns: []types.DetectedVulnerability{ vulns: []types.DetectedVulnerability{
{VulnerabilityID: "CVE-2019-0004"}, {VulnerabilityID: "CVE-2019-0004"},
@@ -346,17 +216,15 @@ func TestClient_FillInfo(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mockDBConfig := new(db.MockOperation) dbtest.InitDB(t, tt.fixtures)
mockDBConfig.ApplyGetSeverityExpectations(tt.getSeverity) defer db.Close()
mockDBConfig.ApplyGetVulnerabilityExpectations(tt.getVulnerability)
c := Client{ c := Client{
dbc: mockDBConfig, dbc: db.Config{},
} }
c.FillInfo(tt.args.vulns, tt.args.reportType) c.FillInfo(tt.args.vulns, tt.args.reportType)
assert.Equal(t, tt.expectedVulnerabilities, tt.args.vulns, tt.name) 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 policyFile string
} }
tests := []struct { tests := []struct {
name string name string
args args args args
want []types.DetectedVulnerability wantVulns []types.DetectedVulnerability
}{ }{
{ {
name: "happy path", name: "happy path",
@@ -509,7 +377,7 @@ func TestClient_Filter(t *testing.T) {
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown}, severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false, ignoreUnfixed: false,
}, },
want: []types.DetectedVulnerability{ wantVulns: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2018-0001", VulnerabilityID: "CVE-2018-0001",
PkgName: "bar", PkgName: "bar",
@@ -613,7 +481,7 @@ func TestClient_Filter(t *testing.T) {
ignoreUnfixed: false, ignoreUnfixed: false,
ignoreFile: "testdata/.trivyignore", ignoreFile: "testdata/.trivyignore",
}, },
want: []types.DetectedVulnerability{ wantVulns: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0003", VulnerabilityID: "CVE-2019-0003",
PkgName: "foo", PkgName: "foo",
@@ -663,7 +531,7 @@ func TestClient_Filter(t *testing.T) {
ignoreUnfixed: false, ignoreUnfixed: false,
policyFile: "./testdata/test.rego", policyFile: "./testdata/test.rego",
}, },
want: []types.DetectedVulnerability{ wantVulns: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2019-0001", VulnerabilityID: "CVE-2019-0001",
PkgName: "foo", PkgName: "foo",
@@ -755,7 +623,7 @@ func TestClient_Filter(t *testing.T) {
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown}, severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
ignoreUnfixed: false, ignoreUnfixed: false,
}, },
want: []types.DetectedVulnerability{ wantVulns: []types.DetectedVulnerability{
{ {
VulnerabilityID: "CVE-2018-0001", VulnerabilityID: "CVE-2018-0001",
PkgName: "bar", PkgName: "bar",
@@ -807,10 +675,9 @@ func TestClient_Filter(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := Client{} c := Client{}
got, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities, gotVulns, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities, tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
require.NoError(t, err) 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" fmt "fmt"
common "github.com/aquasecurity/trivy/rpc/common" common "github.com/aquasecurity/trivy/rpc/common"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
_ "github.com/golang/protobuf/ptypes/empty" _ "google.golang.org/protobuf/types/known/emptypb"
timestamp "github.com/golang/protobuf/ptypes/timestamp" timestamppb "google.golang.org/protobuf/types/known/timestamppb"
math "math" math "math"
) )
@@ -24,15 +24,15 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ArtifactInfo struct { type ArtifactInfo struct {
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` 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"` 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"` 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"` 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"` 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"` HistoryPackages []*common.Package `protobuf:"bytes,6,rep,name=history_packages,json=historyPackages,proto3" json:"history_packages,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
} }
func (m *ArtifactInfo) Reset() { *m = ArtifactInfo{} } func (m *ArtifactInfo) Reset() { *m = ArtifactInfo{} }
@@ -74,7 +74,7 @@ func (m *ArtifactInfo) GetArchitecture() string {
return "" return ""
} }
func (m *ArtifactInfo) GetCreated() *timestamp.Timestamp { func (m *ArtifactInfo) GetCreated() *timestamppb.Timestamp {
if m != nil { if m != nil {
return m.Created return m.Created
} }

View File

@@ -17,23 +17,19 @@ import fmt "fmt"
import ioutil "io/ioutil" import ioutil "io/ioutil"
import http "net/http" import http "net/http"
import strconv "strconv" import strconv "strconv"
import gzip "compress/gzip"
import jsonpb "github.com/golang/protobuf/jsonpb" import jsonpb "github.com/golang/protobuf/jsonpb"
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
import twirp "github.com/twitchtv/twirp" import twirp "github.com/twitchtv/twirp"
import ctxsetters "github.com/twitchtv/twirp/ctxsetters" 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: // Imports only used by utility functions:
import io "io" import io "io"
import json "encoding/json" import json "encoding/json"
import url "net/url" import url "net/url"
// A response is compressed with gzip when the response size exceeds this threshold.
const CompressThreshold = 10000
// =============== // ===============
// Cache Interface // Cache Interface
// =============== // ===============
@@ -420,16 +416,6 @@ func (s *cacheServer) servePutArtifactProtobuf(ctx context.Context, resp http.Re
return 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) ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf") resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -559,16 +545,6 @@ func (s *cacheServer) servePutBlobProtobuf(ctx context.Context, resp http.Respon
return 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) ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf") resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
@@ -698,16 +674,6 @@ func (s *cacheServer) serveMissingBlobsProtobuf(ctx context.Context, resp http.R
return 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) ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
resp.Header().Set("Content-Type", "application/protobuf") resp.Header().Set("Content-Type", "application/protobuf")
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 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("Accept", contentType)
req.Header.Set("Content-Type", contentType) req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept-Encoding", "gzip")
req.Header.Set("Twirp-Version", "v5.10.1") req.Header.Set("Twirp-Version", "v5.10.1")
return req, nil return req, nil
} }
@@ -1119,15 +1084,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
return errorFromResponse(resp) return errorFromResponse(resp)
} }
r := resp.Body respBodyBytes, err := ioutil.ReadAll(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)
if err != nil { if err != nil {
return wrapInternal(err, "failed to read response body") 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) 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) { func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
if h == nil || h.ResponseReceived == nil { if h == nil || h.ResponseReceived == nil {
return return

View File

@@ -6,7 +6,7 @@ package common
import ( import (
fmt "fmt" fmt "fmt"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
timestamp "github.com/golang/protobuf/ptypes/timestamp" timestamppb "google.golang.org/protobuf/types/known/timestamppb"
math "math" math "math"
) )
@@ -359,24 +359,24 @@ func (m *Library) GetVersion() string {
} }
type Vulnerability struct { type Vulnerability struct {
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"` 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"` 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"` 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"` 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"` Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,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"` 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"` References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,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"` 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"` 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"` 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"` 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"` PublishedDate *timestamppb.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"` LastModifiedDate *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=last_modified_date,json=lastModifiedDate,proto3" json:"last_modified_date,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
} }
func (m *Vulnerability) Reset() { *m = Vulnerability{} } func (m *Vulnerability) Reset() { *m = Vulnerability{} }
@@ -495,14 +495,14 @@ func (m *Vulnerability) GetPrimaryUrl() string {
return "" return ""
} }
func (m *Vulnerability) GetPublishedDate() *timestamp.Timestamp { func (m *Vulnerability) GetPublishedDate() *timestamppb.Timestamp {
if m != nil { if m != nil {
return m.PublishedDate return m.PublishedDate
} }
return nil return nil
} }
func (m *Vulnerability) GetLastModifiedDate() *timestamp.Timestamp { func (m *Vulnerability) GetLastModifiedDate() *timestamppb.Timestamp {
if m != nil { if m != nil {
return m.LastModifiedDate return m.LastModifiedDate
} }

View File

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

View File

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