From 4b0e33d1bffacd9bea4e0ae00c7e18680a3ed03b Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Tue, 8 Oct 2024 15:15:48 +0900 Subject: [PATCH] fix: options for multiple vulnerability/java db references --- config/config.go | 8 +++++--- detector/javadb/javadb.go | 39 +++++++++++++++++++++++++++++++++-- detector/library.go | 40 +++++++++++++++++++++++++++++------- go.mod | 2 +- subcmds/report.go | 23 ++++++++++++++------- subcmds/string_array_flag.go | 19 +++++++++++++++++ subcmds/tui.go | 16 +++++++++++++++ 7 files changed, 127 insertions(+), 20 deletions(-) create mode 100644 subcmds/string_array_flag.go diff --git a/config/config.go b/config/config.go index 5e726e1fa6..c8afe5aace 100644 --- a/config/config.go +++ b/config/config.go @@ -89,9 +89,11 @@ type ReportOpts struct { // TrivyOpts is options for trivy DBs type TrivyOpts struct { - TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"` - TrivyJavaDBRepository string `json:"trivyJavaDBRepository,omitempty"` - TrivySkipJavaDBUpdate bool `json:"trivySkipJavaDBUpdate,omitempty"` + TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"` + TrivyDBRepositories []string `json:"trivyDBRepositories,omitempty"` + TrivyJavaDBRepository string `json:"trivyJavaDBRepository,omitempty"` // only for backward compatibility + TrivyJavaDBRepositories []string `json:"trivyJavaDBRepositories,omitempty"` + TrivySkipJavaDBUpdate bool `json:"trivySkipJavaDBUpdate,omitempty"` } // ValidateOnConfigtest validates diff --git a/detector/javadb/javadb.go b/detector/javadb/javadb.go index b1c23fd999..553bc2d187 100644 --- a/detector/javadb/javadb.go +++ b/detector/javadb/javadb.go @@ -7,14 +7,19 @@ package javadb import ( "context" "errors" + "fmt" "os" "path/filepath" + "slices" + "strings" "time" "github.com/aquasecurity/trivy-java-db/pkg/db" "github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar" "github.com/aquasecurity/trivy/pkg/fanal/types" + trivyjavadb "github.com/aquasecurity/trivy/pkg/javadb" "github.com/aquasecurity/trivy/pkg/oci" + "github.com/google/go-containerregistry/pkg/name" "golang.org/x/xerrors" "github.com/future-architect/vuls/config" @@ -45,10 +50,40 @@ func UpdateJavaDB(trivyOpts config.TrivyOpts, noProgress bool) error { } // Download DB - logging.Log.Infof("Trivy Java DB Repository: %s", trivyOpts.TrivyJavaDBRepository) + repos := trivyOpts.TrivyJavaDBRepositories + if trivyOpts.TrivyJavaDBRepository != "" && !slices.Contains(repos, trivyOpts.TrivyJavaDBRepository) { + repos = append(repos, trivyOpts.TrivyJavaDBRepository) + } + logging.Log.Infof("Trivy Java DB Repository: %s", strings.Join(repos, ",")) logging.Log.Info("Downloading Trivy Java DB...") - a := oci.NewArtifact(trivyOpts.TrivyJavaDBRepository, types.RegistryOptions{}) + refs := make([]name.Reference, 0, len(repos)) + for _, repo := range repos { + ref, err := func() (name.Reference, error) { + ref, err := name.ParseReference(repo, name.WithDefaultTag("")) + if err != nil { + return nil, err + } + + // Add the schema version if the tag is not specified for backward compatibility. + t, ok := ref.(name.Tag) + if !ok || t.TagStr() != "" { + return ref, nil + } + + ref = t.Tag(fmt.Sprint(trivyjavadb.SchemaVersion)) + logging.Log.Infof("Adding schema version to the DB repository for backward compatibility. repository: %s", ref.String()) + + return ref, nil + }() + if err != nil { + return xerrors.Errorf("invalid javadb repository: %w", err) + } + refs = append(refs, ref) + } + + a := oci.NewArtifacts(refs, types.RegistryOptions{}) + if err = a.Download(context.Background(), dbDir, oci.DownloadOption{ MediaType: "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip", Quiet: noProgress, diff --git a/detector/library.go b/detector/library.go index e6ef3f25a2..64ef7e18e8 100644 --- a/detector/library.go +++ b/detector/library.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "path/filepath" "strings" "time" @@ -19,6 +20,7 @@ import ( ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" + "github.com/google/go-containerregistry/pkg/name" "github.com/samber/lo" "golang.org/x/xerrors" @@ -47,7 +49,7 @@ func DetectLibsCves(r *models.ScanResult, trivyOpts config.TrivyOpts, logOpts lo if err := downloadDB("", trivyOpts, noProgress, false); err != nil { return xerrors.Errorf("Failed to download trivy DB. err: %w", err) } - if err := trivydb.Init(trivyOpts.TrivyCacheDBDir); err != nil { + if err := trivydb.Init(filepath.Join(trivyOpts.TrivyCacheDBDir, "db")); err != nil { return xerrors.Errorf("Failed to init trivy DB. err: %w", err) } defer trivydb.Close() @@ -94,17 +96,41 @@ func DetectLibsCves(r *models.ScanResult, trivyOpts config.TrivyOpts, logOpts lo } func downloadDB(appVersion string, trivyOpts config.TrivyOpts, noProgress, skipUpdate bool) error { - client := db.NewClient(trivyOpts.TrivyCacheDBDir, noProgress) + refs := make([]name.Reference, 0, len(trivyOpts.TrivyDBRepositories)) + for _, repo := range trivyOpts.TrivyDBRepositories { + ref, err := func() (name.Reference, error) { + ref, err := name.ParseReference(repo, name.WithDefaultTag("")) + if err != nil { + return nil, err + } + + // Add the schema version if the tag is not specified for backward compatibility. + t, ok := ref.(name.Tag) + if !ok || t.TagStr() != "" { + return ref, nil + } + + ref = t.Tag(fmt.Sprint(trivydb.SchemaVersion)) + logging.Log.Infof("Adding schema version to the DB repository for backward compatibility. repository: %s", ref.String()) + + return ref, nil + }() + if err != nil { + return xerrors.Errorf("invalid db repository: %w", err) + } + refs = append(refs, ref) + } + client := db.NewClient(filepath.Join(trivyOpts.TrivyCacheDBDir, "db"), noProgress, db.WithDBRepository(refs)) ctx := context.Background() - needsUpdate, err := client.NeedsUpdate(context.TODO(), appVersion, skipUpdate) + needsUpdate, err := client.NeedsUpdate(ctx, appVersion, skipUpdate) if err != nil { - return xerrors.Errorf("database error: %w", err) + return xerrors.Errorf("Failed to check NeedsUpdate. err: %w", err) } if needsUpdate { logging.Log.Info("Need to update DB") - logging.Log.Info("Downloading DB...") - if err := client.Download(ctx, trivyOpts.TrivyCacheDBDir, ftypes.RegistryOptions{}); err != nil { + logging.Log.Infof("Downloading DB from %s...", strings.Join(trivyOpts.TrivyDBRepositories, ", ")) + if err := client.Download(ctx, filepath.Join(trivyOpts.TrivyCacheDBDir, "db"), ftypes.RegistryOptions{}); err != nil { return xerrors.Errorf("Failed to download vulnerability DB. err: %w", err) } } @@ -117,7 +143,7 @@ func downloadDB(appVersion string, trivyOpts config.TrivyOpts, noProgress, skipU } func showDBInfo(cacheDir string) error { - m := metadata.NewClient(cacheDir) + m := metadata.NewClient(filepath.Join(cacheDir, "db")) meta, err := m.Get() if err != nil { return xerrors.Errorf("Failed to get DB metadata. err: %w", err) diff --git a/go.mod b/go.mod index 2686b3b8a9..47604abc7b 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 github.com/emersion/go-smtp v0.21.3 github.com/google/go-cmp v0.6.0 + github.com/google/go-containerregistry v0.20.2 github.com/google/subcommands v1.2.0 github.com/google/uuid v1.6.0 github.com/gosnmp/gosnmp v1.38.0 @@ -182,7 +183,6 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect - github.com/google/go-containerregistry v0.20.2 // indirect github.com/google/go-github/v62 v62.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect diff --git a/subcmds/report.go b/subcmds/report.go index 5e2d35567e..6f025f6e99 100644 --- a/subcmds/report.go +++ b/subcmds/report.go @@ -5,11 +5,13 @@ package subcmds import ( "context" "flag" + "fmt" "os" "path/filepath" "github.com/aquasecurity/trivy/pkg/cache" - trivyJavaDb "github.com/aquasecurity/trivy/pkg/javadb" + trivydb "github.com/aquasecurity/trivy/pkg/db" + trivyjavadb "github.com/aquasecurity/trivy/pkg/javadb" "github.com/google/subcommands" "github.com/k0kubun/pp" "golang.org/x/xerrors" @@ -96,8 +98,9 @@ func (*ReportCmd) Usage() string { [-pipe] [-http="http://vuls-report-server"] [-trivy-cachedb-dir=/path/to/dir] - [-trivy-java-db-repository="OCI-repository-for-trivy-java-db"] - [-trivy-skip-java-db-update] + [-trivy-db-repository="OCI-repository-for-trivy-db"] + [-trivy-java-db-repository="OCI-repository-for-trivy-java-db"] + [-trivy-skip-java-db-update] [RFC3339 datetime format under results dir] ` @@ -179,10 +182,16 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) { f.StringVar(&config.Conf.TrivyCacheDBDir, "trivy-cachedb-dir", cache.DefaultDir(), "/path/to/dir") - f.StringVar(&config.Conf.TrivyJavaDBRepository, "trivy-java-db-repository", - trivyJavaDb.DefaultGHCRRepository, "Trivy Java DB Repository") - f.BoolVar(&config.Conf.TrivySkipJavaDBUpdate, "trivy-skip-java-db-update", - false, "Skip Trivy Java DB Update") + + config.Conf.TrivyOpts.TrivyDBRepositories = []string{trivydb.DefaultGHCRRepository} + dbRepos := stringArrayFlag{target: &config.Conf.TrivyOpts.TrivyDBRepositories} + f.Var(&dbRepos, "trivy-db-repository", fmt.Sprintf("Trivy DB Repository in a comma-separated list (default %s)", trivydb.DefaultGHCRRepository)) + + config.Conf.TrivyOpts.TrivyJavaDBRepositories = []string{trivyjavadb.DefaultGHCRRepository} + javaDBRepos := stringArrayFlag{target: &config.Conf.TrivyOpts.TrivyJavaDBRepositories} + f.Var(&javaDBRepos, "trivy-java-db-repository", fmt.Sprintf("Trivy Java DB Repository in a comma-separated list (default %s)", trivyjavadb.DefaultGHCRRepository)) + + f.BoolVar(&config.Conf.TrivySkipJavaDBUpdate, "trivy-skip-java-db-update", false, "Skip Trivy Java DB Update") } // Execute execute diff --git a/subcmds/string_array_flag.go b/subcmds/string_array_flag.go new file mode 100644 index 0000000000..9ea3c06d60 --- /dev/null +++ b/subcmds/string_array_flag.go @@ -0,0 +1,19 @@ +package subcmds + +import "strings" + +type stringArrayFlag struct { + target *[]string +} + +func (f *stringArrayFlag) String() string { + if f.target == nil { + return "" + } + return strings.Join(*f.target, ",") +} + +func (f *stringArrayFlag) Set(value string) error { + *f.target = strings.Split(value, ",") + return nil +} diff --git a/subcmds/tui.go b/subcmds/tui.go index 380e82ffef..4c3d64cd2d 100644 --- a/subcmds/tui.go +++ b/subcmds/tui.go @@ -6,10 +6,13 @@ package subcmds import ( "context" "flag" + "fmt" "os" "path/filepath" "github.com/aquasecurity/trivy/pkg/cache" + trivydb "github.com/aquasecurity/trivy/pkg/db" + trivyjavadb "github.com/aquasecurity/trivy/pkg/javadb" "github.com/google/subcommands" "github.com/future-architect/vuls/config" @@ -53,6 +56,9 @@ func (*TuiCmd) Usage() string { [-no-progress] [-pipe] [-trivy-cachedb-dir=/path/to/dir] + [-trivy-db-repository="OCI-repository-for-trivy-db"] + [-trivy-java-db-repository="OCI-repository-for-trivy-java-db"] + [-trivy-skip-java-db-update] ` } @@ -105,6 +111,16 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) { f.StringVar(&config.Conf.TrivyCacheDBDir, "trivy-cachedb-dir", cache.DefaultDir(), "/path/to/dir") + + config.Conf.TrivyOpts.TrivyDBRepositories = []string{trivydb.DefaultGHCRRepository} + dbRepos := stringArrayFlag{target: &config.Conf.TrivyOpts.TrivyDBRepositories} + f.Var(&dbRepos, "trivy-db-repository", fmt.Sprintf("Trivy DB Repository in a comma-separated list (default %s)", trivydb.DefaultGHCRRepository)) + + config.Conf.TrivyOpts.TrivyJavaDBRepositories = []string{trivyjavadb.DefaultGHCRRepository} + javaDBRepos := stringArrayFlag{target: &config.Conf.TrivyOpts.TrivyJavaDBRepositories} + f.Var(&javaDBRepos, "trivy-java-db-repository", fmt.Sprintf("Trivy Java DB Repository in a comma-separated list (default %s)", trivyjavadb.DefaultGHCRRepository)) + + f.BoolVar(&config.Conf.TrivySkipJavaDBUpdate, "trivy-skip-java-db-update", false, "Skip Trivy Java DB Update") } // Execute execute