mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 07:40:48 -08:00
322 lines
8.7 KiB
Go
322 lines
8.7 KiB
Go
package db_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/samber/lo"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/aquasecurity/trivy-db/pkg/metadata"
|
|
"github.com/aquasecurity/trivy/internal/dbtest"
|
|
"github.com/aquasecurity/trivy/pkg/clock"
|
|
"github.com/aquasecurity/trivy/pkg/db"
|
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
|
"github.com/aquasecurity/trivy/pkg/log"
|
|
)
|
|
|
|
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)
|
|
|
|
timeDownloadAt := time.Date(2019, 9, 30, 22, 30, 0, 0, time.UTC)
|
|
|
|
tests := []struct {
|
|
name string
|
|
skip bool
|
|
dbFileExists bool
|
|
metadata metadata.Metadata
|
|
want bool
|
|
wantLogs []string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "happy path",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "happy path for first run",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{},
|
|
want: true,
|
|
wantLogs: []string{
|
|
"There is no valid metadata file",
|
|
},
|
|
},
|
|
{
|
|
name: "happy path for first run without trivy.db",
|
|
dbFileExists: false,
|
|
want: true,
|
|
wantLogs: []string{
|
|
"There is no db file",
|
|
"There is no valid metadata file",
|
|
},
|
|
},
|
|
{
|
|
name: "happy path with old schema version",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: 0,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
want: true,
|
|
wantLogs: []string{
|
|
"The local DB schema version does not match with supported version schema.",
|
|
},
|
|
},
|
|
{
|
|
name: "happy path with --skip-db-update",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
skip: true,
|
|
want: false,
|
|
wantLogs: []string{
|
|
"Skipping DB update...",
|
|
},
|
|
},
|
|
{
|
|
name: "skip downloading DB",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay2,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
want: false,
|
|
wantLogs: []string{
|
|
"DB update was skipped because the local DB is the latest",
|
|
},
|
|
},
|
|
{
|
|
name: "newer schema version",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion + 1,
|
|
NextUpdate: timeNextUpdateDay2,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
wantErr: fmt.Sprintf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
|
|
db.SchemaVersion+1, db.SchemaVersion),
|
|
wantLogs: []string{
|
|
"Trivy version is old. Update to the latest version.",
|
|
},
|
|
},
|
|
{
|
|
name: "--skip-db-update without trivy.db on the first run",
|
|
dbFileExists: false,
|
|
skip: true,
|
|
wantErr: "--skip-db-update cannot be specified on the first run",
|
|
wantLogs: []string{
|
|
"There is no db file",
|
|
"There is no valid metadata file",
|
|
"The first run cannot skip downloading DB",
|
|
},
|
|
},
|
|
{
|
|
name: "--skip-db-update without metadata.json on the first run",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{},
|
|
skip: true,
|
|
wantErr: "--skip-db-update cannot be specified on the first run",
|
|
wantLogs: []string{
|
|
"There is no valid metadata file",
|
|
"The first run cannot skip downloading DB",
|
|
},
|
|
},
|
|
{
|
|
name: "--skip-db-update with different schema version",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: 0,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
skip: true,
|
|
wantErr: fmt.Sprintf("--skip-db-update cannot be specified with the old DB schema. Local DB: %d, Expected: %d",
|
|
0, db.SchemaVersion),
|
|
wantLogs: []string{
|
|
"The local DB has an old schema version which is not supported by the current version of Trivy CLI. DB needs to be updated.",
|
|
},
|
|
},
|
|
{
|
|
name: "happy with old DownloadedAt",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: timeDownloadAt,
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "skip downloading DB with recent DownloadedAt",
|
|
dbFileExists: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: time.Date(2019, 9, 30, 23, 30, 0, 0, time.UTC),
|
|
},
|
|
want: false,
|
|
wantLogs: []string{
|
|
"DB update was skipped because the local DB was downloaded during the last hour",
|
|
},
|
|
},
|
|
{
|
|
name: "DownloadedAt is zero, skip is false",
|
|
dbFileExists: true,
|
|
skip: false,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
DownloadedAt: time.Time{}, // zero time
|
|
NextUpdate: timeNextUpdateDay1,
|
|
},
|
|
want: true,
|
|
wantLogs: []string{
|
|
"Trivy DB may be corrupted and will be re-downloaded. If you manually downloaded DB - use the `--skip-db-update` flag to skip updating DB.",
|
|
},
|
|
},
|
|
{
|
|
name: "DownloadedAt is zero, skip is true",
|
|
dbFileExists: true,
|
|
skip: true,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
DownloadedAt: time.Time{}, // zero time
|
|
NextUpdate: timeNextUpdateDay1,
|
|
},
|
|
want: false,
|
|
wantLogs: []string{
|
|
"Skipping DB update...",
|
|
},
|
|
},
|
|
{
|
|
name: "DownloadedAt is zero, skip is true, old schema version",
|
|
dbFileExists: true,
|
|
skip: true,
|
|
metadata: metadata.Metadata{
|
|
Version: 0,
|
|
DownloadedAt: time.Time{}, // zero time
|
|
NextUpdate: timeNextUpdateDay1,
|
|
},
|
|
wantErr: "--skip-db-update cannot be specified with the old DB schema. Local DB: 0, Expected: 2",
|
|
want: false,
|
|
wantLogs: []string{
|
|
"The local DB has an old schema version which is not supported by the current version of Trivy CLI. DB needs to be updated.",
|
|
},
|
|
},
|
|
{
|
|
name: "trivy.db is missing but metadata with recent DownloadedAt",
|
|
dbFileExists: false,
|
|
metadata: metadata.Metadata{
|
|
Version: db.SchemaVersion,
|
|
NextUpdate: timeNextUpdateDay1,
|
|
DownloadedAt: time.Date(2019, 9, 30, 23, 30, 0, 0, time.UTC),
|
|
},
|
|
want: true,
|
|
wantLogs: []string{
|
|
"There is no db file",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
out := bytes.NewBuffer(nil)
|
|
logger := log.New(log.NewHandler(out, &log.Options{Level: log.LevelDebug}))
|
|
log.SetDefault(logger)
|
|
|
|
// Initialize DB with metadata and optionally create DB file
|
|
dbDir := dbtest.InitWithMetadata(t, tt.metadata, tt.dbFileExists)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, db.Close())
|
|
})
|
|
|
|
// Set a fake time
|
|
ctx := clock.With(t.Context(), time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC))
|
|
|
|
client := db.NewClient(dbDir, true)
|
|
needsUpdate, err := client.NeedsUpdate(ctx, "test", tt.skip)
|
|
|
|
// Compare log messages
|
|
require.Len(t, lo.Compact(strings.Split(out.String(), "\n")), len(tt.wantLogs))
|
|
for _, logMsg := range tt.wantLogs {
|
|
assert.Contains(t, out.String(), logMsg)
|
|
}
|
|
|
|
switch {
|
|
case tt.wantErr != "":
|
|
require.ErrorContains(t, err, tt.wantErr, tt.name)
|
|
default:
|
|
require.NoError(t, err, tt.name)
|
|
}
|
|
|
|
assert.Equal(t, tt.want, needsUpdate)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_Download(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want metadata.Metadata
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "happy path",
|
|
input: "testdata/db.tar.gz",
|
|
want: metadata.Metadata{
|
|
Version: 1,
|
|
NextUpdate: time.Date(3000, 1, 1, 18, 5, 43, 198355188, time.UTC),
|
|
UpdatedAt: time.Date(3000, 1, 1, 12, 5, 43, 198355588, time.UTC),
|
|
DownloadedAt: time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC),
|
|
},
|
|
},
|
|
{
|
|
name: "invalid gzip",
|
|
input: "testdata/trivy.db",
|
|
wantErr: "OCI artifact error: failed to download vulnerability DB",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Set a fake time
|
|
ctx := clock.With(t.Context(), time.Date(2019, 10, 1, 0, 0, 0, 0, time.UTC))
|
|
|
|
// Fake DB
|
|
art := dbtest.NewFakeDB(t, tt.input, dbtest.FakeDBOptions{})
|
|
|
|
dbDir := db.Dir(t.TempDir())
|
|
client := db.NewClient(dbDir, true, db.WithOCIArtifact(art))
|
|
err := client.Download(ctx, dbDir, ftypes.RegistryOptions{})
|
|
if tt.wantErr != "" {
|
|
assert.ErrorContains(t, err, tt.wantErr)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
meta := metadata.NewClient(dbDir)
|
|
got, err := meta.Get()
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|