diff --git a/README.md b/README.md
index 83fc9e7499..3ac1cbd01f 100644
--- a/README.md
+++ b/README.md
@@ -103,7 +103,7 @@ See [here](#continuous-integration-ci) for details.
- Detect comprehensive vulnerabilities
- OS packages (Alpine, **Red Hat Universal Base Image**, Red Hat Enterprise Linux, CentOS, Oracle Linux, Debian, Ubuntu, Amazon Linux, openSUSE Leap, SUSE Enterprise Linux, Photon OS and Distroless)
- - **Application dependencies** (Bundler, Composer, Pipenv, Poetry, npm, yarn, Cargo and NuGet)
+ - **Application dependencies** (Bundler, Composer, Pipenv, Poetry, npm, yarn, Cargo, NuGet, and Maven)
- Simple
- Specify only an image name or artifact name
- See [Quick Start](#quick-start) and [Examples](#examples)
@@ -1763,6 +1763,8 @@ Distroless: https://github.com/GoogleContainerTools/distroless
- Cargo.lock
- .NET
- packages.lock.json
+- Java
+ - JAR/WAR/EAR files (*.jar, *.war, and *.ear)
The path of these files does not matter.
@@ -1796,6 +1798,8 @@ Trivy scans a tar image with the following format.
- https://github.com/RustSec/advisory-db
- .NET
- https://github.com/advisories?query=ecosystem%3Anuget
+- Java
+ - https://github.com/advisories?query=ecosystem%3Amaven
# Usage
Trivy has several sub commands, image, fs, repo, client and server.
@@ -1921,7 +1925,7 @@ See [here](docs/air-gap.md)
| Scanner | OS
Packages | Application
Dependencies | Easy to use | Accuracy | Suitable
for CI |
| -------------- | :-------------: | :-------------------------: | :----------: | :---------: | :-----------------: |
-| Trivy | ✅ | ✅
(5 languages) | ⭐ ⭐ ⭐ | ⭐ ⭐ ⭐ | ⭐ ⭐ ⭐ |
+| Trivy | ✅ | ✅
(7 languages) | ⭐ ⭐ ⭐ | ⭐ ⭐ ⭐ | ⭐ ⭐ ⭐ |
| Clair | ✅ | × | ⭐ | ⭐ ⭐ | ⭐ ⭐ |
| Anchore Engine | ✅ | ✅
(4 languages) | ⭐ ⭐ | ⭐ ⭐ | ⭐ ⭐ ⭐ |
| Quay | ✅ | × | ⭐ ⭐ ⭐ | ⭐ ⭐ | × |
diff --git a/go.mod b/go.mod
index e4f1b10fad..ac231d0ed5 100644
--- a/go.mod
+++ b/go.mod
@@ -4,13 +4,13 @@ go 1.15
require (
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
- github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd
- github.com/aquasecurity/go-dep-parser v0.0.0-20201028043324-889d4a92b8e0
+ github.com/aquasecurity/fanal v0.0.0-20210214122859-98d76e2b3b96
+ github.com/aquasecurity/go-dep-parser v0.0.0-20210214113128-b97635cfd627
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-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492
- github.com/aquasecurity/trivy-db v0.0.0-20210105160501-c5bf4e153277
+ github.com/aquasecurity/trivy-db v0.0.0-20210214043256-acc144af2228
github.com/caarlos0/env/v6 v6.0.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cheggaaa/pb/v3 v3.0.3
@@ -30,6 +30,7 @@ require (
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/kylelemons/godebug v1.1.0
+ github.com/masahiro331/go-mvn-version v0.0.0-20210214074851-415aa65db8c0
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
github.com/open-policy-agent/opa v0.21.1
@@ -37,13 +38,11 @@ require (
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/spf13/afero v1.2.2
github.com/stretchr/objx v0.3.0 // indirect
- github.com/stretchr/testify v1.6.1
+ github.com/stretchr/testify v1.7.0
github.com/testcontainers/testcontainers-go v0.3.1
github.com/twitchtv/twirp v5.10.1+incompatible
github.com/urfave/cli/v2 v2.3.0
- go.uber.org/atomic v1.5.1 // indirect
- go.uber.org/multierr v1.4.0 // indirect
- go.uber.org/zap v1.13.0
+ go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
golang.org/x/sys v0.0.0-20201006155630-ac719f4daadf // indirect
@@ -51,6 +50,6 @@ require (
google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
- gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
+ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
)
diff --git a/go.sum b/go.sum
index 41817fefed..3057895209 100644
--- a/go.sum
+++ b/go.sum
@@ -91,10 +91,10 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
-github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd h1:meqa2AA+7K1r/nfNB19K2AP/v8+nemuWeQoTSqZ2R9s=
-github.com/aquasecurity/fanal v0.0.0-20210119051230-28c249da7cfd/go.mod h1:kur6SaohYhsjQLzijAdtn+X8rkTtwxawE51WyVCXLKk=
-github.com/aquasecurity/go-dep-parser v0.0.0-20201028043324-889d4a92b8e0 h1:cLH3SebzhbJ+jU1GIad8A1N8p7m7OjHhtY6JePISiVc=
-github.com/aquasecurity/go-dep-parser v0.0.0-20201028043324-889d4a92b8e0/go.mod h1:X42mTIRhgPalSm81Om2kD+3ydeunbC8TZtZj1bvgRo8=
+github.com/aquasecurity/fanal v0.0.0-20210214122859-98d76e2b3b96 h1:5Uk/RmXp+Itbm3fLa0vg1OKBl6Z6BfxDVrJpwETYqWk=
+github.com/aquasecurity/fanal v0.0.0-20210214122859-98d76e2b3b96/go.mod h1:8HoL/kipOrDodHh+jzGgO2/iXJoRf6roLP5qPv8Mi90=
+github.com/aquasecurity/go-dep-parser v0.0.0-20210214113128-b97635cfd627 h1:ccLbhkZ54xTmI6Q+CyBL8sll+OXIEzX0aDDjmy8pVWQ=
+github.com/aquasecurity/go-dep-parser v0.0.0-20210214113128-b97635cfd627/go.mod h1:KHiF3uK6FOE1v27YK+5CWEJ1jd0OvA2Gmk8VFjT3utk=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 h1:eveqE9ivrt30CJ7dOajOfBavhZ4zPqHcZe/4tKp0alc=
@@ -107,8 +107,8 @@ github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
github.com/aquasecurity/testdocker v0.0.0-20210106133225-0b17fe083674 h1:Xq/HxWFGaB4G/prC6czH/F5woB91GMCCilJxs/5DnDk=
github.com/aquasecurity/testdocker v0.0.0-20210106133225-0b17fe083674/go.mod h1:psfu0MVaiTDLpNxCoNsTeILSKY2EICBwv345f3M+Ffs=
-github.com/aquasecurity/trivy-db v0.0.0-20210105160501-c5bf4e153277 h1:lXN72H9uNM1exUArIsN++n7b67PIaqhZON2cVWxwmpg=
-github.com/aquasecurity/trivy-db v0.0.0-20210105160501-c5bf4e153277/go.mod h1:N7CWA/vjVw78GWAdCJGhFQVqNGEA4e47a6eIWm+C/Bc=
+github.com/aquasecurity/trivy-db v0.0.0-20210214043256-acc144af2228 h1:Hl1eKVUApdGdMUNiSYWekViWccauKxscorOV2bc1cJE=
+github.com/aquasecurity/trivy-db v0.0.0-20210214043256-acc144af2228/go.mod h1:N7CWA/vjVw78GWAdCJGhFQVqNGEA4e47a6eIWm+C/Bc=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 h1:xbdUfr2KE4THsFx9CFWtWpU91lF+YhgP46moV94nYTA=
github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ=
github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
@@ -429,6 +429,8 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/masahiro331/go-mvn-version v0.0.0-20210214074851-415aa65db8c0 h1:GT77MM4NtCZv3oeOlY+Y7EANZ86/h49oqPytEKTm3f8=
+github.com/masahiro331/go-mvn-version v0.0.0-20210214074851-415aa65db8c0/go.mod h1:M5wLEr5YKZ6OcOpiNGO0qPE4cd+xjUbBLlBvJGhZgWQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -584,6 +586,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/testcontainers/testcontainers-go v0.3.1 h1:KZkEKNfnlsipJblzGCz6fmzd+0DzJ3djulYrislG3Zw=
github.com/testcontainers/testcontainers-go v0.3.1/go.mod h1:br7bkzIukhPSIjy07Ma3OuXjjFvl2jm7CDU0LQNsqLw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -624,18 +628,16 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
-go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
-go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
-go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
+go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -993,8 +995,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/internal/artifact/wire_gen.go b/internal/artifact/wire_gen.go
index 477e405fac..c1e2287120 100644
--- a/internal/artifact/wire_gen.go
+++ b/internal/artifact/wire_gen.go
@@ -14,7 +14,6 @@ import (
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/fanal/image"
"github.com/aquasecurity/trivy-db/pkg/db"
- "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/scanner/local"
@@ -28,9 +27,7 @@ import (
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
- driverFactory := library.DriverFactory{}
- libraryDetector := library.NewDetector(driverFactory)
- localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
+ localScanner := local.NewScanner(applierApplier, detector)
dockerOption, err := types.GetDockerOption(timeout)
if err != nil {
return scanner.Scanner{}, nil, err
@@ -49,9 +46,7 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration) (scanner.Scanner, error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
- driverFactory := library.DriverFactory{}
- libraryDetector := library.NewDetector(driverFactory)
- localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
+ localScanner := local.NewScanner(applierApplier, detector)
imageImage, err := image.NewArchiveImage(filePath)
if err != nil {
return scanner.Scanner{}, err
@@ -64,9 +59,7 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
- driverFactory := library.DriverFactory{}
- libraryDetector := library.NewDetector(driverFactory)
- localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
+ localScanner := local.NewScanner(applierApplier, detector)
artifact := local2.NewArtifact(dir, artifactCache)
scannerScanner := scanner.NewScanner(localScanner, artifact)
return scannerScanner, func() {
@@ -76,9 +69,7 @@ func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
- driverFactory := library.DriverFactory{}
- libraryDetector := library.NewDetector(driverFactory)
- localScanner := local.NewScanner(applierApplier, detector, libraryDetector)
+ localScanner := local.NewScanner(applierApplier, detector)
artifact, cleanup, err := remote.NewArtifact(url, artifactCache)
if err != nil {
return scanner.Scanner{}, nil, err
diff --git a/pkg/cache/remote_test.go b/pkg/cache/remote_test.go
index 28d41f1994..12db8d1c1f 100644
--- a/pkg/cache/remote_test.go
+++ b/pkg/cache/remote_test.go
@@ -8,6 +8,8 @@ import (
"testing"
"time"
+ rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
+
google_protobuf "github.com/golang/protobuf/ptypes/empty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -18,7 +20,6 @@ import (
"github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/cache"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
- "github.com/aquasecurity/trivy/rpc/detector"
)
type mockCacheServer struct {
@@ -53,7 +54,7 @@ func (s *mockCacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBl
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get(tokenHeader) {
- detector.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
+ rpcScanner.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
diff --git a/pkg/dbtest/db.go b/pkg/dbtest/db.go
new file mode 100644
index 0000000000..05700a22e4
--- /dev/null
+++ b/pkg/dbtest/db.go
@@ -0,0 +1,34 @@
+package dbtest
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ fixtures "github.com/aquasecurity/bolt-fixtures"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+)
+
+// InitDB initializes testing database.
+func InitDB(t *testing.T, fixtureFiles []string) string {
+ // Create a temp dir
+ dir := t.TempDir()
+
+ dbPath := db.Path(dir)
+ dbDir := filepath.Dir(dbPath)
+ err := os.MkdirAll(dbDir, 0700)
+ require.NoError(t, err)
+
+ // Load testdata into BoltDB
+ loader, err := fixtures.New(dbPath, fixtureFiles)
+ require.NoError(t, err)
+ require.NoError(t, loader.Load())
+ require.NoError(t, loader.Close())
+
+ // Initialize DB
+ require.NoError(t, db.Init(dir))
+
+ return dir
+}
diff --git a/pkg/detector/library/advisory_test.go b/pkg/detector/library/advisory_test.go
index 810040faf2..7f14cffee1 100644
--- a/pkg/detector/library/advisory_test.go
+++ b/pkg/detector/library/advisory_test.go
@@ -1,7 +1,6 @@
package library_test
import (
- "os"
"testing"
"github.com/stretchr/testify/assert"
@@ -9,11 +8,11 @@ import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
"github.com/aquasecurity/trivy/pkg/detector/library/comparer"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -98,8 +97,7 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Initialize DB
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
defer db.Close()
adv := library.NewAdvisory(tt.ecosystem, tt.comparer)
diff --git a/pkg/detector/library/bundler/advisory_test.go b/pkg/detector/library/bundler/advisory_test.go
index 5d970a0f41..bd9f315316 100644
--- a/pkg/detector/library/bundler/advisory_test.go
+++ b/pkg/detector/library/bundler/advisory_test.go
@@ -1,16 +1,16 @@
package bundler_test
import (
- "os"
"testing"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -65,8 +65,8 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
a := bundler.NewAdvisory()
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/library/cargo/advisory_test.go b/pkg/detector/library/cargo/advisory_test.go
index 760c132c3a..8f5dfe3582 100644
--- a/pkg/detector/library/cargo/advisory_test.go
+++ b/pkg/detector/library/cargo/advisory_test.go
@@ -1,17 +1,16 @@
package cargo_test
import (
- "os"
"testing"
- "github.com/aquasecurity/trivy/pkg/detector/library/cargo"
-
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
+ "github.com/aquasecurity/trivy/pkg/detector/library/cargo"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -81,8 +80,8 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
a := cargo.NewAdvisory()
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/library/composer/advisory_test.go b/pkg/detector/library/composer/advisory_test.go
index 64906d475e..af726cacbc 100644
--- a/pkg/detector/library/composer/advisory_test.go
+++ b/pkg/detector/library/composer/advisory_test.go
@@ -1,17 +1,16 @@
package composer_test
import (
- "os"
"testing"
- "github.com/aquasecurity/trivy/pkg/detector/library/composer"
-
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
+ "github.com/aquasecurity/trivy/pkg/detector/library/composer"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -65,8 +64,8 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
a := composer.NewAdvisory()
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/library/detect.go b/pkg/detector/library/detect.go
index fa18a2683a..dea5b85e11 100644
--- a/pkg/detector/library/detect.go
+++ b/pkg/detector/library/detect.go
@@ -1,44 +1,15 @@
package library
import (
- "path/filepath"
- "time"
-
- "github.com/google/wire"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
- "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
-// SuperSet binds the dependencies for library scan
-var SuperSet = wire.NewSet(
- wire.Struct(new(DriverFactory)),
- wire.Bind(new(Factory), new(DriverFactory)),
- NewDetector,
- wire.Bind(new(Operation), new(Detector)),
-)
-
-// Operation defines library scan operations
-type Operation interface {
- Detect(imageName string, filePath string, created time.Time, pkgs []ftypes.LibraryInfo) (vulns []types.DetectedVulnerability, err error)
-}
-
-// Detector implements driverFactory
-type Detector struct {
- driverFactory Factory
-}
-
-// NewDetector is the factory method for detector
-func NewDetector(factory Factory) Detector {
- return Detector{driverFactory: factory}
-}
-
// Detect scans and returns vulnerabilities of library
-func (d Detector) Detect(_, filePath string, _ time.Time, pkgs []ftypes.LibraryInfo) ([]types.DetectedVulnerability, error) {
- log.Logger.Debugf("Detecting library vulnerabilities, path: %s", filePath)
- driver, err := d.driverFactory.NewDriver(filepath.Base(filePath))
+func Detect(libType string, pkgs []ftypes.LibraryInfo) ([]types.DetectedVulnerability, error) {
+ driver, err := NewDriver(libType)
if err != nil {
return nil, xerrors.Errorf("failed to new driver: %w", err)
}
@@ -52,7 +23,6 @@ func (d Detector) Detect(_, filePath string, _ time.Time, pkgs []ftypes.LibraryI
}
func detect(driver Driver, libs []ftypes.LibraryInfo) ([]types.DetectedVulnerability, error) {
- log.Logger.Infof("Detecting %s vulnerabilities...", driver.Type())
var vulnerabilities []types.DetectedVulnerability
for _, lib := range libs {
vulns, err := driver.Detect(lib.Library.Name, lib.Library.Version)
diff --git a/pkg/detector/library/driver.go b/pkg/detector/library/driver.go
index 07228d0db3..5f27e549a9 100644
--- a/pkg/detector/library/driver.go
+++ b/pkg/detector/library/driver.go
@@ -1,10 +1,9 @@
package library
import (
- "fmt"
-
"golang.org/x/xerrors"
+ "github.com/aquasecurity/fanal/analyzer/library"
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
@@ -12,41 +11,36 @@ import (
"github.com/aquasecurity/trivy/pkg/detector/library/comparer"
"github.com/aquasecurity/trivy/pkg/detector/library/composer"
"github.com/aquasecurity/trivy/pkg/detector/library/ghsa"
- "github.com/aquasecurity/trivy/pkg/detector/library/node"
+ "github.com/aquasecurity/trivy/pkg/detector/library/maven"
+ "github.com/aquasecurity/trivy/pkg/detector/library/npm"
"github.com/aquasecurity/trivy/pkg/detector/library/python"
"github.com/aquasecurity/trivy/pkg/types"
)
-// Factory defines library operations
-type Factory interface {
- NewDriver(filename string) (Driver, error)
-}
-
type advisory interface {
DetectVulnerabilities(string, string) ([]types.DetectedVulnerability, error)
}
-// DriverFactory implements Factory
-type DriverFactory struct{}
-
-// NewDriver factory method for driver
-func (d DriverFactory) NewDriver(filename string) (Driver, error) {
+// NewDriver returns a driver according to the library type
+func NewDriver(libType string) (Driver, error) {
var driver Driver
- switch filename {
- case "Gemfile.lock":
+ switch libType {
+ case library.Bundler:
driver = newRubyGemsDriver()
- case "Cargo.lock":
+ case library.Cargo:
driver = newCargoDriver()
- case "composer.lock":
+ case library.Composer:
driver = newComposerDriver()
- case "package-lock.json", "yarn.lock":
+ case library.Npm, library.Yarn:
driver = newNpmDriver()
- case "Pipfile.lock", "poetry.lock":
+ case library.Pipenv, library.Poetry:
driver = newPipDriver()
- case "packages.lock.json":
+ case library.NuGet:
driver = newNugetDriver()
+ case library.Jar:
+ driver = newMavenDriver()
default:
- return Driver{}, xerrors.New(fmt.Sprintf("unsupport filename %s", filename))
+ return Driver{}, xerrors.Errorf("unsupported type %s", libType)
}
return driver, nil
}
@@ -57,17 +51,17 @@ type Driver struct {
advisories []advisory
}
-// NewDriver is the factory method from drier
-func NewDriver(advisories ...advisory) Driver {
- return Driver{advisories: advisories}
+// Aggregate aggregates drivers
+func Aggregate(ecosystem string, advisories ...advisory) Driver {
+ return Driver{ecosystem: ecosystem, advisories: advisories}
}
// Detect scans and returns vulnerabilities
func (d *Driver) Detect(pkgName string, pkgVer string) ([]types.DetectedVulnerability, error) {
var detectedVulnerabilities []types.DetectedVulnerability
uniqVulnIDMap := make(map[string]struct{})
- for _, d := range d.advisories {
- vulns, err := d.DetectVulnerabilities(pkgName, pkgVer)
+ for _, adv := range d.advisories {
+ vulns, err := adv.DetectVulnerabilities(pkgName, pkgVer)
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
@@ -90,34 +84,41 @@ func (d *Driver) Type() string {
func newRubyGemsDriver() Driver {
c := bundler.RubyGemsComparer{}
- return NewDriver(ghsa.NewAdvisory(ecosystem.Rubygems, c), bundler.NewAdvisory(),
+ return Aggregate(vulnerability.RubyGems, ghsa.NewAdvisory(ecosystem.Rubygems, c), bundler.NewAdvisory(),
NewAdvisory(vulnerability.RubyGems, c))
}
func newComposerDriver() Driver {
c := comparer.GenericComparer{}
- return NewDriver(
- ghsa.NewAdvisory(ecosystem.Composer, c), composer.NewAdvisory(),
+ return Aggregate(vulnerability.Composer, ghsa.NewAdvisory(ecosystem.Composer, c), composer.NewAdvisory(),
NewAdvisory(vulnerability.Composer, c))
}
func newCargoDriver() Driver {
- return NewDriver(cargo.NewAdvisory(), NewAdvisory(vulnerability.Cargo, comparer.GenericComparer{}))
+ return Aggregate(vulnerability.Cargo, cargo.NewAdvisory(),
+ NewAdvisory(vulnerability.Cargo, comparer.GenericComparer{}))
}
func newNpmDriver() Driver {
- c := node.NpmComparer{}
- return NewDriver(ghsa.NewAdvisory(ecosystem.Npm, c), node.NewAdvisory(),
- NewAdvisory(vulnerability.Npm, c))
+ c := npm.Comparer{}
+ return Aggregate(vulnerability.Npm, ghsa.NewAdvisory(ecosystem.Npm, c),
+ npm.NewAdvisory(), NewAdvisory(vulnerability.Npm, c))
}
func newPipDriver() Driver {
c := comparer.GenericComparer{}
- return NewDriver(ghsa.NewAdvisory(ecosystem.Pip, c), python.NewAdvisory(),
- NewAdvisory(vulnerability.Pip, c))
+ return Aggregate(vulnerability.Pip, ghsa.NewAdvisory(ecosystem.Pip, c),
+ python.NewAdvisory(), NewAdvisory(vulnerability.Pip, c))
}
func newNugetDriver() Driver {
c := comparer.GenericComparer{}
- return NewDriver(ghsa.NewAdvisory(ecosystem.Nuget, c), NewAdvisory(vulnerability.NuGet, c))
+ return Aggregate(vulnerability.NuGet, ghsa.NewAdvisory(ecosystem.Nuget, c),
+ NewAdvisory(vulnerability.NuGet, c))
+}
+
+func newMavenDriver() Driver {
+ c := maven.Comparer{}
+ return Aggregate(vulnerability.Maven, ghsa.NewAdvisory(ecosystem.Maven, c),
+ NewAdvisory(vulnerability.Maven, c))
}
diff --git a/pkg/detector/library/driver_test.go b/pkg/detector/library/driver_test.go
index bd5162476a..b1f0e3e1b4 100644
--- a/pkg/detector/library/driver_test.go
+++ b/pkg/detector/library/driver_test.go
@@ -1,22 +1,19 @@
package library_test
import (
- "os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ lib "github.com/aquasecurity/fanal/analyzer/library"
"github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestDriver_Detect(t *testing.T) {
- type fields struct {
- fileName string
- }
type args struct {
pkgName string
pkgVer string
@@ -24,7 +21,7 @@ func TestDriver_Detect(t *testing.T) {
tests := []struct {
name string
fixtures []string
- fields fields
+ libType string
args args
want []types.DetectedVulnerability
wantErr string
@@ -32,7 +29,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "happy path",
fixtures: []string{"testdata/fixtures/php.yaml"},
- fields: fields{fileName: "composer.lock"},
+ libType: lib.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.2.6",
@@ -49,7 +46,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "non-prefix buckets",
fixtures: []string{"testdata/fixtures/php-without-prefix.yaml"},
- fields: fields{fileName: "composer.lock"},
+ libType: lib.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.2.6",
@@ -66,7 +63,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no patched versions in the advisory",
fixtures: []string{"testdata/fixtures/php.yaml"},
- fields: fields{fileName: "composer.lock"},
+ libType: lib.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.4.6",
@@ -83,7 +80,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no vulnerable versions in the advisory",
fixtures: []string{"testdata/fixtures/ruby.yaml"},
- fields: fields{fileName: "Gemfile.lock"},
+ libType: lib.Bundler,
args: args{
pkgName: "activesupport",
pkgVer: "4.1.1",
@@ -100,7 +97,7 @@ func TestDriver_Detect(t *testing.T) {
{
name: "no vulnerability",
fixtures: []string{"testdata/fixtures/php.yaml"},
- fields: fields{fileName: "composer.lock"},
+ libType: lib.Composer,
args: args{
pkgName: "symfony/symfony",
pkgVer: "4.4.7",
@@ -110,12 +107,10 @@ func TestDriver_Detect(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Initialize DB
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
defer db.Close()
- factory := library.DriverFactory{}
- driver, err := factory.NewDriver(tt.fields.fileName)
+ driver, err := library.NewDriver(tt.libType)
require.NoError(t, err)
got, err := driver.Detect(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/library/ghsa/advisory_test.go b/pkg/detector/library/ghsa/advisory_test.go
index 17f7cb60f5..b0113bf524 100644
--- a/pkg/detector/library/ghsa/advisory_test.go
+++ b/pkg/detector/library/ghsa/advisory_test.go
@@ -1,18 +1,18 @@
package ghsa_test
import (
- "os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/aquasecurity/trivy-db/pkg/db"
ghsaSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library/comparer"
"github.com/aquasecurity/trivy/pkg/detector/library/ghsa"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -103,8 +103,8 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
a := ghsa.NewAdvisory(tt.fields.ecosystem, tt.fields.comparer)
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/library/maven/compare.go b/pkg/detector/library/maven/compare.go
new file mode 100644
index 0000000000..5218cb2c5e
--- /dev/null
+++ b/pkg/detector/library/maven/compare.go
@@ -0,0 +1,33 @@
+package maven
+
+import (
+ "golang.org/x/xerrors"
+
+ version "github.com/masahiro331/go-mvn-version"
+
+ dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
+ "github.com/aquasecurity/trivy/pkg/detector/library/comparer"
+)
+
+// Comparer represents a comparer for maven
+type Comparer struct{}
+
+// IsVulnerable checks if the package version is vulnerable to the advisory.
+func (n Comparer) IsVulnerable(ver string, advisory dbTypes.Advisory) bool {
+ return comparer.IsVulnerable(ver, advisory, n.matchVersion)
+}
+
+// matchVersion checks if the package version satisfies the given constraint.
+func (n Comparer) matchVersion(currentVersion, constraint string) (bool, error) {
+ v, err := version.NewVersion(currentVersion)
+ if err != nil {
+ return false, xerrors.Errorf("maven version error (%s): %s", currentVersion, err)
+ }
+
+ c, err := version.NewConstraints(constraint)
+ if err != nil {
+ return false, xerrors.Errorf("maven constraint error (%s): %s", constraint, err)
+ }
+
+ return c.Check(v), nil
+}
diff --git a/pkg/detector/library/maven/compare_test.go b/pkg/detector/library/maven/compare_test.go
new file mode 100644
index 0000000000..eafd08dadf
--- /dev/null
+++ b/pkg/detector/library/maven/compare_test.go
@@ -0,0 +1,87 @@
+package maven_test
+
+import (
+ "testing"
+
+ "github.com/aquasecurity/trivy/pkg/detector/library/maven"
+
+ "github.com/stretchr/testify/assert"
+
+ dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
+ "github.com/aquasecurity/trivy/pkg/log"
+)
+
+func TestComparer_IsVulnerable(t *testing.T) {
+ type args struct {
+ currentVersion string
+ advisory dbTypes.Advisory
+ }
+ tests := []struct {
+ name string
+ args args
+ want bool
+ }{
+ {
+ name: "happy path",
+ args: args{
+ currentVersion: "1.0.0",
+ advisory: dbTypes.Advisory{
+ VulnerableVersions: []string{"<=1.0"},
+ PatchedVersions: []string{">=1.1"},
+ },
+ },
+ want: true,
+ },
+ {
+ name: "final release",
+ args: args{
+ currentVersion: "1.2.3.final",
+ advisory: dbTypes.Advisory{
+ VulnerableVersions: []string{"<1.2.3"},
+ PatchedVersions: []string{"1.2.3"},
+ },
+ },
+ want: false,
+ },
+ {
+ name: "pre-release",
+ args: args{
+ currentVersion: "1.2.3-a1",
+ advisory: dbTypes.Advisory{
+ VulnerableVersions: []string{"<1.2.3"},
+ PatchedVersions: []string{">=1.2.3"},
+ },
+ },
+ want: true,
+ },
+ {
+ name: "multiple constraints",
+ args: args{
+ currentVersion: "2.0.0",
+ advisory: dbTypes.Advisory{
+ VulnerableVersions: []string{">=1.7.0 <1.7.16", ">=1.8.0 <1.8.8", ">=2.0.0 <2.0.8", ">=3.0.0-beta.1 <3.0.0-beta.7"},
+ PatchedVersions: []string{">=3.0.0-beta.7", ">=2.0.8 <3.0.0-beta.1", ">=1.8.8 <2.0.0", ">=1.7.16 <1.8.0"},
+ },
+ },
+ want: true,
+ },
+ {
+ name: "invalid constraint",
+ args: args{
+ currentVersion: "1.2.3",
+ advisory: dbTypes.Advisory{
+ VulnerableVersions: []string{`<1.0\.0`},
+ },
+ },
+ want: false,
+ },
+ }
+ log.InitLogger(false, false)
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := maven.Comparer{}
+ got := c.IsVulnerable(tt.args.currentVersion, tt.args.advisory)
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
diff --git a/pkg/detector/library/node/advisory.go b/pkg/detector/library/npm/advisory.go
similarity index 96%
rename from pkg/detector/library/node/advisory.go
rename to pkg/detector/library/npm/advisory.go
index f0689a1359..17cb6b56e5 100644
--- a/pkg/detector/library/node/advisory.go
+++ b/pkg/detector/library/npm/advisory.go
@@ -1,4 +1,4 @@
-package node
+package npm
import (
"strings"
@@ -12,7 +12,7 @@ import (
// Advisory encapsulate Node vulnerability source
type Advisory struct {
- comparer NpmComparer
+ comparer Comparer
vs node.VulnSrc
}
@@ -20,7 +20,7 @@ type Advisory struct {
func NewAdvisory() *Advisory {
return &Advisory{
vs: node.NewVulnSrc(),
- comparer: NpmComparer{},
+ comparer: Comparer{},
}
}
diff --git a/pkg/detector/library/node/advisory_test.go b/pkg/detector/library/npm/advisory_test.go
similarity index 87%
rename from pkg/detector/library/node/advisory_test.go
rename to pkg/detector/library/npm/advisory_test.go
index bbc73c5cb5..3f2f308bd7 100644
--- a/pkg/detector/library/node/advisory_test.go
+++ b/pkg/detector/library/npm/advisory_test.go
@@ -1,16 +1,16 @@
-package node_test
+package npm_test
import (
- "os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/aquasecurity/trivy/pkg/detector/library/node"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
+ "github.com/aquasecurity/trivy/pkg/detector/library/npm"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -72,10 +72,10 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
- a := node.NewAdvisory()
+ a := npm.NewAdvisory()
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
if tt.wantErr != "" {
require.NotNil(t, err)
diff --git a/pkg/detector/library/node/compare.go b/pkg/detector/library/npm/compare.go
similarity index 74%
rename from pkg/detector/library/node/compare.go
rename to pkg/detector/library/npm/compare.go
index 3533de0465..847254c9a8 100644
--- a/pkg/detector/library/node/compare.go
+++ b/pkg/detector/library/npm/compare.go
@@ -1,4 +1,4 @@
-package node
+package npm
import (
"golang.org/x/xerrors"
@@ -8,16 +8,16 @@ import (
"github.com/aquasecurity/trivy/pkg/detector/library/comparer"
)
-// NpmComparer represents a comparer for npm
-type NpmComparer struct{}
+// Comparer represents a comparer for npm
+type Comparer struct{}
// IsVulnerable checks if the package version is vulnerable to the advisory.
-func (n NpmComparer) IsVulnerable(ver string, advisory dbTypes.Advisory) bool {
+func (n Comparer) IsVulnerable(ver string, advisory dbTypes.Advisory) bool {
return comparer.IsVulnerable(ver, advisory, n.matchVersion)
}
// matchVersion checks if the package version satisfies the given constraint.
-func (n NpmComparer) matchVersion(currentVersion, constraint string) (bool, error) {
+func (n Comparer) matchVersion(currentVersion, constraint string) (bool, error) {
v, err := npm.NewVersion(currentVersion)
if err != nil {
return false, xerrors.Errorf("npm version error (%s): %s", currentVersion, err)
diff --git a/pkg/detector/library/node/compare_test.go b/pkg/detector/library/npm/compare_test.go
similarity index 96%
rename from pkg/detector/library/node/compare_test.go
rename to pkg/detector/library/npm/compare_test.go
index 91fe7df451..57549f0b4b 100644
--- a/pkg/detector/library/node/compare_test.go
+++ b/pkg/detector/library/npm/compare_test.go
@@ -1,4 +1,4 @@
-package node_test
+package npm_test
import (
"testing"
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
- "github.com/aquasecurity/trivy/pkg/detector/library/node"
+ "github.com/aquasecurity/trivy/pkg/detector/library/npm"
"github.com/aquasecurity/trivy/pkg/log"
)
@@ -132,7 +132,7 @@ func TestNpmComparer_IsVulnerable(t *testing.T) {
log.InitLogger(false, false)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- c := node.NpmComparer{}
+ c := npm.Comparer{}
got := c.IsVulnerable(tt.args.currentVersion, tt.args.advisory)
assert.Equal(t, tt.want, got)
})
diff --git a/pkg/detector/library/node/testdata/fixtures/invalid-type.yaml b/pkg/detector/library/npm/testdata/fixtures/invalid-type.yaml
similarity index 100%
rename from pkg/detector/library/node/testdata/fixtures/invalid-type.yaml
rename to pkg/detector/library/npm/testdata/fixtures/invalid-type.yaml
diff --git a/pkg/detector/library/node/testdata/fixtures/no-value.yaml b/pkg/detector/library/npm/testdata/fixtures/no-value.yaml
similarity index 100%
rename from pkg/detector/library/node/testdata/fixtures/no-value.yaml
rename to pkg/detector/library/npm/testdata/fixtures/no-value.yaml
diff --git a/pkg/detector/library/node/testdata/fixtures/npm.yaml b/pkg/detector/library/npm/testdata/fixtures/npm.yaml
similarity index 100%
rename from pkg/detector/library/node/testdata/fixtures/npm.yaml
rename to pkg/detector/library/npm/testdata/fixtures/npm.yaml
diff --git a/pkg/detector/library/python/advisory_test.go b/pkg/detector/library/python/advisory_test.go
index bc078e132e..6bf04b2250 100644
--- a/pkg/detector/library/python/advisory_test.go
+++ b/pkg/detector/library/python/advisory_test.go
@@ -1,16 +1,16 @@
package python_test
import (
- "os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
"github.com/aquasecurity/trivy/pkg/detector/library/python"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
)
func TestAdvisory_DetectVulnerabilities(t *testing.T) {
@@ -65,8 +65,8 @@ func TestAdvisory_DetectVulnerabilities(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
a := python.NewAdvisory()
got, err := a.DetectVulnerabilities(tt.args.pkgName, tt.args.pkgVer)
diff --git a/pkg/detector/ospkg/oracle/oracle_test.go b/pkg/detector/ospkg/oracle/oracle_test.go
index 3c044243d8..de02321138 100644
--- a/pkg/detector/ospkg/oracle/oracle_test.go
+++ b/pkg/detector/ospkg/oracle/oracle_test.go
@@ -5,17 +5,17 @@ import (
"testing"
"time"
- ftypes "github.com/aquasecurity/fanal/types"
- "github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
-
- oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
- "github.com/aquasecurity/trivy/pkg/log"
-
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
+
+ ftypes "github.com/aquasecurity/fanal/types"
+ "github.com/aquasecurity/trivy-db/pkg/db"
+ oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
+ "github.com/aquasecurity/trivy/pkg/dbtest"
+ "github.com/aquasecurity/trivy/pkg/log"
+ "github.com/aquasecurity/trivy/pkg/types"
)
func TestMain(m *testing.M) {
@@ -209,8 +209,8 @@ func TestScanner_Detect(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dir := utils.InitTestDB(t, tt.fixtures)
- defer os.RemoveAll(dir)
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
s := NewScanner()
got, err := s.Detect(tt.args.osVer, tt.args.pkgs)
diff --git a/pkg/log/logger.go b/pkg/log/logger.go
index 655f60c683..dccfbef259 100644
--- a/pkg/log/logger.go
+++ b/pkg/log/logger.go
@@ -6,6 +6,8 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/xerrors"
+
+ "github.com/aquasecurity/go-dep-parser/pkg/log"
)
var (
@@ -19,8 +21,12 @@ func InitLogger(debug, disable bool) (err error) {
debugOption = debug
Logger, err = NewLogger(debug, disable)
if err != nil {
- return xerrors.Errorf("error in new logger: %w", err)
+ return xerrors.Errorf("failed to initialize a logger: %w", err)
}
+
+ // Set logger for go-dep-parser
+ log.SetLogger(Logger)
+
return nil
}
diff --git a/pkg/report/writer.go b/pkg/report/writer.go
index 0180ac8091..ca560d109e 100644
--- a/pkg/report/writer.go
+++ b/pkg/report/writer.go
@@ -74,13 +74,14 @@ type TableWriter struct {
// Write writes the result on standard output
func (tw TableWriter) Write(results Results) error {
for _, result := range results {
+ if len(result.Vulnerabilities) == 0 {
+ continue
+ }
tw.write(result)
}
return nil
}
-// nolint: gocyclo
-// TODO: refactror and fix cyclometic complexity
func (tw TableWriter) write(result Result) {
table := tablewriter.NewWriter(tw.Output)
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
@@ -88,38 +89,7 @@ func (tw TableWriter) write(result Result) {
header = append(header, "Title")
}
table.SetHeader(header)
-
- severityCount := map[string]int{}
- for _, v := range result.Vulnerabilities {
- severityCount[v.Severity]++
-
- title := v.Title
- if title == "" {
- title = v.Description
- }
- splittedTitle := strings.Split(title, " ")
- if len(splittedTitle) >= 12 {
- title = strings.Join(splittedTitle[: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)
- }
+ severityCount := tw.setRows(table, result.Vulnerabilities)
var results []string
@@ -150,6 +120,41 @@ func (tw TableWriter) write(result Result) {
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
diff --git a/pkg/rpc/server/inject.go b/pkg/rpc/server/inject.go
index b9dc81103d..06dd02ce29 100644
--- a/pkg/rpc/server/inject.go
+++ b/pkg/rpc/server/inject.go
@@ -4,8 +4,6 @@ package server
import (
"github.com/aquasecurity/fanal/cache"
- "github.com/aquasecurity/trivy/pkg/rpc/server/library"
- "github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
"github.com/google/wire"
)
@@ -14,16 +12,6 @@ func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServ
return &ScanServer{}
}
-func initializeOspkgServer() *ospkg.Server {
- wire.Build(ospkg.SuperSet)
- return &ospkg.Server{}
-}
-
-func initializeLibServer() *library.Server {
- wire.Build(library.SuperSet)
- return &library.Server{}
-}
-
func initializeDBWorker(cacheDir string, quiet bool) dbWorker {
wire.Build(DBWorkerSuperSet)
return dbWorker{}
diff --git a/pkg/rpc/server/library/server.go b/pkg/rpc/server/library/server.go
deleted file mode 100644
index f08649b340..0000000000
--- a/pkg/rpc/server/library/server.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package library
-
-import (
- "context"
- "time"
-
- "github.com/google/wire"
- "golang.org/x/xerrors"
-
- detector "github.com/aquasecurity/trivy/pkg/detector/library"
- "github.com/aquasecurity/trivy/pkg/log"
- "github.com/aquasecurity/trivy/pkg/rpc"
- "github.com/aquasecurity/trivy/pkg/vulnerability"
- proto "github.com/aquasecurity/trivy/rpc/detector"
-)
-
-// SuperSet binds the dependencies for library RPC server
-var SuperSet = wire.NewSet(
- detector.SuperSet,
- vulnerability.SuperSet,
- NewServer,
-)
-
-// Server is for backward compatibility
-type Server struct {
- detector detector.Operation
- vulnClient vulnerability.Operation
-}
-
-// NewServer is the facotry method for Server
-func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
- return &Server{detector: detector, vulnClient: vulnClient}
-}
-
-// Detect is for backward compatibility
-func (s *Server) Detect(_ context.Context, req *proto.LibDetectRequest) (res *proto.DetectResponse, err error) {
- vulns, err := s.detector.Detect("", req.FilePath, time.Time{}, rpc.ConvertFromRPCLibraries(req.Libraries))
- if err != nil {
- err = xerrors.Errorf("failed to detect library vulnerabilities: %w", err)
- log.Logger.Error(err)
- return nil, err
- }
-
- s.vulnClient.FillInfo(vulns, "")
-
- return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRPCVulns(vulns)}, nil
-}
diff --git a/pkg/rpc/server/library/server_test.go b/pkg/rpc/server/library/server_test.go
deleted file mode 100644
index 5af70fc43e..0000000000
--- a/pkg/rpc/server/library/server_test.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package library
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/golang/protobuf/ptypes/timestamp"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/xerrors"
-
- ftypes "github.com/aquasecurity/fanal/types"
- ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
- dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
- "github.com/aquasecurity/trivy-db/pkg/utils"
- "github.com/aquasecurity/trivy/pkg/detector/library"
- "github.com/aquasecurity/trivy/pkg/log"
- "github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/vulnerability"
- "github.com/aquasecurity/trivy/rpc/common"
- proto "github.com/aquasecurity/trivy/rpc/detector"
-)
-
-func TestMain(m *testing.M) {
- log.InitLogger(false, false)
- code := m.Run()
- os.Exit(code)
-}
-
-func TestServer_Detect(t *testing.T) {
- type args struct {
- req *proto.LibDetectRequest
- }
- tests := []struct {
- name string
- args args
- detectExpectation library.OperationDetectExpectation
- fillInfoExpectation vulnerability.OperationFillInfoExpectation
- wantRes *proto.DetectResponse
- wantErr string
- }{
- {
- name: "happy path",
- args: args{
- req: &proto.LibDetectRequest{
- ImageName: "alpine:3.10",
- FilePath: "app/Pipfile.lock",
- Libraries: []*common.Library{
- {Name: "django", Version: "3.0.0"},
- },
- },
- },
- detectExpectation: library.OperationDetectExpectation{
- Args: library.OperationDetectArgs{
- FilePath: "app/Pipfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: ptypes.Library{Name: "django", Version: "3.0.0"},
- },
- },
- },
- Returns: library.OperationDetectReturns{
- Vulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2019-0001",
- PkgName: "test",
- InstalledVersion: "1",
- FixedVersion: "2",
- Vulnerability: dbTypes.Vulnerability{
- Title: "title",
- Description: "description",
- Severity: "MEDIUM",
- References: []string{"http://example.com"},
- LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
- PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
- CweIDs: []string{"CWE-78"},
- },
- Layer: ftypes.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- },
- },
- },
- },
- fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
- Args: vulnerability.OperationFillInfoArgs{
- Vulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2019-0001",
- PkgName: "test",
- InstalledVersion: "1",
- FixedVersion: "2",
- Vulnerability: dbTypes.Vulnerability{
- Title: "title",
- Description: "description",
- Severity: "MEDIUM",
- References: []string{"http://example.com"},
- LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
- PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
- CweIDs: []string{"CWE-78"},
- },
- Layer: ftypes.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- },
- },
- },
- },
- wantRes: &proto.DetectResponse{
- Vulnerabilities: []*common.Vulnerability{
- {
- VulnerabilityId: "CVE-2019-0001",
- PkgName: "test",
- InstalledVersion: "1",
- FixedVersion: "2",
- Title: "title",
- Description: "description",
- Severity: common.Severity_MEDIUM,
- Cvss: make(map[string]*common.CVSS),
- References: []string{"http://example.com"},
- LastModifiedDate: ×tamp.Timestamp{
- Seconds: 1577840460,
- },
- PublishedDate: ×tamp.Timestamp{
- Seconds: 978310860,
- },
- CweIds: []string{"CWE-78"},
- Layer: &common.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- },
- },
- },
- },
- {
- name: "Detect returns an error",
- args: args{
- req: &proto.LibDetectRequest{
- ImageName: "alpine:3.10",
- FilePath: "app/Pipfile.lock",
- Libraries: []*common.Library{
- {Name: "django", Version: "3.0.0"},
- },
- },
- },
- detectExpectation: library.OperationDetectExpectation{
- Args: library.OperationDetectArgs{
- FilePath: "app/Pipfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {Library: ptypes.Library{Name: "django", Version: "3.0.0"}},
- },
- },
- Returns: library.OperationDetectReturns{
- Err: xerrors.New("error"),
- },
- },
- wantErr: "failed to detect library vulnerabilities",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mockDetector := new(library.MockOperation)
- mockDetector.ApplyDetectExpectation(tt.detectExpectation)
- mockVulnClient := new(vulnerability.MockOperation)
- mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
-
- s := NewServer(mockDetector, mockVulnClient)
- ctx := context.TODO()
- gotRes, err := s.Detect(ctx, tt.args.req)
- if tt.wantErr != "" {
- require.NotNil(t, err, tt.name)
- assert.Contains(t, err.Error(), tt.wantErr, tt.name)
- return
- } else {
- assert.NoError(t, err, tt.name)
- }
-
- assert.Equal(t, tt.wantRes, gotRes, tt.name)
- mockDetector.AssertExpectations(t)
- mockVulnClient.AssertExpectations(t)
- })
- }
-}
diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go
index 3d29df12c3..3c9f641e94 100644
--- a/pkg/rpc/server/listen.go
+++ b/pkg/rpc/server/listen.go
@@ -19,8 +19,6 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
- "github.com/aquasecurity/trivy/rpc/detector"
- rpcDetector "github.com/aquasecurity/trivy/rpc/detector"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
@@ -75,14 +73,6 @@ func newServeMux(serverCache cache.Cache, dbUpdateWg, requestWg *sync.WaitGroup,
layerHandler := rpcCache.NewCacheServer(NewCacheServer(serverCache), nil)
mux.Handle(rpcCache.CachePathPrefix, withToken(withWaitGroup(layerHandler), token, tokenHeader))
- // osHandler is for backward compatibility
- osHandler := rpcDetector.NewOSDetectorServer(initializeOspkgServer(), nil)
- mux.Handle(rpcDetector.OSDetectorPathPrefix, withToken(withWaitGroup(osHandler), token, tokenHeader))
-
- // libHandler is for backward compatibility
- libHandler := rpcDetector.NewLibDetectorServer(initializeLibServer(), nil)
- mux.Handle(rpcDetector.LibDetectorPathPrefix, withToken(withWaitGroup(libHandler), token, tokenHeader))
-
mux.HandleFunc("/healthz", func(rw http.ResponseWriter, r *http.Request) {
if _, err := rw.Write([]byte("ok")); err != nil {
log.Logger.Errorf("health check error: %s", err)
@@ -95,7 +85,7 @@ func newServeMux(serverCache cache.Cache, dbUpdateWg, requestWg *sync.WaitGroup,
func withToken(base http.Handler, token, tokenHeader string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if token != "" && token != r.Header.Get(tokenHeader) {
- detector.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
+ rpcScanner.WriteError(w, twirp.NewError(twirp.Unauthenticated, "invalid token"))
return
}
base.ServeHTTP(w, r)
diff --git a/pkg/rpc/server/ospkg/server.go b/pkg/rpc/server/ospkg/server.go
deleted file mode 100644
index 09bcfdbc1e..0000000000
--- a/pkg/rpc/server/ospkg/server.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package ospkg
-
-import (
- "context"
- "time"
-
- "github.com/google/wire"
- "golang.org/x/xerrors"
-
- detector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
- "github.com/aquasecurity/trivy/pkg/log"
- "github.com/aquasecurity/trivy/pkg/rpc"
- "github.com/aquasecurity/trivy/pkg/vulnerability"
- proto "github.com/aquasecurity/trivy/rpc/detector"
-)
-
-// SuperSet binds dependencies for RPC server
-var SuperSet = wire.NewSet(
- detector.SuperSet,
- vulnerability.SuperSet,
- NewServer,
-)
-
-// Server is for backward compatibility
-type Server struct {
- detector detector.Operation
- vulnClient vulnerability.Operation
-}
-
-// NewServer is the factory method to return Server
-func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
- return &Server{detector: detector, vulnClient: vulnClient}
-}
-
-// Detect is for backward compatibility
-func (s *Server) Detect(_ context.Context, req *proto.OSDetectRequest) (res *proto.DetectResponse, err error) {
- vulns, eosl, err := s.detector.Detect("", req.OsFamily, req.OsName, time.Time{}, rpc.ConvertFromRPCPkgs(req.Packages))
- if err != nil {
- err = xerrors.Errorf("failed to detect vulnerabilities of OS packages: %w", err)
- log.Logger.Error(err)
- return nil, err
- }
-
- s.vulnClient.FillInfo(vulns, "")
-
- return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRPCVulns(vulns), Eosl: eosl}, nil
-}
diff --git a/pkg/rpc/server/ospkg/server_test.go b/pkg/rpc/server/ospkg/server_test.go
deleted file mode 100644
index 11bbd1f8ad..0000000000
--- a/pkg/rpc/server/ospkg/server_test.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package ospkg
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/golang/protobuf/ptypes/timestamp"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/xerrors"
-
- ftypes "github.com/aquasecurity/fanal/types"
- dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
- "github.com/aquasecurity/trivy-db/pkg/utils"
- "github.com/aquasecurity/trivy/pkg/detector/ospkg"
- "github.com/aquasecurity/trivy/pkg/log"
- "github.com/aquasecurity/trivy/pkg/types"
- "github.com/aquasecurity/trivy/pkg/vulnerability"
- "github.com/aquasecurity/trivy/rpc/common"
- proto "github.com/aquasecurity/trivy/rpc/detector"
-)
-
-func TestMain(m *testing.M) {
- _ = log.InitLogger(false, false)
- code := m.Run()
- os.Exit(code)
-}
-
-func TestServer_Detect(t *testing.T) {
- type args struct {
- req *proto.OSDetectRequest
- }
- tests := []struct {
- name string
- args args
- detectExpectation ospkg.DetectExpectation
- fillInfoExpectation vulnerability.OperationFillInfoExpectation
- wantRes *proto.DetectResponse
- wantErr string
- }{
- {
- name: "happy path",
- args: args{
- req: &proto.OSDetectRequest{
- OsFamily: "alpine",
- OsName: "3.10.2",
- Packages: []*common.Package{
- {Name: "musl", Version: "1.1.22-r3"},
- },
- },
- },
- detectExpectation: ospkg.DetectExpectation{
- Args: ospkg.DetectInput{
- OSFamily: "alpine",
- OSName: "3.10.2",
- Pkgs: []ftypes.Package{
- {Name: "musl", Version: "1.1.22-r3"},
- },
- },
- ReturnArgs: ospkg.DetectOutput{
- Eosl: false,
- Vulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2019-0001",
- PkgName: "musl",
- Vulnerability: dbTypes.Vulnerability{
- Severity: "HIGH",
- LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
- PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
- },
- Layer: ftypes.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- },
- },
- },
- },
- fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
- Args: vulnerability.OperationFillInfoArgs{
- Vulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2019-0001",
- PkgName: "musl",
- Vulnerability: dbTypes.Vulnerability{
- Severity: "HIGH",
- LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
- PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
- },
- Layer: ftypes.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffID: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- },
- },
- },
- },
- wantRes: &proto.DetectResponse{
- Vulnerabilities: []*common.Vulnerability{
- {
- VulnerabilityId: "CVE-2019-0001",
- PkgName: "musl",
- Severity: common.Severity_HIGH,
- Cvss: make(map[string]*common.CVSS),
- Layer: &common.Layer{
- Digest: "sha256:154ad0735c360b212b167f424d33a62305770a1fcfb6363882f5c436cfbd9812",
- DiffId: "sha256:b2a1a2d80bf0c747a4f6b0ca6af5eef23f043fcdb1ed4f3a3e750aef2dc68079",
- },
- LastModifiedDate: ×tamp.Timestamp{
- Seconds: 1577840460,
- },
- PublishedDate: ×tamp.Timestamp{
- Seconds: 978310860,
- },
- },
- },
- },
- },
- {
- name: "Detect returns an error",
- args: args{
- req: &proto.OSDetectRequest{
- OsFamily: "alpine",
- OsName: "3.10.2",
- Packages: []*common.Package{
- {Name: "musl", Version: "1.1.22-r3"},
- },
- },
- },
- detectExpectation: ospkg.DetectExpectation{
- Args: ospkg.DetectInput{
- OSFamily: "alpine",
- OSName: "3.10.2",
- Pkgs: []ftypes.Package{
- {Name: "musl", Version: "1.1.22-r3"},
- },
- },
- ReturnArgs: ospkg.DetectOutput{
- Err: xerrors.New("error"),
- },
- },
- wantErr: "failed to detect vulnerabilities of OS packages: error",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mockDetector := ospkg.NewMockDetector([]ospkg.DetectExpectation{tt.detectExpectation})
- mockVulnClient := new(vulnerability.MockOperation)
- mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
-
- s := NewServer(mockDetector, mockVulnClient)
- gotRes, err := s.Detect(context.TODO(), tt.args.req)
- if tt.wantErr != "" {
- require.NotNil(t, err, tt.name)
- assert.Contains(t, err.Error(), tt.wantErr, tt.name)
- return
- } else {
- assert.NoError(t, err, tt.name)
- }
-
- assert.Equal(t, tt.wantRes, gotRes, tt.name)
- mockDetector.AssertExpectations(t)
- mockVulnClient.AssertExpectations(t)
- })
- }
-}
diff --git a/pkg/rpc/server/wire_gen.go b/pkg/rpc/server/wire_gen.go
index e2bbb98d02..c7d0e24245 100644
--- a/pkg/rpc/server/wire_gen.go
+++ b/pkg/rpc/server/wire_gen.go
@@ -10,12 +10,9 @@ import (
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy-db/pkg/db"
db2 "github.com/aquasecurity/trivy/pkg/db"
- "github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
- library2 "github.com/aquasecurity/trivy/pkg/rpc/server/library"
- ospkg2 "github.com/aquasecurity/trivy/pkg/rpc/server/ospkg"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/vulnerability"
"github.com/spf13/afero"
@@ -27,32 +24,13 @@ import (
func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServer {
applierApplier := applier.NewApplier(localArtifactCache)
detector := ospkg.Detector{}
- driverFactory := library.DriverFactory{}
- libraryDetector := library.NewDetector(driverFactory)
- scanner := local.NewScanner(applierApplier, detector, libraryDetector)
+ scanner := local.NewScanner(applierApplier, detector)
config := db.Config{}
client := vulnerability.NewClient(config)
scanServer := NewScanServer(scanner, client)
return scanServer
}
-func initializeOspkgServer() *ospkg2.Server {
- detector := ospkg.Detector{}
- config := db.Config{}
- client := vulnerability.NewClient(config)
- server := ospkg2.NewServer(detector, client)
- return server
-}
-
-func initializeLibServer() *library2.Server {
- driverFactory := library.DriverFactory{}
- detector := library.NewDetector(driverFactory)
- config := db.Config{}
- client := vulnerability.NewClient(config)
- server := library2.NewServer(detector, client)
- return server
-}
-
func initializeDBWorker(cacheDir string, quiet bool) dbWorker {
config := db.Config{}
client := github.NewClient()
diff --git a/pkg/scanner/local/mock_library_detector.go b/pkg/scanner/local/mock_library_detector.go
deleted file mode 100644
index 97d7f0dd6d..0000000000
--- a/pkg/scanner/local/mock_library_detector.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Code generated by mockery v1.0.0. DO NOT EDIT.
-
-package local
-
-import mock "github.com/stretchr/testify/mock"
-import pkgtypes "github.com/aquasecurity/trivy/pkg/types"
-import time "time"
-import types "github.com/aquasecurity/fanal/types"
-
-// MockLibraryDetector is an autogenerated mock type for the LibraryDetector type
-type MockLibraryDetector struct {
- mock.Mock
-}
-
-type LibraryDetectorDetectArgs struct {
- ImageName string
- ImageNameAnything bool
- FilePath string
- FilePathAnything bool
- Created time.Time
- CreatedAnything bool
- Pkgs []types.LibraryInfo
- PkgsAnything bool
-}
-
-type LibraryDetectorDetectReturns struct {
- DetectedVulns []pkgtypes.DetectedVulnerability
- Err error
-}
-
-type LibraryDetectorDetectExpectation struct {
- Args LibraryDetectorDetectArgs
- Returns LibraryDetectorDetectReturns
-}
-
-func (_m *MockLibraryDetector) ApplyDetectExpectation(e LibraryDetectorDetectExpectation) {
- var args []interface{}
- if e.Args.ImageNameAnything {
- args = append(args, mock.Anything)
- } else {
- args = append(args, e.Args.ImageName)
- }
- if e.Args.FilePathAnything {
- args = append(args, mock.Anything)
- } else {
- args = append(args, e.Args.FilePath)
- }
- if e.Args.CreatedAnything {
- args = append(args, mock.Anything)
- } else {
- args = append(args, e.Args.Created)
- }
- if e.Args.PkgsAnything {
- args = append(args, mock.Anything)
- } else {
- args = append(args, e.Args.Pkgs)
- }
- _m.On("Detect", args...).Return(e.Returns.DetectedVulns, e.Returns.Err)
-}
-
-func (_m *MockLibraryDetector) ApplyDetectExpectations(expectations []LibraryDetectorDetectExpectation) {
- for _, e := range expectations {
- _m.ApplyDetectExpectation(e)
- }
-}
-
-// Detect provides a mock function with given fields: imageName, filePath, created, pkgs
-func (_m *MockLibraryDetector) Detect(imageName string, filePath string, created time.Time, pkgs []types.LibraryInfo) ([]pkgtypes.DetectedVulnerability, error) {
- ret := _m.Called(imageName, filePath, created, pkgs)
-
- var r0 []pkgtypes.DetectedVulnerability
- if rf, ok := ret.Get(0).(func(string, string, time.Time, []types.LibraryInfo) []pkgtypes.DetectedVulnerability); ok {
- r0 = rf(imageName, filePath, created, pkgs)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).([]pkgtypes.DetectedVulnerability)
- }
- }
-
- var r1 error
- if rf, ok := ret.Get(1).(func(string, string, time.Time, []types.LibraryInfo) error); ok {
- r1 = rf(imageName, filePath, created, pkgs)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go
index 6fd53446eb..7d416e8a6a 100644
--- a/pkg/scanner/local/scan.go
+++ b/pkg/scanner/local/scan.go
@@ -8,6 +8,8 @@ import (
"strings"
"time"
+ "github.com/aquasecurity/trivy/pkg/detector/library"
+
"github.com/google/wire"
"golang.org/x/xerrors"
@@ -16,6 +18,7 @@ import (
_ "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/jar"
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
_ "github.com/aquasecurity/fanal/analyzer/library/nuget"
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
@@ -33,7 +36,6 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpm"
"github.com/aquasecurity/fanal/applier"
ftypes "github.com/aquasecurity/fanal/types"
- libDetector "github.com/aquasecurity/trivy/pkg/detector/library"
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
@@ -47,8 +49,6 @@ var SuperSet = wire.NewSet(
wire.Bind(new(Applier), new(applier.Applier)),
ospkgDetector.SuperSet,
wire.Bind(new(OspkgDetector), new(ospkgDetector.Detector)),
- libDetector.SuperSet,
- wire.Bind(new(LibraryDetector), new(libDetector.Detector)),
NewScanner,
)
@@ -62,21 +62,15 @@ type OspkgDetector interface {
Detect(imageName, osFamily, osName string, created time.Time, pkgs []ftypes.Package) (detectedVulns []types.DetectedVulnerability, eosl bool, err error)
}
-// LibraryDetector defines operation to detect library vulnerabilities
-type LibraryDetector interface {
- Detect(imageName, filePath string, created time.Time, pkgs []ftypes.LibraryInfo) (detectedVulns []types.DetectedVulnerability, err error)
-}
-
// Scanner implements the OspkgDetector and LibraryDetector
type Scanner struct {
applier Applier
ospkgDetector OspkgDetector
- libDetector LibraryDetector
}
// NewScanner is the factory method for Scanner
-func NewScanner(applier Applier, ospkgDetector OspkgDetector, libDetector LibraryDetector) Scanner {
- return Scanner{applier: applier, ospkgDetector: ospkgDetector, libDetector: libDetector}
+func NewScanner(applier Applier, ospkgDetector OspkgDetector) Scanner {
+ return Scanner{applier: applier, ospkgDetector: ospkgDetector}
}
// Scan scans the local image and return results. TODO: fix cyclometic complexity
@@ -156,17 +150,29 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
log.Logger.Info("Trivy skips scanning programming language libraries because no supported file was detected")
return nil, nil
}
- var results report.Results
- for _, app := range apps {
- vulns, err := s.libDetector.Detect("", app.FilePath, time.Time{}, app.Libraries)
- if err != nil {
- return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err)
- }
+ var results report.Results
+ printedTypes := map[string]struct{}{}
+ for _, app := range apps {
+ if len(app.Libraries) == 0 {
+ continue
+ }
if skipped(app.FilePath, options.SkipFiles, options.SkipDirectories) {
continue
}
+ // Prevent the same log messages from being displayed many times for the same type.
+ if _, ok := printedTypes[app.Type]; !ok {
+ log.Logger.Infof("Detecting %s vulnerabilities...", app.Type)
+ printedTypes[app.Type] = struct{}{}
+ }
+
+ log.Logger.Debugf("Detecting library vulnerabilities, type: %s, path: %s", app.Type, app.FilePath)
+ vulns, err := library.Detect(app.Type, app.Libraries)
+ if err != nil {
+ return nil, xerrors.Errorf("failed vulnerability detection of libraries: %w", err)
+ }
+
libReport := report.Result{
Target: app.FilePath,
Vulnerabilities: vulns,
diff --git a/pkg/scanner/local/scan_test.go b/pkg/scanner/local/scan_test.go
index 1e4e882fa6..10a4d85634 100644
--- a/pkg/scanner/local/scan_test.go
+++ b/pkg/scanner/local/scan_test.go
@@ -4,6 +4,12 @@ import (
"errors"
"testing"
+ "github.com/aquasecurity/fanal/analyzer/library"
+
+ "github.com/aquasecurity/trivy-db/pkg/db"
+
+ "github.com/aquasecurity/trivy/pkg/dbtest"
+
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,9 +32,9 @@ func TestScanner_Scan(t *testing.T) {
tests := []struct {
name string
args args
+ fixtures []string
applyLayersExpectation ApplierApplyLayersExpectation
ospkgDetectExpectations []OspkgDetectorDetectExpectation
- libDetectExpectations []LibraryDetectorDetectExpectation
wantResults report.Results
wantOS *ftypes.OS
wantEosl bool
@@ -41,6 +47,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -62,11 +69,11 @@ func TestScanner_Scan(t *testing.T) {
},
Applications: []ftypes.Application{
{
- Type: "bundler",
+ Type: library.Bundler,
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -108,34 +115,6 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-10000",
- PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "alpine:latest (alpine 3.11)",
@@ -156,10 +135,10 @@ func TestScanner_Scan(t *testing.T) {
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-10000",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -180,6 +159,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}, ListAllPackages: true},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -212,7 +192,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -261,34 +241,6 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-10000",
- PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "alpine:latest (alpine 3.11)",
@@ -326,7 +278,7 @@ func TestScanner_Scan(t *testing.T) {
Packages: []ftypes.Package{
{
Name: "rails",
- Version: "6.0",
+ Version: "4.0.2",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -334,10 +286,10 @@ func TestScanner_Scan(t *testing.T) {
},
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-10000",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -358,6 +310,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -371,7 +324,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -382,43 +335,15 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-10000",
- PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-10000",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -436,6 +361,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -452,7 +378,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -475,34 +401,6 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-10000",
- PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
- Layer: ftypes.Layer{
- DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "alpine:latest (alpine 3.11)",
@@ -512,10 +410,10 @@ func TestScanner_Scan(t *testing.T) {
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-10000",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:0ea33a93585cf1917ba522b2304634c3073654062d5282c1346322967790ef33",
},
@@ -536,6 +434,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -552,7 +451,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -574,43 +473,15 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-10000",
- PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-10000",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "6.0",
- FixedVersion: "6.1",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -631,6 +502,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
@@ -652,6 +524,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -671,7 +544,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/Gemfile.lock",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "rails", Version: "5.1"},
+ Library: dtypes.Library{Name: "rails", Version: "4.0.2"},
Layer: ftypes.Layer{
DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
},
@@ -683,7 +556,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "/app/composer-lock.json",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "laravel", Version: "6.0.0"},
+ Library: dtypes.Library{Name: "laravel/framework", Version: "6.0.0"},
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -694,69 +567,15 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "5.1"},
- Layer: ftypes.Layer{
- DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-11111",
- PkgName: "rails",
- InstalledVersion: "5.1",
- FixedVersion: "5.2",
- Layer: ftypes.Layer{
- DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
- },
- },
- },
- },
- },
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/composer-lock.json",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "laravel", Version: "6.0.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-22222",
- PkgName: "laravel",
- InstalledVersion: "6.0.0",
- FixedVersion: "6.1.0",
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "/app/Gemfile.lock",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-11111",
+ VulnerabilityID: "CVE-2014-0081",
PkgName: "rails",
- InstalledVersion: "5.1",
- FixedVersion: "5.2",
+ InstalledVersion: "4.0.2",
+ FixedVersion: "4.0.3, 3.2.17",
Layer: ftypes.Layer{
DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
},
@@ -768,10 +587,10 @@ func TestScanner_Scan(t *testing.T) {
Target: "/app/composer-lock.json",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-22222",
- PkgName: "laravel",
+ VulnerabilityID: "CVE-2021-21263",
+ PkgName: "laravel/framework",
InstalledVersion: "6.0.0",
- FixedVersion: "6.1.0",
+ FixedVersion: "8.22.1, 7.30.3, 6.20.12",
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -795,6 +614,7 @@ func TestScanner_Scan(t *testing.T) {
SkipDirectories: []string{"/usr/lib/ruby/gems"},
},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -826,7 +646,7 @@ func TestScanner_Scan(t *testing.T) {
FilePath: "app/composer-lock.json",
Libraries: []ftypes.LibraryInfo{
{
- Library: dtypes.Library{Name: "laravel", Version: "6.0.0"},
+ Library: dtypes.Library{Name: "laravel/framework", Version: "6.0.0"},
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -837,69 +657,15 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "usr/lib/ruby/gems/2.5.0/gems/http_parser.rb-0.6.0/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "5.1"},
- Layer: ftypes.Layer{
- DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-11111",
- PkgName: "rails",
- InstalledVersion: "5.1",
- FixedVersion: "5.2",
- Layer: ftypes.Layer{
- DiffID: "sha256:5cb2a5009179b1e78ecfef81a19756328bb266456cf9a9dbbcf9af8b83b735f0",
- },
- },
- },
- },
- },
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "app/composer-lock.json",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "laravel", Version: "6.0.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- DetectedVulns: []types.DetectedVulnerability{
- {
- VulnerabilityID: "CVE-2020-22222",
- PkgName: "laravel",
- InstalledVersion: "6.0.0",
- FixedVersion: "6.1.0",
- Layer: ftypes.Layer{
- DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
- },
- },
- },
- },
- },
- },
wantResults: report.Results{
{
Target: "app/composer-lock.json",
Vulnerabilities: []types.DetectedVulnerability{
{
- VulnerabilityID: "CVE-2020-22222",
- PkgName: "laravel",
+ VulnerabilityID: "CVE-2021-21263",
+ PkgName: "laravel/framework",
InstalledVersion: "6.0.0",
- FixedVersion: "6.1.0",
+ FixedVersion: "8.22.1, 7.30.3, 6.20.12",
Layer: ftypes.Layer{
DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303",
},
@@ -920,6 +686,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -937,6 +704,7 @@ func TestScanner_Scan(t *testing.T) {
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"os", "library"}},
},
+ fixtures: []string{"testdata/fixtures/happy.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -982,12 +750,13 @@ func TestScanner_Scan(t *testing.T) {
wantErr: "failed to scan OS packages",
},
{
- name: "sad path: libDetector.Detect returns an error",
+ name: "sad path: library.Detect returns an error",
args: args{
target: "alpine:latest",
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
options: types.ScanOptions{VulnType: []string{"library"}},
},
+ fixtures: []string{"testdata/fixtures/sad.yaml"},
applyLayersExpectation: ApplierApplyLayersExpectation{
Args: ApplierApplyLayersArgs{
BlobIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
@@ -1024,24 +793,6 @@ func TestScanner_Scan(t *testing.T) {
},
},
},
- libDetectExpectations: []LibraryDetectorDetectExpectation{
- {
- Args: LibraryDetectorDetectArgs{
- FilePath: "/app/Gemfile.lock",
- Pkgs: []ftypes.LibraryInfo{
- {
- Library: dtypes.Library{Name: "rails", Version: "6.0"},
- Layer: ftypes.Layer{
- DiffID: "sha256:9bdb2c849099a99c8ab35f6fd7469c623635e8f4479a0a5a3df61e22bae509f6",
- },
- },
- },
- },
- Returns: LibraryDetectorDetectReturns{
- Err: errors.New("error"),
- },
- },
- },
wantErr: "failed to scan application libraries",
},
}
@@ -1049,16 +800,16 @@ func TestScanner_Scan(t *testing.T) {
log.InitLogger(false, true)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+ _ = dbtest.InitDB(t, tt.fixtures)
+ defer db.Close()
+
applier := new(MockApplier)
applier.ApplyApplyLayersExpectation(tt.applyLayersExpectation)
ospkgDetector := new(MockOspkgDetector)
ospkgDetector.ApplyDetectExpectations(tt.ospkgDetectExpectations)
- libDetector := new(MockLibraryDetector)
- libDetector.ApplyDetectExpectations(tt.libDetectExpectations)
-
- s := NewScanner(applier, ospkgDetector, libDetector)
+ s := NewScanner(applier, ospkgDetector)
gotResults, gotOS, gotEosl, err := s.Scan(tt.args.target, "", tt.args.layerIDs, tt.args.options)
if tt.wantErr != "" {
require.NotNil(t, err, tt.name)
@@ -1074,7 +825,6 @@ func TestScanner_Scan(t *testing.T) {
applier.AssertExpectations(t)
ospkgDetector.AssertExpectations(t)
- libDetector.AssertExpectations(t)
})
}
}
diff --git a/pkg/scanner/local/testdata/fixtures/happy.yaml b/pkg/scanner/local/testdata/fixtures/happy.yaml
new file mode 100644
index 0000000000..ba9f0d5d8b
--- /dev/null
+++ b/pkg/scanner/local/testdata/fixtures/happy.yaml
@@ -0,0 +1,28 @@
+- bucket: "GitHub Security Advisory Rubygems"
+ pairs:
+ - bucket: rails
+ pairs:
+ - key: CVE-2014-0081
+ value:
+ PatchedVersions:
+ - "4.0.3"
+ - "3.2.17"
+ VulnerableVersions:
+ - ">= 4.0.0, < 4.0.3"
+ - ">= 3.0.0, < 3.2.17"
+
+- bucket: "composer::GitHub Security Advisory Composer"
+ pairs:
+ - bucket: laravel/framework
+ pairs:
+ - key: CVE-2021-21263
+ value:
+ PatchedVersions:
+ - 8.22.1
+ - 7.30.3
+ - 6.20.12
+ VulnerableVersions:
+ - ">= 8.0.0, < 8.22.1"
+ - ">= 7.0.0, < 7.30.3"
+ - "< 6.20.12"
+
diff --git a/pkg/scanner/local/testdata/fixtures/sad.yaml b/pkg/scanner/local/testdata/fixtures/sad.yaml
new file mode 100644
index 0000000000..ea14c83c22
--- /dev/null
+++ b/pkg/scanner/local/testdata/fixtures/sad.yaml
@@ -0,0 +1,7 @@
+- bucket: "ruby-advisory-db"
+ pairs:
+ - bucket: rails
+ pairs:
+ - key: CVE-2014-0081
+ value:
+ PatchedVersions: "invalid"
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index f51b69e311..7ac3c943ce 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -3,17 +3,12 @@ package utils
import (
"fmt"
"io"
- "io/ioutil"
"os"
"path/filepath"
"strings"
- "testing"
- "github.com/stretchr/testify/require"
"golang.org/x/xerrors"
- fixtures "github.com/aquasecurity/bolt-fixtures"
- "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
)
@@ -132,26 +127,3 @@ func CopyFile(src, dst string) (int64, error) {
n, err := io.Copy(destination, source)
return n, err
}
-
-// InitTestDB is a utility function initializing BoltDB for unit testing
-func InitTestDB(t *testing.T, fixtureFiles []string) string {
- // Create a temp dir
- dir, err := ioutil.TempDir("", "TestDB")
- require.NoError(t, err)
-
- dbPath := db.Path(dir)
- dbDir := filepath.Dir(dbPath)
- err = os.MkdirAll(dbDir, 0700)
- require.NoError(t, err)
-
- // Load testdata into BoltDB
- loader, err := fixtures.New(dbPath, fixtureFiles)
- require.NoError(t, err)
- require.NoError(t, loader.Load())
- require.NoError(t, loader.Close())
-
- // Initialize DB
- require.NoError(t, db.Init(dir))
-
- return dir
-}
diff --git a/rpc/detector/service.pb.go b/rpc/detector/service.pb.go
deleted file mode 100644
index 5d012010fa..0000000000
--- a/rpc/detector/service.pb.go
+++ /dev/null
@@ -1,243 +0,0 @@
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: rpc/detector/service.proto
-
-package detector
-
-import (
- fmt "fmt"
- common "github.com/aquasecurity/trivy/rpc/common"
- proto "github.com/golang/protobuf/proto"
- timestamp "github.com/golang/protobuf/ptypes/timestamp"
- math "math"
-)
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-
-type OSDetectRequest struct {
- OsFamily string `protobuf:"bytes,1,opt,name=os_family,json=osFamily,proto3" json:"os_family,omitempty"`
- OsName string `protobuf:"bytes,2,opt,name=os_name,json=osName,proto3" json:"os_name,omitempty"`
- Packages []*common.Package `protobuf:"bytes,3,rep,name=packages,proto3" json:"packages,omitempty"`
- ImageName string `protobuf:"bytes,4,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"`
- Created *timestamp.Timestamp `protobuf:"bytes,5,opt,name=created,proto3" json:"created,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *OSDetectRequest) Reset() { *m = OSDetectRequest{} }
-func (m *OSDetectRequest) String() string { return proto.CompactTextString(m) }
-func (*OSDetectRequest) ProtoMessage() {}
-func (*OSDetectRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_93e16dbd737b8924, []int{0}
-}
-
-func (m *OSDetectRequest) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_OSDetectRequest.Unmarshal(m, b)
-}
-func (m *OSDetectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_OSDetectRequest.Marshal(b, m, deterministic)
-}
-func (m *OSDetectRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_OSDetectRequest.Merge(m, src)
-}
-func (m *OSDetectRequest) XXX_Size() int {
- return xxx_messageInfo_OSDetectRequest.Size(m)
-}
-func (m *OSDetectRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_OSDetectRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_OSDetectRequest proto.InternalMessageInfo
-
-func (m *OSDetectRequest) GetOsFamily() string {
- if m != nil {
- return m.OsFamily
- }
- return ""
-}
-
-func (m *OSDetectRequest) GetOsName() string {
- if m != nil {
- return m.OsName
- }
- return ""
-}
-
-func (m *OSDetectRequest) GetPackages() []*common.Package {
- if m != nil {
- return m.Packages
- }
- return nil
-}
-
-func (m *OSDetectRequest) GetImageName() string {
- if m != nil {
- return m.ImageName
- }
- return ""
-}
-
-func (m *OSDetectRequest) GetCreated() *timestamp.Timestamp {
- if m != nil {
- return m.Created
- }
- return nil
-}
-
-type DetectResponse struct {
- Vulnerabilities []*common.Vulnerability `protobuf:"bytes,1,rep,name=vulnerabilities,proto3" json:"vulnerabilities,omitempty"`
- Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *DetectResponse) Reset() { *m = DetectResponse{} }
-func (m *DetectResponse) String() string { return proto.CompactTextString(m) }
-func (*DetectResponse) ProtoMessage() {}
-func (*DetectResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_93e16dbd737b8924, []int{1}
-}
-
-func (m *DetectResponse) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_DetectResponse.Unmarshal(m, b)
-}
-func (m *DetectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_DetectResponse.Marshal(b, m, deterministic)
-}
-func (m *DetectResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DetectResponse.Merge(m, src)
-}
-func (m *DetectResponse) XXX_Size() int {
- return xxx_messageInfo_DetectResponse.Size(m)
-}
-func (m *DetectResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_DetectResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_DetectResponse proto.InternalMessageInfo
-
-func (m *DetectResponse) GetVulnerabilities() []*common.Vulnerability {
- if m != nil {
- return m.Vulnerabilities
- }
- return nil
-}
-
-func (m *DetectResponse) GetEosl() bool {
- if m != nil {
- return m.Eosl
- }
- return false
-}
-
-type LibDetectRequest struct {
- FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
- Libraries []*common.Library `protobuf:"bytes,2,rep,name=libraries,proto3" json:"libraries,omitempty"`
- ImageName string `protobuf:"bytes,3,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"`
- Created *timestamp.Timestamp `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *LibDetectRequest) Reset() { *m = LibDetectRequest{} }
-func (m *LibDetectRequest) String() string { return proto.CompactTextString(m) }
-func (*LibDetectRequest) ProtoMessage() {}
-func (*LibDetectRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_93e16dbd737b8924, []int{2}
-}
-
-func (m *LibDetectRequest) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_LibDetectRequest.Unmarshal(m, b)
-}
-func (m *LibDetectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_LibDetectRequest.Marshal(b, m, deterministic)
-}
-func (m *LibDetectRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LibDetectRequest.Merge(m, src)
-}
-func (m *LibDetectRequest) XXX_Size() int {
- return xxx_messageInfo_LibDetectRequest.Size(m)
-}
-func (m *LibDetectRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LibDetectRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_LibDetectRequest proto.InternalMessageInfo
-
-func (m *LibDetectRequest) GetFilePath() string {
- if m != nil {
- return m.FilePath
- }
- return ""
-}
-
-func (m *LibDetectRequest) GetLibraries() []*common.Library {
- if m != nil {
- return m.Libraries
- }
- return nil
-}
-
-func (m *LibDetectRequest) GetImageName() string {
- if m != nil {
- return m.ImageName
- }
- return ""
-}
-
-func (m *LibDetectRequest) GetCreated() *timestamp.Timestamp {
- if m != nil {
- return m.Created
- }
- return nil
-}
-
-func init() {
- proto.RegisterType((*OSDetectRequest)(nil), "trivy.detector.OSDetectRequest")
- proto.RegisterType((*DetectResponse)(nil), "trivy.detector.DetectResponse")
- proto.RegisterType((*LibDetectRequest)(nil), "trivy.detector.LibDetectRequest")
-}
-
-func init() { proto.RegisterFile("rpc/detector/service.proto", fileDescriptor_93e16dbd737b8924) }
-
-var fileDescriptor_93e16dbd737b8924 = []byte{
- // 422 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xd1, 0x8a, 0xd3, 0x40,
- 0x14, 0x86, 0xc9, 0xb6, 0x76, 0xdb, 0x53, 0xd8, 0x95, 0x01, 0x31, 0x64, 0xd1, 0x2d, 0xbd, 0xea,
- 0xd5, 0x04, 0xbb, 0xe2, 0x03, 0x88, 0x0a, 0xca, 0xa2, 0x4b, 0x14, 0x45, 0x6f, 0xca, 0x64, 0xf6,
- 0x34, 0x1d, 0x36, 0xd3, 0x93, 0xce, 0x4c, 0x0a, 0x79, 0x34, 0x9f, 0xc2, 0x57, 0x92, 0x4e, 0x98,
- 0x6a, 0xa2, 0xe8, 0xde, 0x4d, 0xe6, 0xff, 0xcf, 0x7f, 0xce, 0xf9, 0x32, 0x90, 0x98, 0x4a, 0xa6,
- 0xb7, 0xe8, 0x50, 0x3a, 0x32, 0xa9, 0x45, 0xb3, 0x57, 0x12, 0x79, 0x65, 0xc8, 0x11, 0x3b, 0x73,
- 0x46, 0xed, 0x1b, 0x1e, 0xd4, 0xe4, 0xb2, 0x20, 0x2a, 0x4a, 0x4c, 0xbd, 0x9a, 0xd7, 0xeb, 0xd4,
- 0x29, 0x8d, 0xd6, 0x09, 0x5d, 0xb5, 0x05, 0xc9, 0x8b, 0x42, 0xb9, 0x4d, 0x9d, 0x73, 0x49, 0x3a,
- 0x15, 0xbb, 0x5a, 0x58, 0x94, 0xb5, 0x51, 0xae, 0x49, 0x7d, 0x50, 0x7a, 0x68, 0x25, 0x49, 0x6b,
- 0xda, 0x76, 0x1b, 0xcd, 0x7f, 0x44, 0x70, 0xfe, 0xe1, 0xe3, 0x2b, 0xdf, 0x27, 0xc3, 0x5d, 0x8d,
- 0xd6, 0xb1, 0x0b, 0x98, 0x90, 0x5d, 0xad, 0x85, 0x56, 0x65, 0x13, 0x47, 0xb3, 0x68, 0x31, 0xc9,
- 0xc6, 0x64, 0xdf, 0xf8, 0x6f, 0xf6, 0x18, 0x4e, 0xc9, 0xae, 0xb6, 0x42, 0x63, 0x7c, 0xe2, 0xa5,
- 0x11, 0xd9, 0xf7, 0x42, 0x23, 0x7b, 0x06, 0xe3, 0x4a, 0xc8, 0x3b, 0x51, 0xa0, 0x8d, 0x07, 0xb3,
- 0xc1, 0x62, 0xba, 0x7c, 0xc4, 0xdb, 0x2d, 0xda, 0xc6, 0xfc, 0xa6, 0x55, 0xb3, 0xa3, 0x8d, 0x3d,
- 0x01, 0x50, 0x5a, 0x14, 0xd8, 0xc6, 0x0d, 0x7d, 0xdc, 0xc4, 0xdf, 0xf8, 0xc4, 0xe7, 0x70, 0x2a,
- 0x0d, 0x0a, 0x87, 0xb7, 0xf1, 0x83, 0x59, 0xb4, 0x98, 0x2e, 0x13, 0xde, 0x62, 0xe0, 0x01, 0x03,
- 0xff, 0x14, 0x30, 0x64, 0xc1, 0x3a, 0xbf, 0x83, 0xb3, 0xb0, 0x8e, 0xad, 0x68, 0x6b, 0x91, 0xbd,
- 0x86, 0xf3, 0x7d, 0x5d, 0x6e, 0xd1, 0x88, 0x5c, 0x95, 0xca, 0x29, 0xb4, 0x71, 0xe4, 0x07, 0xbc,
- 0xe8, 0x0e, 0xf8, 0xf9, 0x37, 0x53, 0x93, 0xf5, 0x6b, 0x18, 0x83, 0x21, 0x92, 0x2d, 0xfd, 0xda,
- 0xe3, 0xcc, 0x9f, 0xe7, 0xdf, 0x23, 0x78, 0x78, 0xad, 0xf2, 0x3f, 0xf8, 0xad, 0x55, 0x89, 0xab,
- 0x4a, 0xb8, 0x4d, 0xe0, 0x77, 0xb8, 0xb8, 0x11, 0x6e, 0xc3, 0xae, 0x60, 0x52, 0xaa, 0xdc, 0x08,
- 0x73, 0x18, 0xe3, 0xe4, 0x6f, 0x9c, 0xae, 0xbd, 0xdc, 0x64, 0xbf, 0x7c, 0x3d, 0x50, 0x83, 0x7f,
- 0x80, 0x1a, 0xde, 0x1b, 0xd4, 0xf2, 0x0b, 0x40, 0xf8, 0xf3, 0x64, 0xd8, 0x5b, 0x18, 0xb5, 0x67,
- 0x76, 0xc9, 0xbb, 0x8f, 0x8f, 0xf7, 0xde, 0x47, 0xf2, 0xb4, 0x6f, 0xe8, 0xf2, 0x5e, 0x7e, 0x85,
- 0xe9, 0x91, 0x09, 0x19, 0xf6, 0xee, 0x98, 0x3c, 0xeb, 0x17, 0xf6, 0xd1, 0xfd, 0x2f, 0xfa, 0x25,
- 0x7c, 0x1b, 0x07, 0x29, 0x1f, 0xf9, 0xe5, 0xae, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x60, 0x68,
- 0xcc, 0x2b, 0x48, 0x03, 0x00, 0x00,
-}
diff --git a/rpc/detector/service.proto b/rpc/detector/service.proto
deleted file mode 100644
index bd7c3c3bfe..0000000000
--- a/rpc/detector/service.proto
+++ /dev/null
@@ -1,37 +0,0 @@
-// for backward compatibility
-syntax = "proto3";
-
-import "google/protobuf/timestamp.proto";
-
-package trivy.detector;
-option go_package = "detector";
-
-import "github.com/aquasecurity/trivy/rpc/common/service.proto";
-
-service OSDetector {
- rpc Detect(OSDetectRequest) returns (DetectResponse);
-}
-
-message OSDetectRequest {
- string os_family = 1;
- string os_name = 2;
- repeated common.Package packages = 3;
- string image_name = 4;
- google.protobuf.Timestamp created = 5;
-}
-
-message DetectResponse {
- repeated common.Vulnerability vulnerabilities = 1;
- bool eosl = 2;
-}
-
-service LibDetector {
- rpc Detect(LibDetectRequest) returns (DetectResponse);
-}
-
-message LibDetectRequest {
- string file_path = 1;
- repeated common.Library libraries = 2;
- string image_name = 3;
- google.protobuf.Timestamp created = 4;
-}
diff --git a/rpc/detector/service.twirp.go b/rpc/detector/service.twirp.go
deleted file mode 100644
index 1e8e7e9e30..0000000000
--- a/rpc/detector/service.twirp.go
+++ /dev/null
@@ -1,1267 +0,0 @@
-// Code generated by protoc-gen-twirp v5.10.1, DO NOT EDIT.
-// source: rpc/detector/service.proto
-
-/*
-Package detector is a generated twirp stub package.
-This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.10.1.
-
-It is generated from these files:
- rpc/detector/service.proto
-*/
-package detector
-
-import bytes "bytes"
-import strings "strings"
-import context "context"
-import fmt "fmt"
-import ioutil "io/ioutil"
-import http "net/http"
-import strconv "strconv"
-import gzip "compress/gzip"
-
-import jsonpb "github.com/golang/protobuf/jsonpb"
-import proto "github.com/golang/protobuf/proto"
-import twirp "github.com/twitchtv/twirp"
-import ctxsetters "github.com/twitchtv/twirp/ctxsetters"
-
-// Imports only used by utility functions:
-import io "io"
-import json "encoding/json"
-import url "net/url"
-
-// A response is compressed with gzip when the response size exceeds this threshold.
-const CompressThreshold = 10000
-
-// ====================
-// OSDetector Interface
-// ====================
-
-type OSDetector interface {
- Detect(context.Context, *OSDetectRequest) (*DetectResponse, error)
-}
-
-// ==========================
-// OSDetector Protobuf Client
-// ==========================
-
-type oSDetectorProtobufClient struct {
- client HTTPClient
- urls [1]string
- opts twirp.ClientOptions
-}
-
-// NewOSDetectorProtobufClient creates a Protobuf client that implements the OSDetector interface.
-// It communicates using Protobuf and can be configured with a custom HTTPClient.
-func NewOSDetectorProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) OSDetector {
- if c, ok := client.(*http.Client); ok {
- client = withoutRedirects(c)
- }
-
- clientOpts := twirp.ClientOptions{}
- for _, o := range opts {
- o(&clientOpts)
- }
-
- prefix := urlBase(addr) + OSDetectorPathPrefix
- urls := [1]string{
- prefix + "Detect",
- }
-
- return &oSDetectorProtobufClient{
- client: client,
- urls: urls,
- opts: clientOpts,
- }
-}
-
-func (c *oSDetectorProtobufClient) Detect(ctx context.Context, in *OSDetectRequest) (*DetectResponse, error) {
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "OSDetector")
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- out := new(DetectResponse)
- err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
- if err != nil {
- twerr, ok := err.(twirp.Error)
- if !ok {
- twerr = twirp.InternalErrorWith(err)
- }
- callClientError(ctx, c.opts.Hooks, twerr)
- return nil, err
- }
-
- callClientResponseReceived(ctx, c.opts.Hooks)
-
- return out, nil
-}
-
-// ======================
-// OSDetector JSON Client
-// ======================
-
-type oSDetectorJSONClient struct {
- client HTTPClient
- urls [1]string
- opts twirp.ClientOptions
-}
-
-// NewOSDetectorJSONClient creates a JSON client that implements the OSDetector interface.
-// It communicates using JSON and can be configured with a custom HTTPClient.
-func NewOSDetectorJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) OSDetector {
- if c, ok := client.(*http.Client); ok {
- client = withoutRedirects(c)
- }
-
- clientOpts := twirp.ClientOptions{}
- for _, o := range opts {
- o(&clientOpts)
- }
-
- prefix := urlBase(addr) + OSDetectorPathPrefix
- urls := [1]string{
- prefix + "Detect",
- }
-
- return &oSDetectorJSONClient{
- client: client,
- urls: urls,
- opts: clientOpts,
- }
-}
-
-func (c *oSDetectorJSONClient) Detect(ctx context.Context, in *OSDetectRequest) (*DetectResponse, error) {
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "OSDetector")
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- out := new(DetectResponse)
- err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
- if err != nil {
- twerr, ok := err.(twirp.Error)
- if !ok {
- twerr = twirp.InternalErrorWith(err)
- }
- callClientError(ctx, c.opts.Hooks, twerr)
- return nil, err
- }
-
- callClientResponseReceived(ctx, c.opts.Hooks)
-
- return out, nil
-}
-
-// =========================
-// OSDetector Server Handler
-// =========================
-
-type oSDetectorServer struct {
- OSDetector
- hooks *twirp.ServerHooks
-}
-
-func NewOSDetectorServer(svc OSDetector, hooks *twirp.ServerHooks) TwirpServer {
- return &oSDetectorServer{
- OSDetector: svc,
- hooks: hooks,
- }
-}
-
-// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks.
-// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
-func (s *oSDetectorServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) {
- writeError(ctx, resp, err, s.hooks)
-}
-
-// OSDetectorPathPrefix is used for all URL paths on a twirp OSDetector server.
-// Requests are always: POST OSDetectorPathPrefix/method
-// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes.
-const OSDetectorPathPrefix = "/twirp/trivy.detector.OSDetector/"
-
-func (s *oSDetectorServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
- ctx := req.Context()
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "OSDetector")
- ctx = ctxsetters.WithResponseWriter(ctx, resp)
-
- var err error
- ctx, err = callRequestReceived(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- if req.Method != "POST" {
- msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method)
- err = badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, err)
- return
- }
-
- switch req.URL.Path {
- case "/twirp/trivy.detector.OSDetector/Detect":
- s.serveDetect(ctx, resp, req)
- return
- default:
- msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
- err = badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, err)
- return
- }
-}
-
-func (s *oSDetectorServer) serveDetect(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- header := req.Header.Get("Content-Type")
- i := strings.Index(header, ";")
- if i == -1 {
- i = len(header)
- }
- switch strings.TrimSpace(strings.ToLower(header[:i])) {
- case "application/json":
- s.serveDetectJSON(ctx, resp, req)
- case "application/protobuf":
- s.serveDetectProtobuf(ctx, resp, req)
- default:
- msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
- twerr := badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, twerr)
- }
-}
-
-func (s *oSDetectorServer) serveDetectJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- var err error
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- ctx, err = callRequestRouted(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- reqContent := new(OSDetectRequest)
- unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
- if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
- s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
- return
- }
-
- // Call service method
- var respContent *DetectResponse
- func() {
- defer ensurePanicResponses(ctx, resp, s.hooks)
- respContent, err = s.OSDetector.Detect(ctx, reqContent)
- }()
-
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
- if respContent == nil {
- s.writeError(ctx, resp, twirp.InternalError("received a nil *DetectResponse and nil error while calling Detect. nil responses are not supported"))
- return
- }
-
- ctx = callResponsePrepared(ctx, s.hooks)
-
- var buf bytes.Buffer
- marshaler := &jsonpb.Marshaler{OrigName: true}
- if err = marshaler.Marshal(&buf, respContent); err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response"))
- return
- }
-
- ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
- respBytes := buf.Bytes()
- resp.Header().Set("Content-Type", "application/json")
- resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
- resp.WriteHeader(http.StatusOK)
-
- if n, err := resp.Write(respBytes); err != nil {
- msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
- twerr := twirp.NewError(twirp.Unknown, msg)
- callError(ctx, s.hooks, twerr)
- }
- callResponseSent(ctx, s.hooks)
-}
-
-func (s *oSDetectorServer) serveDetectProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- var err error
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- ctx, err = callRequestRouted(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- buf, err := ioutil.ReadAll(req.Body)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
- return
- }
- reqContent := new(OSDetectRequest)
- if err = proto.Unmarshal(buf, reqContent); err != nil {
- s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
- return
- }
-
- // Call service method
- var respContent *DetectResponse
- func() {
- defer ensurePanicResponses(ctx, resp, s.hooks)
- respContent, err = s.OSDetector.Detect(ctx, reqContent)
- }()
-
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
- if respContent == nil {
- s.writeError(ctx, resp, twirp.InternalError("received a nil *DetectResponse and nil error while calling Detect. nil responses are not supported"))
- return
- }
-
- ctx = callResponsePrepared(ctx, s.hooks)
-
- respBytes, err := proto.Marshal(respContent)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response"))
- return
- }
-
- // Compress the response if the size exceeds the threshold
- if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
- respBytes, err = compressWithGzip(respBytes)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
- return
- }
- resp.Header().Set("Content-Encoding", "gzip")
- }
-
- ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
- resp.Header().Set("Content-Type", "application/protobuf")
- resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
- resp.WriteHeader(http.StatusOK)
- if n, err := resp.Write(respBytes); err != nil {
- msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
- twerr := twirp.NewError(twirp.Unknown, msg)
- callError(ctx, s.hooks, twerr)
- }
- callResponseSent(ctx, s.hooks)
-}
-
-func (s *oSDetectorServer) ServiceDescriptor() ([]byte, int) {
- return twirpFileDescriptor0, 0
-}
-
-func (s *oSDetectorServer) ProtocGenTwirpVersion() string {
- return "v5.10.1"
-}
-
-func (s *oSDetectorServer) PathPrefix() string {
- return OSDetectorPathPrefix
-}
-
-// =====================
-// LibDetector Interface
-// =====================
-
-type LibDetector interface {
- Detect(context.Context, *LibDetectRequest) (*DetectResponse, error)
-}
-
-// ===========================
-// LibDetector Protobuf Client
-// ===========================
-
-type libDetectorProtobufClient struct {
- client HTTPClient
- urls [1]string
- opts twirp.ClientOptions
-}
-
-// NewLibDetectorProtobufClient creates a Protobuf client that implements the LibDetector interface.
-// It communicates using Protobuf and can be configured with a custom HTTPClient.
-func NewLibDetectorProtobufClient(addr string, client HTTPClient, opts ...twirp.ClientOption) LibDetector {
- if c, ok := client.(*http.Client); ok {
- client = withoutRedirects(c)
- }
-
- clientOpts := twirp.ClientOptions{}
- for _, o := range opts {
- o(&clientOpts)
- }
-
- prefix := urlBase(addr) + LibDetectorPathPrefix
- urls := [1]string{
- prefix + "Detect",
- }
-
- return &libDetectorProtobufClient{
- client: client,
- urls: urls,
- opts: clientOpts,
- }
-}
-
-func (c *libDetectorProtobufClient) Detect(ctx context.Context, in *LibDetectRequest) (*DetectResponse, error) {
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "LibDetector")
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- out := new(DetectResponse)
- err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
- if err != nil {
- twerr, ok := err.(twirp.Error)
- if !ok {
- twerr = twirp.InternalErrorWith(err)
- }
- callClientError(ctx, c.opts.Hooks, twerr)
- return nil, err
- }
-
- callClientResponseReceived(ctx, c.opts.Hooks)
-
- return out, nil
-}
-
-// =======================
-// LibDetector JSON Client
-// =======================
-
-type libDetectorJSONClient struct {
- client HTTPClient
- urls [1]string
- opts twirp.ClientOptions
-}
-
-// NewLibDetectorJSONClient creates a JSON client that implements the LibDetector interface.
-// It communicates using JSON and can be configured with a custom HTTPClient.
-func NewLibDetectorJSONClient(addr string, client HTTPClient, opts ...twirp.ClientOption) LibDetector {
- if c, ok := client.(*http.Client); ok {
- client = withoutRedirects(c)
- }
-
- clientOpts := twirp.ClientOptions{}
- for _, o := range opts {
- o(&clientOpts)
- }
-
- prefix := urlBase(addr) + LibDetectorPathPrefix
- urls := [1]string{
- prefix + "Detect",
- }
-
- return &libDetectorJSONClient{
- client: client,
- urls: urls,
- opts: clientOpts,
- }
-}
-
-func (c *libDetectorJSONClient) Detect(ctx context.Context, in *LibDetectRequest) (*DetectResponse, error) {
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "LibDetector")
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- out := new(DetectResponse)
- err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
- if err != nil {
- twerr, ok := err.(twirp.Error)
- if !ok {
- twerr = twirp.InternalErrorWith(err)
- }
- callClientError(ctx, c.opts.Hooks, twerr)
- return nil, err
- }
-
- callClientResponseReceived(ctx, c.opts.Hooks)
-
- return out, nil
-}
-
-// ==========================
-// LibDetector Server Handler
-// ==========================
-
-type libDetectorServer struct {
- LibDetector
- hooks *twirp.ServerHooks
-}
-
-func NewLibDetectorServer(svc LibDetector, hooks *twirp.ServerHooks) TwirpServer {
- return &libDetectorServer{
- LibDetector: svc,
- hooks: hooks,
- }
-}
-
-// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks.
-// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
-func (s *libDetectorServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) {
- writeError(ctx, resp, err, s.hooks)
-}
-
-// LibDetectorPathPrefix is used for all URL paths on a twirp LibDetector server.
-// Requests are always: POST LibDetectorPathPrefix/method
-// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes.
-const LibDetectorPathPrefix = "/twirp/trivy.detector.LibDetector/"
-
-func (s *libDetectorServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
- ctx := req.Context()
- ctx = ctxsetters.WithPackageName(ctx, "trivy.detector")
- ctx = ctxsetters.WithServiceName(ctx, "LibDetector")
- ctx = ctxsetters.WithResponseWriter(ctx, resp)
-
- var err error
- ctx, err = callRequestReceived(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- if req.Method != "POST" {
- msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method)
- err = badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, err)
- return
- }
-
- switch req.URL.Path {
- case "/twirp/trivy.detector.LibDetector/Detect":
- s.serveDetect(ctx, resp, req)
- return
- default:
- msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
- err = badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, err)
- return
- }
-}
-
-func (s *libDetectorServer) serveDetect(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- header := req.Header.Get("Content-Type")
- i := strings.Index(header, ";")
- if i == -1 {
- i = len(header)
- }
- switch strings.TrimSpace(strings.ToLower(header[:i])) {
- case "application/json":
- s.serveDetectJSON(ctx, resp, req)
- case "application/protobuf":
- s.serveDetectProtobuf(ctx, resp, req)
- default:
- msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
- twerr := badRouteError(msg, req.Method, req.URL.Path)
- s.writeError(ctx, resp, twerr)
- }
-}
-
-func (s *libDetectorServer) serveDetectJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- var err error
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- ctx, err = callRequestRouted(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- reqContent := new(LibDetectRequest)
- unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
- if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
- s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
- return
- }
-
- // Call service method
- var respContent *DetectResponse
- func() {
- defer ensurePanicResponses(ctx, resp, s.hooks)
- respContent, err = s.LibDetector.Detect(ctx, reqContent)
- }()
-
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
- if respContent == nil {
- s.writeError(ctx, resp, twirp.InternalError("received a nil *DetectResponse and nil error while calling Detect. nil responses are not supported"))
- return
- }
-
- ctx = callResponsePrepared(ctx, s.hooks)
-
- var buf bytes.Buffer
- marshaler := &jsonpb.Marshaler{OrigName: true}
- if err = marshaler.Marshal(&buf, respContent); err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response"))
- return
- }
-
- ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
- respBytes := buf.Bytes()
- resp.Header().Set("Content-Type", "application/json")
- resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
- resp.WriteHeader(http.StatusOK)
-
- if n, err := resp.Write(respBytes); err != nil {
- msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
- twerr := twirp.NewError(twirp.Unknown, msg)
- callError(ctx, s.hooks, twerr)
- }
- callResponseSent(ctx, s.hooks)
-}
-
-func (s *libDetectorServer) serveDetectProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
- var err error
- ctx = ctxsetters.WithMethodName(ctx, "Detect")
- ctx, err = callRequestRouted(ctx, s.hooks)
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
-
- buf, err := ioutil.ReadAll(req.Body)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
- return
- }
- reqContent := new(LibDetectRequest)
- if err = proto.Unmarshal(buf, reqContent); err != nil {
- s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
- return
- }
-
- // Call service method
- var respContent *DetectResponse
- func() {
- defer ensurePanicResponses(ctx, resp, s.hooks)
- respContent, err = s.LibDetector.Detect(ctx, reqContent)
- }()
-
- if err != nil {
- s.writeError(ctx, resp, err)
- return
- }
- if respContent == nil {
- s.writeError(ctx, resp, twirp.InternalError("received a nil *DetectResponse and nil error while calling Detect. nil responses are not supported"))
- return
- }
-
- ctx = callResponsePrepared(ctx, s.hooks)
-
- respBytes, err := proto.Marshal(respContent)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response"))
- return
- }
-
- // Compress the response if the size exceeds the threshold
- if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
- respBytes, err = compressWithGzip(respBytes)
- if err != nil {
- s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
- return
- }
- resp.Header().Set("Content-Encoding", "gzip")
- }
-
- ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
- resp.Header().Set("Content-Type", "application/protobuf")
- resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
- resp.WriteHeader(http.StatusOK)
- if n, err := resp.Write(respBytes); err != nil {
- msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
- twerr := twirp.NewError(twirp.Unknown, msg)
- callError(ctx, s.hooks, twerr)
- }
- callResponseSent(ctx, s.hooks)
-}
-
-func (s *libDetectorServer) ServiceDescriptor() ([]byte, int) {
- return twirpFileDescriptor0, 1
-}
-
-func (s *libDetectorServer) ProtocGenTwirpVersion() string {
- return "v5.10.1"
-}
-
-func (s *libDetectorServer) PathPrefix() string {
- return LibDetectorPathPrefix
-}
-
-// =====
-// Utils
-// =====
-
-// HTTPClient is the interface used by generated clients to send HTTP requests.
-// It is fulfilled by *(net/http).Client, which is sufficient for most users.
-// Users can provide their own implementation for special retry policies.
-//
-// HTTPClient implementations should not follow redirects. Redirects are
-// automatically disabled if *(net/http).Client is passed to client
-// constructors. See the withoutRedirects function in this file for more
-// details.
-type HTTPClient interface {
- Do(req *http.Request) (*http.Response, error)
-}
-
-// TwirpServer is the interface generated server structs will support: they're
-// HTTP handlers with additional methods for accessing metadata about the
-// service. Those accessors are a low-level API for building reflection tools.
-// Most people can think of TwirpServers as just http.Handlers.
-type TwirpServer interface {
- http.Handler
- // ServiceDescriptor returns gzipped bytes describing the .proto file that
- // this service was generated from. Once unzipped, the bytes can be
- // unmarshalled as a
- // github.com/golang/protobuf/protoc-gen-go/descriptor.FileDescriptorProto.
- //
- // The returned integer is the index of this particular service within that
- // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a
- // low-level field, expected to be used for reflection.
- ServiceDescriptor() ([]byte, int)
- // ProtocGenTwirpVersion is the semantic version string of the version of
- // twirp used to generate this file.
- ProtocGenTwirpVersion() string
- // PathPrefix returns the HTTP URL path prefix for all methods handled by this
- // service. This can be used with an HTTP mux to route twirp requests
- // alongside non-twirp requests on one HTTP listener.
- PathPrefix() string
-}
-
-// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta).
-// Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks.
-// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
-func WriteError(resp http.ResponseWriter, err error) {
- writeError(context.Background(), resp, err, nil)
-}
-
-// writeError writes Twirp errors in the response and triggers hooks.
-func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) {
- // Non-twirp errors are wrapped as Internal (default)
- twerr, ok := err.(twirp.Error)
- if !ok {
- twerr = twirp.InternalErrorWith(err)
- }
-
- statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code())
- ctx = ctxsetters.WithStatusCode(ctx, statusCode)
- ctx = callError(ctx, hooks, twerr)
-
- respBody := marshalErrorToJSON(twerr)
-
- resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON
- resp.Header().Set("Content-Length", strconv.Itoa(len(respBody)))
- resp.WriteHeader(statusCode) // set HTTP status code and send response
-
- _, writeErr := resp.Write(respBody)
- if writeErr != nil {
- // We have three options here. We could log the error, call the Error
- // hook, or just silently ignore the error.
- //
- // Logging is unacceptable because we don't have a user-controlled
- // logger; writing out to stderr without permission is too rude.
- //
- // Calling the Error hook would confuse users: it would mean the Error
- // hook got called twice for one request, which is likely to lead to
- // duplicated log messages and metrics, no matter how well we document
- // the behavior.
- //
- // Silently ignoring the error is our least-bad option. It's highly
- // likely that the connection is broken and the original 'err' says
- // so anyway.
- _ = writeErr
- }
-
- callResponseSent(ctx, hooks)
-}
-
-// urlBase helps ensure that addr specifies a scheme. If it is unparsable
-// as a URL, it returns addr unchanged.
-func urlBase(addr string) string {
- // If the addr specifies a scheme, use it. If not, default to
- // http. If url.Parse fails on it, return it unchanged.
- url, err := url.Parse(addr)
- if err != nil {
- return addr
- }
- if url.Scheme == "" {
- url.Scheme = "http"
- }
- return url.String()
-}
-
-// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in
-// a context through the twirp.WithHTTPRequestHeaders function.
-// If there are no headers set, or if they have the wrong type, nil is returned.
-func getCustomHTTPReqHeaders(ctx context.Context) http.Header {
- header, ok := twirp.HTTPRequestHeaders(ctx)
- if !ok || header == nil {
- return nil
- }
- copied := make(http.Header)
- for k, vv := range header {
- if vv == nil {
- copied[k] = nil
- continue
- }
- copied[k] = make([]string, len(vv))
- copy(copied[k], vv)
- }
- return copied
-}
-
-// newRequest makes an http.Request from a client, adding common headers.
-func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) {
- req, err := http.NewRequest("POST", url, reqBody)
- if err != nil {
- return nil, err
- }
- req = req.WithContext(ctx)
- if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil {
- req.Header = customHeader
- }
- req.Header.Set("Accept", contentType)
- req.Header.Set("Content-Type", contentType)
- req.Header.Set("Accept-Encoding", "gzip")
- req.Header.Set("Twirp-Version", "v5.10.1")
- return req, nil
-}
-
-// JSON serialization for errors
-type twerrJSON struct {
- Code string `json:"code"`
- Msg string `json:"msg"`
- Meta map[string]string `json:"meta,omitempty"`
-}
-
-// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body.
-// If serialization fails, it will use a descriptive Internal error instead.
-func marshalErrorToJSON(twerr twirp.Error) []byte {
- // make sure that msg is not too large
- msg := twerr.Msg()
- if len(msg) > 1e6 {
- msg = msg[:1e6]
- }
-
- tj := twerrJSON{
- Code: string(twerr.Code()),
- Msg: msg,
- Meta: twerr.MetaMap(),
- }
-
- buf, err := json.Marshal(&tj)
- if err != nil {
- buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback
- }
-
- return buf
-}
-
-// errorFromResponse builds a twirp.Error from a non-200 HTTP response.
-// If the response has a valid serialized Twirp error, then it's returned.
-// If not, the response status code is used to generate a similar twirp
-// error. See twirpErrorFromIntermediary for more info on intermediary errors.
-func errorFromResponse(resp *http.Response) twirp.Error {
- statusCode := resp.StatusCode
- statusText := http.StatusText(statusCode)
-
- if isHTTPRedirect(statusCode) {
- // Unexpected redirect: it must be an error from an intermediary.
- // Twirp clients don't follow redirects automatically, Twirp only handles
- // POST requests, redirects should only happen on GET and HEAD requests.
- location := resp.Header.Get("Location")
- msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location)
- return twirpErrorFromIntermediary(statusCode, msg, location)
- }
-
- respBodyBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return wrapInternal(err, "failed to read server error response body")
- }
-
- var tj twerrJSON
- dec := json.NewDecoder(bytes.NewReader(respBodyBytes))
- dec.DisallowUnknownFields()
- if err := dec.Decode(&tj); err != nil || tj.Code == "" {
- // Invalid JSON response; it must be an error from an intermediary.
- msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText)
- return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes))
- }
-
- errorCode := twirp.ErrorCode(tj.Code)
- if !twirp.IsValidErrorCode(errorCode) {
- msg := "invalid type returned from server error response: " + tj.Code
- return twirp.InternalError(msg)
- }
-
- twerr := twirp.NewError(errorCode, tj.Msg)
- for k, v := range tj.Meta {
- twerr = twerr.WithMeta(k, v)
- }
- return twerr
-}
-
-// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors.
-// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
-// Returned twirp Errors have some additional metadata for inspection.
-func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error {
- var code twirp.ErrorCode
- if isHTTPRedirect(status) { // 3xx
- code = twirp.Internal
- } else {
- switch status {
- case 400: // Bad Request
- code = twirp.Internal
- case 401: // Unauthorized
- code = twirp.Unauthenticated
- case 403: // Forbidden
- code = twirp.PermissionDenied
- case 404: // Not Found
- code = twirp.BadRoute
- case 429, 502, 503, 504: // Too Many Requests, Bad Gateway, Service Unavailable, Gateway Timeout
- code = twirp.Unavailable
- default: // All other codes
- code = twirp.Unknown
- }
- }
-
- twerr := twirp.NewError(code, msg)
- twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary
- twerr = twerr.WithMeta("status_code", strconv.Itoa(status))
- if isHTTPRedirect(status) {
- twerr = twerr.WithMeta("location", bodyOrLocation)
- } else {
- twerr = twerr.WithMeta("body", bodyOrLocation)
- }
- return twerr
-}
-
-func isHTTPRedirect(status int) bool {
- return status >= 300 && status <= 399
-}
-
-// wrapInternal wraps an error with a prefix as an Internal error.
-// The original error cause is accessible by github.com/pkg/errors.Cause.
-func wrapInternal(err error, prefix string) twirp.Error {
- return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err})
-}
-
-type wrappedError struct {
- prefix string
- cause error
-}
-
-func (e *wrappedError) Cause() error { return e.cause }
-func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() }
-
-// ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal
-// error response (status 500), and error hooks are properly called with the panic wrapped as an error.
-// The panic is re-raised so it can be handled normally with middleware.
-func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) {
- if r := recover(); r != nil {
- // Wrap the panic as an error so it can be passed to error hooks.
- // The original error is accessible from error hooks, but not visible in the response.
- err := errFromPanic(r)
- twerr := &internalWithCause{msg: "Internal service panic", cause: err}
- // Actually write the error
- writeError(ctx, resp, twerr, hooks)
- // If possible, flush the error to the wire.
- f, ok := resp.(http.Flusher)
- if ok {
- f.Flush()
- }
-
- panic(r)
- }
-}
-
-// errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error.
-func errFromPanic(p interface{}) error {
- if err, ok := p.(error); ok {
- return err
- }
- return fmt.Errorf("panic: %v", p)
-}
-
-// internalWithCause is a Twirp Internal error wrapping an original error cause, accessible
-// by github.com/pkg/errors.Cause, but the original error message is not exposed on Msg().
-type internalWithCause struct {
- msg string
- cause error
-}
-
-func (e *internalWithCause) Cause() error { return e.cause }
-func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() }
-func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal }
-func (e *internalWithCause) Msg() string { return e.msg }
-func (e *internalWithCause) Meta(key string) string { return "" }
-func (e *internalWithCause) MetaMap() map[string]string { return nil }
-func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e }
-
-// malformedRequestError is used when the twirp server cannot unmarshal a request
-func malformedRequestError(msg string) twirp.Error {
- return twirp.NewError(twirp.Malformed, msg)
-}
-
-// badRouteError is used when the twirp server cannot route a request
-func badRouteError(msg string, method, url string) twirp.Error {
- err := twirp.NewError(twirp.BadRoute, msg)
- err = err.WithMeta("twirp_invalid_route", method+" "+url)
- return err
-}
-
-// withoutRedirects makes sure that the POST request can not be redirected.
-// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or
-// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the
-// method to GET and removing the body. This produces very confusing error messages, so instead we
-// set a redirect policy that always errors. This stops Go from executing the redirect.
-//
-// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect
-// policy - if so, we'll run through that policy first.
-//
-// Because this requires modifying the http.Client, we make a new copy of the client and return it.
-func withoutRedirects(in *http.Client) *http.Client {
- copy := *in
- copy.CheckRedirect = func(req *http.Request, via []*http.Request) error {
- if in.CheckRedirect != nil {
- // Run the input's redirect if it exists, in case it has side effects, but ignore any error it
- // returns, since we want to use ErrUseLastResponse.
- err := in.CheckRedirect(req, via)
- _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use.
- }
- return http.ErrUseLastResponse
- }
- return ©
-}
-
-// doProtobufRequest makes a Protobuf request to the remote Twirp service.
-func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
- reqBodyBytes, err := proto.Marshal(in)
- if err != nil {
- return wrapInternal(err, "failed to marshal proto request")
- }
- reqBody := bytes.NewBuffer(reqBodyBytes)
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
-
- req, err := newRequest(ctx, url, reqBody, "application/protobuf")
- if err != nil {
- return wrapInternal(err, "could not build request")
- }
- ctx, err = callClientRequestPrepared(ctx, hooks, req)
- if err != nil {
- return err
- }
-
- req = req.WithContext(ctx)
- resp, err := client.Do(req)
- if err != nil {
- return wrapInternal(err, "failed to do request")
- }
-
- defer func() {
- cerr := resp.Body.Close()
- if err == nil && cerr != nil {
- err = wrapInternal(cerr, "failed to close response body")
- }
- }()
-
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
-
- if resp.StatusCode != 200 {
- return errorFromResponse(resp)
- }
-
- r := resp.Body
- if resp.Header.Get("Content-Encoding") == "gzip" {
- r, err = gzip.NewReader(r)
- if err != nil {
- return wrapInternal(err, "invalid gzip")
- }
- }
-
- respBodyBytes, err := ioutil.ReadAll(r)
- if err != nil {
- return wrapInternal(err, "failed to read response body")
- }
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
-
- if err = proto.Unmarshal(respBodyBytes, out); err != nil {
- return wrapInternal(err, "failed to unmarshal proto response")
- }
- return nil
-}
-
-// doJSONRequest makes a JSON request to the remote Twirp service.
-func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (err error) {
- reqBody := bytes.NewBuffer(nil)
- marshaler := &jsonpb.Marshaler{OrigName: true}
- if err = marshaler.Marshal(reqBody, in); err != nil {
- return wrapInternal(err, "failed to marshal json request")
- }
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
-
- req, err := newRequest(ctx, url, reqBody, "application/json")
- if err != nil {
- return wrapInternal(err, "could not build request")
- }
- ctx, err = callClientRequestPrepared(ctx, hooks, req)
- if err != nil {
- return err
- }
-
- req = req.WithContext(ctx)
- resp, err := client.Do(req)
- if err != nil {
- return wrapInternal(err, "failed to do request")
- }
-
- defer func() {
- cerr := resp.Body.Close()
- if err == nil && cerr != nil {
- err = wrapInternal(cerr, "failed to close response body")
- }
- }()
-
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
-
- if resp.StatusCode != 200 {
- return errorFromResponse(resp)
- }
-
- unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
- if err = unmarshaler.Unmarshal(resp.Body, out); err != nil {
- return wrapInternal(err, "failed to unmarshal json response")
- }
- if err = ctx.Err(); err != nil {
- return wrapInternal(err, "aborted because context was done")
- }
- return nil
-}
-
-// Call twirp.ServerHooks.RequestReceived if the hook is available
-func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
- if h == nil || h.RequestReceived == nil {
- return ctx, nil
- }
- return h.RequestReceived(ctx)
-}
-
-// Call twirp.ServerHooks.RequestRouted if the hook is available
-func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
- if h == nil || h.RequestRouted == nil {
- return ctx, nil
- }
- return h.RequestRouted(ctx)
-}
-
-// Call twirp.ServerHooks.ResponsePrepared if the hook is available
-func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context {
- if h == nil || h.ResponsePrepared == nil {
- return ctx
- }
- return h.ResponsePrepared(ctx)
-}
-
-// Call twirp.ServerHooks.ResponseSent if the hook is available
-func callResponseSent(ctx context.Context, h *twirp.ServerHooks) {
- if h == nil || h.ResponseSent == nil {
- return
- }
- h.ResponseSent(ctx)
-}
-
-// Call twirp.ServerHooks.Error if the hook is available
-func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context {
- if h == nil || h.Error == nil {
- return ctx
- }
- return h.Error(ctx, err)
-}
-
-// compressWithGzip compresses the data with gzip
-func compressWithGzip(data []byte) ([]byte, error) {
- var b bytes.Buffer
- gz := gzip.NewWriter(&b)
- defer gz.Close()
-
- if _, err := gz.Write(data); err != nil {
- return nil, err
- }
-
- if err := gz.Flush(); err != nil {
- return nil, err
- }
-
- if err := gz.Close(); err != nil {
- return nil, err
- }
-
- return b.Bytes(), nil
-}
-
-func isGZipAcceptable(request *http.Request) bool {
- for _, encoding := range request.Header["Accept-Encoding"] {
- if encoding == "gzip" {
- return true
- }
- }
- return false
-}
-
-func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
- if h == nil || h.ResponseReceived == nil {
- return
- }
- h.ResponseReceived(ctx)
-}
-
-func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) {
- if h == nil || h.RequestPrepared == nil {
- return ctx, nil
- }
- return h.RequestPrepared(ctx, req)
-}
-
-func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) {
- if h == nil || h.Error == nil {
- return
- }
- h.Error(ctx, err)
-}
-
-var twirpFileDescriptor0 = []byte{
- // 422 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xd1, 0x8a, 0xd3, 0x40,
- 0x14, 0x86, 0xc9, 0xb6, 0x76, 0xdb, 0x53, 0xd8, 0x95, 0x01, 0x31, 0x64, 0xd1, 0x2d, 0xbd, 0xea,
- 0xd5, 0x04, 0xbb, 0xe2, 0x03, 0x88, 0x0a, 0xca, 0xa2, 0x4b, 0x14, 0x45, 0x6f, 0xca, 0x64, 0xf6,
- 0x34, 0x1d, 0x36, 0xd3, 0x93, 0xce, 0x4c, 0x0a, 0x79, 0x34, 0x9f, 0xc2, 0x57, 0x92, 0x4e, 0x98,
- 0x6a, 0xa2, 0xe8, 0xde, 0x4d, 0xe6, 0xff, 0xcf, 0x7f, 0xce, 0xf9, 0x32, 0x90, 0x98, 0x4a, 0xa6,
- 0xb7, 0xe8, 0x50, 0x3a, 0x32, 0xa9, 0x45, 0xb3, 0x57, 0x12, 0x79, 0x65, 0xc8, 0x11, 0x3b, 0x73,
- 0x46, 0xed, 0x1b, 0x1e, 0xd4, 0xe4, 0xb2, 0x20, 0x2a, 0x4a, 0x4c, 0xbd, 0x9a, 0xd7, 0xeb, 0xd4,
- 0x29, 0x8d, 0xd6, 0x09, 0x5d, 0xb5, 0x05, 0xc9, 0x8b, 0x42, 0xb9, 0x4d, 0x9d, 0x73, 0x49, 0x3a,
- 0x15, 0xbb, 0x5a, 0x58, 0x94, 0xb5, 0x51, 0xae, 0x49, 0x7d, 0x50, 0x7a, 0x68, 0x25, 0x49, 0x6b,
- 0xda, 0x76, 0x1b, 0xcd, 0x7f, 0x44, 0x70, 0xfe, 0xe1, 0xe3, 0x2b, 0xdf, 0x27, 0xc3, 0x5d, 0x8d,
- 0xd6, 0xb1, 0x0b, 0x98, 0x90, 0x5d, 0xad, 0x85, 0x56, 0x65, 0x13, 0x47, 0xb3, 0x68, 0x31, 0xc9,
- 0xc6, 0x64, 0xdf, 0xf8, 0x6f, 0xf6, 0x18, 0x4e, 0xc9, 0xae, 0xb6, 0x42, 0x63, 0x7c, 0xe2, 0xa5,
- 0x11, 0xd9, 0xf7, 0x42, 0x23, 0x7b, 0x06, 0xe3, 0x4a, 0xc8, 0x3b, 0x51, 0xa0, 0x8d, 0x07, 0xb3,
- 0xc1, 0x62, 0xba, 0x7c, 0xc4, 0xdb, 0x2d, 0xda, 0xc6, 0xfc, 0xa6, 0x55, 0xb3, 0xa3, 0x8d, 0x3d,
- 0x01, 0x50, 0x5a, 0x14, 0xd8, 0xc6, 0x0d, 0x7d, 0xdc, 0xc4, 0xdf, 0xf8, 0xc4, 0xe7, 0x70, 0x2a,
- 0x0d, 0x0a, 0x87, 0xb7, 0xf1, 0x83, 0x59, 0xb4, 0x98, 0x2e, 0x13, 0xde, 0x62, 0xe0, 0x01, 0x03,
- 0xff, 0x14, 0x30, 0x64, 0xc1, 0x3a, 0xbf, 0x83, 0xb3, 0xb0, 0x8e, 0xad, 0x68, 0x6b, 0x91, 0xbd,
- 0x86, 0xf3, 0x7d, 0x5d, 0x6e, 0xd1, 0x88, 0x5c, 0x95, 0xca, 0x29, 0xb4, 0x71, 0xe4, 0x07, 0xbc,
- 0xe8, 0x0e, 0xf8, 0xf9, 0x37, 0x53, 0x93, 0xf5, 0x6b, 0x18, 0x83, 0x21, 0x92, 0x2d, 0xfd, 0xda,
- 0xe3, 0xcc, 0x9f, 0xe7, 0xdf, 0x23, 0x78, 0x78, 0xad, 0xf2, 0x3f, 0xf8, 0xad, 0x55, 0x89, 0xab,
- 0x4a, 0xb8, 0x4d, 0xe0, 0x77, 0xb8, 0xb8, 0x11, 0x6e, 0xc3, 0xae, 0x60, 0x52, 0xaa, 0xdc, 0x08,
- 0x73, 0x18, 0xe3, 0xe4, 0x6f, 0x9c, 0xae, 0xbd, 0xdc, 0x64, 0xbf, 0x7c, 0x3d, 0x50, 0x83, 0x7f,
- 0x80, 0x1a, 0xde, 0x1b, 0xd4, 0xf2, 0x0b, 0x40, 0xf8, 0xf3, 0x64, 0xd8, 0x5b, 0x18, 0xb5, 0x67,
- 0x76, 0xc9, 0xbb, 0x8f, 0x8f, 0xf7, 0xde, 0x47, 0xf2, 0xb4, 0x6f, 0xe8, 0xf2, 0x5e, 0x7e, 0x85,
- 0xe9, 0x91, 0x09, 0x19, 0xf6, 0xee, 0x98, 0x3c, 0xeb, 0x17, 0xf6, 0xd1, 0xfd, 0x2f, 0xfa, 0x25,
- 0x7c, 0x1b, 0x07, 0x29, 0x1f, 0xf9, 0xe5, 0xae, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x60, 0x68,
- 0xcc, 0x2b, 0x48, 0x03, 0x00, 0x00,
-}