diff --git a/pkg/commands/operation/operation.go b/pkg/commands/operation/operation.go index 7eab510f539b..8f8561a7c290 100644 --- a/pkg/commands/operation/operation.go +++ b/pkg/commands/operation/operation.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/go-redis/redis/v8" + "github.com/google/go-containerregistry/pkg/name" "github.com/google/wire" "github.com/samber/lo" "golang.org/x/xerrors" @@ -110,7 +111,8 @@ func (c Cache) ClearArtifacts() error { } // DownloadDB downloads the DB -func DownloadDB(ctx context.Context, appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool, opt ftypes.RegistryOptions) error { +func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository name.Reference, quiet, skipUpdate bool, + opt ftypes.RegistryOptions) error { mu.Lock() defer mu.Unlock() diff --git a/pkg/db/db.go b/pkg/db/db.go index fddd3393ea70..9ecb281b064e 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -4,9 +4,9 @@ import ( "context" "errors" "fmt" - "strings" "time" + "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "golang.org/x/xerrors" "k8s.io/utils/clock" @@ -19,8 +19,13 @@ import ( ) const ( - dbMediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip" - defaultDBRepository = "ghcr.io/aquasecurity/trivy-db" + SchemaVersion = db.SchemaVersion + dbMediaType = "application/vnd.aquasec.trivy.db.layer.v1.tar+gzip" +) + +var ( + DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-db", db.SchemaVersion) + defaultRepository, _ = name.NewTag(DefaultRepository) ) // Operation defines the DB operations @@ -32,7 +37,7 @@ type Operation interface { type options struct { artifact *oci.Artifact clock clock.Clock - dbRepository string + dbRepository name.Reference } // Option is a functional option @@ -46,7 +51,7 @@ func WithOCIArtifact(art *oci.Artifact) Option { } // WithDBRepository takes a dbRepository -func WithDBRepository(dbRepository string) Option { +func WithDBRepository(dbRepository name.Reference) Option { return func(opts *options) { opts.dbRepository = dbRepository } @@ -72,19 +77,13 @@ type Client struct { func NewClient(cacheDir string, quiet bool, opts ...Option) *Client { o := &options{ clock: clock.RealClock{}, - dbRepository: defaultDBRepository, + dbRepository: defaultRepository, } for _, opt := range opts { opt(o) } - // Add the schema version as a tag if the tag doesn't exist. - // This is required for backward compatibility. - if !strings.Contains(o.dbRepository, ":") { - o.dbRepository = fmt.Sprintf("%s:%d", o.dbRepository, db.SchemaVersion) - } - return &Client{ options: o, cacheDir: cacheDir, @@ -195,7 +194,7 @@ func (c *Client) initOCIArtifact(opt types.RegistryOptions) (*oci.Artifact, erro return c.artifact, nil } - art, err := oci.NewArtifact(c.dbRepository, c.quiet, opt) + art, err := oci.NewArtifact(c.dbRepository.String(), c.quiet, opt) if err != nil { var terr *transport.Error if errors.As(err, &terr) { diff --git a/pkg/fanal/analyzer/analyzer_test.go b/pkg/fanal/analyzer/analyzer_test.go index 2c7284a1ae83..8fee82acf600 100644 --- a/pkg/fanal/analyzer/analyzer_test.go +++ b/pkg/fanal/analyzer/analyzer_test.go @@ -3,6 +3,7 @@ package analyzer_test import ( "context" "fmt" + "github.com/google/go-containerregistry/pkg/name" "os" "sync" "testing" @@ -12,11 +13,11 @@ import ( "golang.org/x/sync/semaphore" "golang.org/x/xerrors" - xio "github.com/aquasecurity/trivy/pkg/x/io" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/javadb" "github.com/aquasecurity/trivy/pkg/mapfs" + xio "github.com/aquasecurity/trivy/pkg/x/io" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/apk" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar" @@ -335,15 +336,18 @@ func TestAnalyzerGroup_AnalyzeFile(t *testing.T) { FilePath: "/lib/apk/db/installed", Packages: types.Packages{ { - ID: "musl@1.1.24-r2", - Name: "musl", - Version: "1.1.24-r2", - SrcName: "musl", - SrcVersion: "1.1.24-r2", - Licenses: []string{"MIT"}, - Arch: "x86_64", - Digest: "sha1:cb2316a189ebee5282c4a9bd98794cc2477a74c6", - InstalledFiles: []string{"lib/libc.musl-x86_64.so.1", "lib/ld-musl-x86_64.so.1"}, + ID: "musl@1.1.24-r2", + Name: "musl", + Version: "1.1.24-r2", + SrcName: "musl", + SrcVersion: "1.1.24-r2", + Licenses: []string{"MIT"}, + Arch: "x86_64", + Digest: "sha1:cb2316a189ebee5282c4a9bd98794cc2477a74c6", + InstalledFiles: []string{ + "lib/libc.musl-x86_64.so.1", + "lib/ld-musl-x86_64.so.1", + }, }, }, }, @@ -615,7 +619,9 @@ func TestAnalyzerGroup_PostAnalyze(t *testing.T) { if tt.analyzerType == analyzer.TypeJar { // init java-trivy-db with skip update - javadb.Init("./language/java/jar/testdata", "ghcr.io/aquasecurity/trivy-java-db", true, false, types.RegistryOptions{Insecure: false}) + repo, err := name.NewTag(javadb.DefaultRepository) + require.NoError(t, err) + javadb.Init("./language/java/jar/testdata", repo, true, false, types.RegistryOptions{Insecure: false}) } ctx := context.Background() diff --git a/pkg/fanal/analyzer/language/java/jar/jar_test.go b/pkg/fanal/analyzer/language/java/jar/jar_test.go index 133ead426d7a..3988dc27daf5 100644 --- a/pkg/fanal/analyzer/language/java/jar/jar_test.go +++ b/pkg/fanal/analyzer/language/java/jar/jar_test.go @@ -2,6 +2,8 @@ package jar import ( "context" + "github.com/google/go-containerregistry/pkg/name" + "github.com/stretchr/testify/require" "os" "path/filepath" "testing" @@ -130,13 +132,15 @@ func Test_javaLibraryAnalyzer_Analyze(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // init java-trivy-db with skip update - javadb.Init("testdata", defaultJavaDBRepository, true, false, types.RegistryOptions{Insecure: false}) + repo, err := name.NewTag(javadb.DefaultRepository) + require.NoError(t, err) + javadb.Init("testdata", repo, true, false, types.RegistryOptions{Insecure: false}) a := javaLibraryAnalyzer{} ctx := context.Background() mfs := mapfs.New() - err := mfs.MkdirAll(filepath.Dir(tt.inputFile), os.ModePerm) + err = mfs.MkdirAll(filepath.Dir(tt.inputFile), os.ModePerm) assert.NoError(t, err) err = mfs.WriteFile(tt.inputFile, tt.inputFile) assert.NoError(t, err) diff --git a/pkg/flag/db_flags.go b/pkg/flag/db_flags.go index 7e018e865a77..58e7809a2152 100644 --- a/pkg/flag/db_flags.go +++ b/pkg/flag/db_flags.go @@ -1,14 +1,17 @@ package flag import ( + "fmt" + + "github.com/google/go-containerregistry/pkg/name" + "go.uber.org/zap" "golang.org/x/xerrors" + "github.com/aquasecurity/trivy/pkg/db" + "github.com/aquasecurity/trivy/pkg/javadb" "github.com/aquasecurity/trivy/pkg/log" ) -const defaultDBRepository = "ghcr.io/aquasecurity/trivy-db:2" -const defaultJavaDBRepository = "ghcr.io/aquasecurity/trivy-java-db:1" - var ( ResetFlag = Flag[bool]{ Name: "reset", @@ -49,13 +52,13 @@ var ( DBRepositoryFlag = Flag[string]{ Name: "db-repository", ConfigName: "db.repository", - Default: defaultDBRepository, + Default: db.DefaultRepository, Usage: "OCI repository to retrieve trivy-db from", } JavaDBRepositoryFlag = Flag[string]{ Name: "java-db-repository", ConfigName: "db.java-repository", - Default: defaultJavaDBRepository, + Default: javadb.DefaultRepository, Usage: "OCI repository to retrieve trivy-java-db from", } LightFlag = Flag[bool]{ @@ -86,8 +89,8 @@ type DBOptions struct { DownloadJavaDBOnly bool SkipJavaDBUpdate bool NoProgress bool - DBRepository string - JavaDBRepository string + DBRepository name.Reference + JavaDBRepository name.Reference Light bool // deprecated } @@ -145,6 +148,32 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { log.Logger.Warn("'--light' option is deprecated and will be removed. See also: https://github.com/aquasecurity/trivy/discussions/1649") } + var dbRepository, javaDBRepository name.Reference + var err error + if f.DBRepository != nil { + if dbRepository, err = name.ParseReference(f.DBRepository.Value(), name.WithDefaultTag("")); err != nil { + return DBOptions{}, xerrors.Errorf("invalid db repository: %w", err) + } + // Add the schema version if the tag is not specified for backward compatibility. + if t, ok := dbRepository.(name.Tag); ok && t.TagStr() == "" { + dbRepository = t.Tag(fmt.Sprint(db.SchemaVersion)) + log.Logger.Infow("Adding schema version to the DB repository for backward compatibility", + zap.String("repository", dbRepository.String())) + } + } + + if f.JavaDBRepository != nil { + if javaDBRepository, err = name.ParseReference(f.JavaDBRepository.Value(), name.WithDefaultTag("")); err != nil { + return DBOptions{}, xerrors.Errorf("invalid javadb repository: %w", err) + } + // Add the schema version if the tag is not specified for backward compatibility. + if t, ok := javaDBRepository.(name.Tag); ok && t.TagStr() == "" { + javaDBRepository = t.Tag(fmt.Sprint(javadb.SchemaVersion)) + log.Logger.Infow("Adding schema version to the Java DB repository for backward compatibility", + zap.String("repository", javaDBRepository.String())) + } + } + return DBOptions{ Reset: f.Reset.Value(), DownloadDBOnly: downloadDBOnly, @@ -153,7 +182,7 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { SkipJavaDBUpdate: skipJavaDBUpdate, Light: light, NoProgress: f.NoProgress.Value(), - DBRepository: f.DBRepository.Value(), - JavaDBRepository: f.JavaDBRepository.Value(), + DBRepository: dbRepository, + JavaDBRepository: javaDBRepository, }, nil } diff --git a/pkg/flag/db_flags_test.go b/pkg/flag/db_flags_test.go index c590ed49f7a3..b53f29135d74 100644 --- a/pkg/flag/db_flags_test.go +++ b/pkg/flag/db_flags_test.go @@ -1,6 +1,7 @@ package flag_test import ( + "github.com/google/go-containerregistry/pkg/name" "testing" "github.com/spf13/viper" @@ -15,9 +16,11 @@ import ( func TestDBFlagGroup_ToOptions(t *testing.T) { type fields struct { - SkipDBUpdate bool - DownloadDBOnly bool - Light bool + SkipDBUpdate bool + DownloadDBOnly bool + Light bool + DBRepository string + JavaDBRepository string } tests := []struct { name string @@ -29,22 +32,30 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { { name: "happy", fields: fields{ - SkipDBUpdate: true, - DownloadDBOnly: false, + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepository: "ghcr.io/aquasecurity/trivy-db", + JavaDBRepository: "ghcr.io/aquasecurity/trivy-java-db", }, want: flag.DBOptions{ - SkipDBUpdate: true, - DownloadDBOnly: false, + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepository: name.Tag{}, // All fields are unexported + JavaDBRepository: name.Tag{}, // All fields are unexported }, assertion: require.NoError, }, { name: "light", fields: fields{ - Light: true, + Light: true, + DBRepository: "ghcr.io/aquasecurity/trivy-db", + JavaDBRepository: "ghcr.io/aquasecurity/trivy-java-db", }, want: flag.DBOptions{ - Light: true, + Light: true, + DBRepository: name.Tag{}, // All fields are unexported + JavaDBRepository: name.Tag{}, // All fields are unexported }, wantLogs: []string{ "'--light' option is deprecated and will be removed. See also: https://github.com/aquasecurity/trivy/discussions/1649", @@ -61,6 +72,17 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { require.ErrorContains(t, err, "--skip-db-update and --download-db-only options can not be specified both") }, }, + { + name: "invalid repo", + fields: fields{ + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepository: "foo:bar:baz", + }, + assertion: func(t require.TestingT, err error, msgs ...interface{}) { + require.ErrorContains(t, err, "invalid db repository") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -71,16 +93,20 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { viper.Set(flag.SkipDBUpdateFlag.ConfigName, tt.fields.SkipDBUpdate) viper.Set(flag.DownloadDBOnlyFlag.ConfigName, tt.fields.DownloadDBOnly) viper.Set(flag.LightFlag.ConfigName, tt.fields.Light) + viper.Set(flag.DBRepositoryFlag.ConfigName, tt.fields.DBRepository) + viper.Set(flag.JavaDBRepositoryFlag.ConfigName, tt.fields.JavaDBRepository) // Assert options f := &flag.DBFlagGroup{ - DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(), - SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(), - Light: flag.LightFlag.Clone(), + DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(), + SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(), + Light: flag.LightFlag.Clone(), + DBRepository: flag.DBRepositoryFlag.Clone(), + JavaDBRepository: flag.JavaDBRepositoryFlag.Clone(), } got, err := f.ToOptions() tt.assertion(t, err) - assert.Equalf(t, tt.want, got, "ToOptions()") + assert.EqualExportedValues(t, tt.want, got) // Assert log messages var gotMessages []string diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index faa110a8460a..86194b263569 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -7,10 +7,10 @@ import ( "os" "path/filepath" "sort" - "strings" "sync" "time" + "github.com/google/go-containerregistry/pkg/name" "golang.org/x/xerrors" "github.com/aquasecurity/trivy-java-db/pkg/db" @@ -22,13 +22,16 @@ import ( ) const ( - mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip" + SchemaVersion = db.SchemaVersion + mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip" ) +var DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-java-db", SchemaVersion) + var updater *Updater type Updater struct { - repo string + repo name.Reference dbDir string skip bool quiet bool @@ -50,14 +53,14 @@ func (u *Updater) Update() error { } } - if (meta.Version != db.SchemaVersion || meta.NextUpdate.Before(time.Now().UTC())) && !u.skip { + if (meta.Version != SchemaVersion || meta.NextUpdate.Before(time.Now().UTC())) && !u.skip { // Download DB log.Logger.Infof("Java DB Repository: %s", u.repo) log.Logger.Info("Downloading the Java DB...") // TODO: support remote options var a *oci.Artifact - if a, err = oci.NewArtifact(u.repo, u.quiet, u.registryOption); err != nil { + if a, err = oci.NewArtifact(u.repo.String(), u.quiet, u.registryOption); err != nil { return xerrors.Errorf("oci error: %w", err) } if err = a.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil { @@ -82,12 +85,7 @@ func (u *Updater) Update() error { return nil } -func Init(cacheDir, javaDBRepository string, skip, quiet bool, registryOption ftypes.RegistryOptions) { - // Add the schema version as a tag if the tag doesn't exist. - // This is required for backward compatibility. - if !strings.Contains(javaDBRepository, ":") { - javaDBRepository = fmt.Sprintf("%s:%d", javaDBRepository, db.SchemaVersion) - } +func Init(cacheDir string, javaDBRepository name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { updater = &Updater{ repo: javaDBRepository, dbDir: filepath.Join(cacheDir, "java-db"), diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go index 33a3a8ee8a81..7433bf20a560 100644 --- a/pkg/rpc/server/listen.go +++ b/pkg/rpc/server/listen.go @@ -9,6 +9,7 @@ import ( "time" "github.com/NYTimes/gziphandler" + "github.com/google/go-containerregistry/pkg/name" "github.com/twitchtv/twirp" "golang.org/x/xerrors" @@ -33,14 +34,14 @@ type Server struct { cacheDir string token string tokenHeader string - dbRepository string + dbRepository name.Reference // For OCI registries types.RegistryOptions } // NewServer returns an instance of Server -func NewServer(appVersion, addr, cacheDir, token, tokenHeader, dbRepository string, opt types.RegistryOptions) Server { +func NewServer(appVersion, addr, cacheDir, token, tokenHeader string, dbRepository name.Reference, opt types.RegistryOptions) Server { return Server{ appVersion: appVersion, addr: addr,