mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 07:10:41 -08:00
feat: prepare for config scanning (#1005)
* temp: disable config scanning
This commit is contained in:
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
@@ -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
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -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
6
go.mod
@@ -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
27
go.sum
@@ -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=
|
||||||
|
|||||||
@@ -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
8
pkg/cache/remote.go
vendored
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{}
|
||||||
}
|
}
|
||||||
|
|||||||
88
pkg/commands/artifact/option.go
Normal file
88
pkg/commands/artifact/option.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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())
|
||||||
|
|
||||||
@@ -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://") &&
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
@@ -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,
|
||||||
|
|
||||||
@@ -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)
|
||||||
19
pkg/commands/option/image.go
Normal file
19
pkg/commands/option/image.go
Normal 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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
127
pkg/commands/option/report.go
Normal file
127
pkg/commands/option/report.go
Normal 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
|
||||||
|
}
|
||||||
178
pkg/commands/option/report_test.go
Normal file
178
pkg/commands/option/report_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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{})},
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
56
pkg/downloader/download.go
Normal file
56
pkg/downloader/download.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
27
pkg/report/json.go
Normal 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
82
pkg/report/json_test.go
Normal 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
113
pkg/report/table.go
Normal 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
145
pkg/report/table_test.go
Normal 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
121
pkg/report/template.go
Normal 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
211
pkg/report/template_test.go
Normal 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 "DARN" 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 < 7.20.0 and curl >= 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 'easy' 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 "DARN" 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 < 7.20.0 and curl >= 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 'easy' 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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: ×tamp.Timestamp{
|
LastModifiedDate: ×tamp.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)
|
||||||
|
|||||||
13
pkg/rpc/server/testdata/fixtures/vulnerability.yaml
vendored
Normal file
13
pkg/rpc/server/testdata/fixtures/vulnerability.yaml
vendored
Normal 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"
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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{
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
47
pkg/types/target.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
5
pkg/vulnerability/testdata/.trivyignore
vendored
5
pkg/vulnerability/testdata/.trivyignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
# test
|
# vulnerabilities
|
||||||
CVE-2019-0001
|
CVE-2019-0001
|
||||||
CVE-2019-0002
|
CVE-2019-0002
|
||||||
|
|
||||||
|
# misconfigurations
|
||||||
|
ID100
|
||||||
59
pkg/vulnerability/testdata/fixtures/full.yaml
vendored
Normal file
59
pkg/vulnerability/testdata/fixtures/full.yaml
vendored
Normal 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"
|
||||||
11
pkg/vulnerability/testdata/fixtures/light.yaml
vendored
Normal file
11
pkg/vulnerability/testdata/fixtures/light.yaml
vendored
Normal 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
|
||||||
6
pkg/vulnerability/testdata/fixtures/sad.yaml
vendored
Normal file
6
pkg/vulnerability/testdata/fixtures/sad.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- bucket: vulnerability
|
||||||
|
pairs:
|
||||||
|
- key: CVE-2019-0001
|
||||||
|
value:
|
||||||
|
Title:
|
||||||
|
- aaa
|
||||||
@@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
rpc/cache/service.pb.go
vendored
24
rpc/cache/service.pb.go
vendored
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
77
rpc/cache/service.twirp.go
vendored
77
rpc/cache/service.twirp.go
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user