diff --git a/go.mod b/go.mod index a7012d8832..702129b940 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/aquasecurity/fanal v0.0.0-20200317181056-f28b6d21845c github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b - github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1 + github.com/aquasecurity/trivy-db v0.0.0-20200318223623-7d3e67b057d4 github.com/caarlos0/env/v6 v6.0.0 github.com/cenkalti/backoff v2.2.1+incompatible github.com/cheggaaa/pb/v3 v3.0.3 diff --git a/go.sum b/go.sum index 2ce76d0c09..83ccf90606 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ github.com/aquasecurity/fanal v0.0.0-20200317181056-f28b6d21845c/go.mod h1:yPZqe github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b h1:55Ulc/gvfWm4ylhVaR7MxOwujRjA6et7KhmUbSgUFf4= github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b/go.mod h1:BpNTD9vHfrejKsED9rx04ldM1WIbeyXGYxUrqTVwxVQ= github.com/aquasecurity/trivy v0.1.6/go.mod h1:5hobyhxLzDtxruHzPxpND2PUKOssvGUdE9BocpJUwo4= -github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1 h1:IVXoVH8ejJuBdxgH/+er2WjBxc0tqIGuBCqI5aWW3A0= -github.com/aquasecurity/trivy-db v0.0.0-20191226181755-d6cabf5bc5d1/go.mod h1:Uf9bXd50zTHtWTP7+7u5+OFCPtUVrmsS4v0RXd7E5lw= +github.com/aquasecurity/trivy-db v0.0.0-20200318223623-7d3e67b057d4 h1:DeVT3LzIgKc+5sGhSGusWL+JZ/kiWGjKjQYeshz/gOk= +github.com/aquasecurity/trivy-db v0.0.0-20200318223623-7d3e67b057d4/go.mod h1:Uf9bXd50zTHtWTP7+7u5+OFCPtUVrmsS4v0RXd7E5lw= github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 h1:xbdUfr2KE4THsFx9CFWtWpU91lF+YhgP46moV94nYTA= github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2/go.mod h1:6NhOP0CjZJL27bZZcaHECtzWdwDDm2g6yCY0QgXEGQQ= github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= diff --git a/internal/app.go b/internal/app.go index 6c7c69ac8d..85172e8a61 100644 --- a/internal/app.go +++ b/internal/app.go @@ -1,9 +1,14 @@ package internal import ( + "encoding/json" + "fmt" + "io" "strings" "time" + "github.com/aquasecurity/trivy-db/pkg/db" + "github.com/urfave/cli" "github.com/aquasecurity/trivy-db/pkg/types" @@ -14,6 +19,11 @@ import ( "github.com/aquasecurity/trivy/pkg/vulnerability" ) +type VersionInfo struct { + Version string `json:",omitempty"` + VulnerabilityDB db.Metadata `json:",omitempty"` +} + var ( templateFlag = cli.StringFlag{ Name: "template, t", @@ -174,6 +184,10 @@ OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} {{end}}{{$option}}{{end}}{{end}} ` + cli.VersionPrinter = func(c *cli.Context) { + showVersion(c.String("cache-dir"), c.String("format"), c.App.Version, c.App.Writer) + } + app := cli.NewApp() app.Name = "trivy" app.Version = version @@ -232,6 +246,44 @@ OPTIONS: return app } +func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer) { + db.Init(cacheDir) + metadata, err := db.Config{}.GetMetadata() + if err != nil { + fmt.Fprintf(outputWriter, "unable to display current version: %s", err.Error()) + return + } + switch outputFormat { + case "json": + b, _ := json.Marshal(VersionInfo{ + Version: version, + VulnerabilityDB: db.Metadata{ + Version: metadata.Version, + Type: metadata.Type, + NextUpdate: metadata.NextUpdate.UTC(), + UpdatedAt: metadata.UpdatedAt.UTC(), + }, + }) + fmt.Fprintln(outputWriter, string(b)) + default: + var dbType string + switch metadata.Type { + case 0: + dbType = "Full" + case 1: + dbType = "Light" + } + + fmt.Fprintf(outputWriter, `Version: %s +Vulnerability DB: + Type: %s + Version: %d + UpdatedAt: %s + NextUpdate: %s +`, version, dbType, metadata.Version, metadata.UpdatedAt.UTC().String(), metadata.NextUpdate.UTC().String()) + } +} + func NewClientCommand() cli.Command { return cli.Command{ Name: "client", diff --git a/internal/app_test.go b/internal/app_test.go new file mode 100644 index 0000000000..a8fc12f19d --- /dev/null +++ b/internal/app_test.go @@ -0,0 +1,94 @@ +package internal + +import ( + "io/ioutil" + "os" + "testing" + "time" + + "github.com/aquasecurity/trivy-db/pkg/db" + + "github.com/stretchr/testify/assert" +) + +type fakeIOWriter struct { + written []byte +} + +func (f *fakeIOWriter) Write(p []byte) (n int, err error) { + f.written = append(f.written, p...) + return len(p), nil +} + +func Test_showVersion(t *testing.T) { + type args struct { + cacheDir string + outputFormat string + version string + } + tests := []struct { + name string + args args + createDB bool + expectedOutput string + }{ + { + name: "happy path, table output", + args: args{ + outputFormat: "table", + version: "v1.2.3", + }, + expectedOutput: `Version: v1.2.3 +Vulnerability DB: + Type: Light + Version: 42 + UpdatedAt: 2020-03-16 23:40:20 +0000 UTC + NextUpdate: 2020-03-16 23:57:00 +0000 UTC +`, + createDB: true, + }, + { + name: "happy path, JSON output", + args: args{ + outputFormat: "json", + version: "1.2.3", + }, + expectedOutput: `{"Version":"1.2.3","VulnerabilityDB":{"Version":42,"Type":1,"NextUpdate":"2020-03-16T23:57:00Z","UpdatedAt":"2020-03-16T23:40:20Z"}} +`, + createDB: true, + }, + { + name: "sad path, no DB is available", + args: args{ + outputFormat: "table", + version: "1.2.3", + }, + expectedOutput: `unable to display current version: unexpected end of JSON input`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d, _ := ioutil.TempDir("", "Test_showVersion-*") + defer func() { + os.RemoveAll(d) + }() + + if tt.createDB { + db.Init(d) + db.Config{}.SetMetadata(db.Metadata{ + Version: 42, + Type: 1, + NextUpdate: time.Unix(1584403020, 0), + UpdatedAt: time.Unix(1584402020, 0), + }) + db.Close() + } + + var wb []byte + fw := fakeIOWriter{written: wb} + + showVersion(d, tt.args.outputFormat, tt.args.version, &fw) + assert.Equal(t, tt.expectedOutput, string(fw.written), tt.name) + }) + } +} diff --git a/pkg/db/db_test.go b/pkg/db/db_test.go index a1913dc02c..bcc2ba4859 100644 --- a/pkg/db/db_test.go +++ b/pkg/db/db_test.go @@ -42,6 +42,9 @@ func (_m *MockConfig) GetMetadata() (db.Metadata, error) { } func TestClient_NeedsUpdate(t *testing.T) { + timeNextUpdateDay1 := time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC) + timeNextUpdateDay2 := time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC) + type getMetadataOutput struct { metadata db.Metadata err error @@ -64,7 +67,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 1, Type: db.TypeFull, - NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay1, }, }, expected: true, @@ -87,7 +90,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 1, Type: db.TypeFull, - NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay1, }, }, expected: true, @@ -100,7 +103,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 0, Type: db.TypeFull, - NextUpdate: time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay1, }, }, expected: true, @@ -113,7 +116,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 1, Type: db.TypeFull, - NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay1, }, }, skip: true, @@ -127,7 +130,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 1, Type: db.TypeFull, - NextUpdate: time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay2, }, }, expected: false, @@ -140,7 +143,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 2, Type: db.TypeFull, - NextUpdate: time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay2, }, }, expectedError: xerrors.New("the version of DB schema doesn't match. Local DB: 2, Expected: 1"), @@ -163,7 +166,7 @@ func TestClient_NeedsUpdate(t *testing.T) { metadata: db.Metadata{ Version: 0, Type: db.TypeFull, - NextUpdate: time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdateDay1, }, }, skip: true, diff --git a/pkg/rpc/server/listen_test.go b/pkg/rpc/server/listen_test.go index 497930f5e0..1f6af1ad03 100644 --- a/pkg/rpc/server/listen_test.go +++ b/pkg/rpc/server/listen_test.go @@ -25,6 +25,9 @@ func TestMain(m *testing.M) { } func Test_dbWorker_update(t *testing.T) { + timeNextUpdate := time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC) + timeUpdateAt := time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC) + type needsUpdateInput struct { appVersion string skip bool @@ -67,8 +70,8 @@ func Test_dbWorker_update(t *testing.T) { want: db.Metadata{ Version: 1, Type: db.TypeFull, - NextUpdate: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC), - UpdatedAt: time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC), + NextUpdate: timeNextUpdate, + UpdatedAt: timeUpdateAt, }, }, {