diff --git a/Makefile b/Makefile index 193bb5117d0..b7b90079fce 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ bootstrap: ## Download and install all go dependencies (+ prep tooling in the ./ [ -f "$(TEMPDIR)/goreleaser" ] || curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh -s -- -b $(TEMPDIR)/ v0.140.0 .PHONY: static-analysis -static-analysis: lint check-licenses +static-analysis: lint check-licenses validate-schema .PHONY: lint lint: ## Run gofmt + golangci lint checks @@ -95,6 +95,11 @@ lint: ## Run gofmt + golangci lint checks $(eval MALFORMED_FILENAMES := $(shell find . | grep -e ':')) @bash -c "[[ '$(MALFORMED_FILENAMES)' == '' ]] || (printf '\nfound unsupported filename characters:\n$(MALFORMED_FILENAMES)\n\n' && false)" +.PHONY: validate-schema +validate-schema: + # ensure the codebase is only referencing a single grype-db schema version, multiple is not allowed + python test/validate_schema.py + lint-fix: ## Auto-format all source code + run golangci lint fixers $(call title,Running lint fixers) gofmt -w -s . diff --git a/cmd/db_check.go b/cmd/db_check.go index 276b66b64d1..9cab97740c6 100644 --- a/cmd/db_check.go +++ b/cmd/db_check.go @@ -25,11 +25,7 @@ func init() { } func runDbCheckCmd(_ *cobra.Command, _ []string) int { - dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig()) - if err != nil { - log.Errorf("could not curate database: %+v", err) - return 1 - } + dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig()) updateAvailable, _, err := dbCurator.IsUpdateAvailable() if err != nil { diff --git a/cmd/db_delete.go b/cmd/db_delete.go index 9ff81974daf..abab9c1e42f 100644 --- a/cmd/db_delete.go +++ b/cmd/db_delete.go @@ -25,14 +25,9 @@ func init() { } func runDbDeleteCmd(_ *cobra.Command, _ []string) int { - dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig()) - if err != nil { - log.Errorf("could not curate database: %w", err) - return 1 - } + dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig()) - err = dbCurator.Delete() - if err != nil { + if err := dbCurator.Delete(); err != nil { log.Errorf("unable to delete vulnerability database: %+v", err) return 1 } diff --git a/cmd/db_import.go b/cmd/db_import.go index 02fa5477778..14541aeb90e 100644 --- a/cmd/db_import.go +++ b/cmd/db_import.go @@ -25,14 +25,9 @@ func init() { } func runDbImportCmd(_ *cobra.Command, args []string) int { - dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig()) - if err != nil { - log.Errorf("could not curate database: %w", err) - return 1 - } + dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig()) - err = dbCurator.ImportFrom(args[0]) - if err != nil { + if err := dbCurator.ImportFrom(args[0]); err != nil { log.Errorf("unable to import vulnerability database: %+v", err) return 1 } diff --git a/cmd/db_status.go b/cmd/db_status.go index 42c92e0fdd7..fa3efe98933 100644 --- a/cmd/db_status.go +++ b/cmd/db_status.go @@ -15,7 +15,11 @@ var statusCmd = &cobra.Command{ Use: "status", Short: "display database status", Run: func(cmd *cobra.Command, args []string) { - os.Exit(runDbStatusCmd(cmd, args)) + err := runDbStatusCmd(cmd, args) + if err != nil { + log.Errorf(err.Error()) + os.Exit(1) + } }, } @@ -26,30 +30,25 @@ func init() { dbCmd.AddCommand(statusCmd) } -func runDbStatusCmd(_ *cobra.Command, _ []string) int { - dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig()) - if err != nil { - log.Errorf("could not curate database: %w", err) - return 1 - } - +func runDbStatusCmd(_ *cobra.Command, _ []string) error { + dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig()) status := dbCurator.Status() if showSupportedDbSchema { - // Note: the output for this option cannot change as it supports the nightly DB generation job - fmt.Println(status.RequiredSchemeVersion) - return 0 + // note: the output for this option cannot change as it supports the nightly DB generation job + fmt.Println(status.RequiredSchemaVersion) + return nil + } + + if status.Err != nil { + return status.Err } fmt.Println("Location: ", status.Location) fmt.Println("Built: ", status.Age.String()) fmt.Println("Current DB Version: ", status.CurrentSchemaVersion) - fmt.Println("Require DB Version: ", status.RequiredSchemeVersion) - if status.Err != nil { - fmt.Printf("Status: INVALID [%+v]\n", status.Err) - } else { - fmt.Println("Status: Valid") - } + fmt.Println("Require DB Version: ", status.RequiredSchemaVersion) + fmt.Println("Status: Valid") - return 0 + return nil } diff --git a/cmd/db_update.go b/cmd/db_update.go index d279fb94800..12f4e8f32ef 100644 --- a/cmd/db_update.go +++ b/cmd/db_update.go @@ -25,11 +25,7 @@ func init() { } func runDbUpdateCmd(_ *cobra.Command, _ []string) int { - dbCurator, err := db.NewCurator(appConfig.Db.ToCuratorConfig()) - if err != nil { - log.Errorf("could not curate database: %+v", err) - return 1 - } + dbCurator := db.NewCurator(appConfig.Db.ToCuratorConfig()) updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable() if err != nil { diff --git a/grype/db/curator.go b/grype/db/curator.go index 7e9e7bc39c1..0ddd5ff6933 100644 --- a/grype/db/curator.go +++ b/grype/db/curator.go @@ -30,13 +30,13 @@ type Curator struct { targetSchema int } -func NewCurator(cfg Config) (Curator, error) { +func NewCurator(cfg Config) Curator { return Curator{ config: cfg, fs: afero.NewOsFs(), targetSchema: v1.SchemaVersion, client: &file.HashiGoGetter{}, - }, nil + } } func (c *Curator) GetStore() (v1.VulnerabilityStoreReader, error) { @@ -54,22 +54,24 @@ func (c *Curator) GetStore() (v1.VulnerabilityStoreReader, error) { func (c *Curator) Status() Status { metadata, err := curation.NewMetadataFromDir(c.fs, c.config.DbDir) if err != nil { - err = fmt.Errorf("failed to parse database metadata (%s): %w", c.config.DbDir, err) + return Status{ + RequiredSchemaVersion: c.targetSchema, + Err: fmt.Errorf("failed to parse database metadata (%s): %w", c.config.DbDir, err), + } } if metadata == nil { - err = fmt.Errorf("database metadata not found at %q", c.config.DbDir) - } - - if err == nil { - err = c.Validate() + return Status{ + RequiredSchemaVersion: c.targetSchema, + Err: fmt.Errorf("database metadata not found at %q", c.config.DbDir), + } } return Status{ Age: metadata.Built, CurrentSchemaVersion: metadata.Version, - RequiredSchemeVersion: v1.SchemaVersion, + RequiredSchemaVersion: c.targetSchema, Location: c.config.DbDir, - Err: err, + Err: c.Validate(), } } diff --git a/grype/db/curator_test.go b/grype/db/curator_test.go index c61681533c1..5aba79de25b 100644 --- a/grype/db/curator_test.go +++ b/grype/db/curator_test.go @@ -53,18 +53,15 @@ func (g *testGetter) GetToDir(dst, src string) error { return afero.WriteFile(g.fs, dst, []byte(g.dir[src]), 0755) } -func newTestCurator(fs afero.Fs, getter file.Getter, dbDir, metadataUrl string) (Curator, error) { - c, err := NewCurator(Config{ +func newTestCurator(fs afero.Fs, getter file.Getter, dbDir, metadataUrl string) Curator { + c := NewCurator(Config{ DbDir: dbDir, ListingURL: metadataUrl, }) - if err != nil { - return Curator{}, err - } c.client = getter c.fs = fs - return c, err + return c } func TestCuratorDownload(t *testing.T) { @@ -95,10 +92,7 @@ func TestCuratorDownload(t *testing.T) { } fs := afero.NewMemMapFs() getter := newTestGetter(fs, files, dirs) - cur, err := newTestCurator(fs, getter, "/tmp/dbdir", metadataUrl) - if err != nil { - t.Fatalf("failed making curator: %+v", err) - } + cur := newTestCurator(fs, getter, "/tmp/dbdir", metadataUrl) path, err := cur.download(test.entry) @@ -163,14 +157,11 @@ func TestCuratorValidate(t *testing.T) { fs := afero.NewOsFs() getter := newTestGetter(fs, nil, nil) - cur, err := newTestCurator(fs, getter, "/tmp/dbdir", metadataUrl) - if err != nil { - t.Fatalf("failed making curator: %+v", err) - } + cur := newTestCurator(fs, getter, "/tmp/dbdir", metadataUrl) cur.targetSchema = test.constraint - err = cur.validate(test.fixture) + err := cur.validate(test.fixture) if err == nil && test.err { t.Errorf("expected an error but got none") diff --git a/grype/db/status.go b/grype/db/status.go index c29d934c27e..8b9f2e03ec0 100644 --- a/grype/db/status.go +++ b/grype/db/status.go @@ -5,7 +5,7 @@ import "time" type Status struct { Age time.Time CurrentSchemaVersion int - RequiredSchemeVersion int + RequiredSchemaVersion int Location string Err error } diff --git a/grype/lib.go b/grype/lib.go index f66a77dc905..bb668ea189e 100644 --- a/grype/lib.go +++ b/grype/lib.go @@ -44,10 +44,7 @@ func FindVulnerabilitiesForPackage(provider vulnerability.Provider, d distro.Dis } func LoadVulnerabilityDb(cfg db.Config, update bool) (vulnerability.Provider, error) { - dbCurator, err := db.NewCurator(cfg) - if err != nil { - return nil, fmt.Errorf("could not curate database: %w", err) - } + dbCurator := db.NewCurator(cfg) if update { updateAvailable, updateEntry, err := dbCurator.IsUpdateAvailable() diff --git a/test/inline-compare/compare.py b/test/inline-compare/compare.py index c48a981f8d5..b79e3468abb 100755 --- a/test/inline-compare/compare.py +++ b/test/inline-compare/compare.py @@ -49,7 +49,7 @@ def vulnerabilities(self): return vulnerabilities, metadata -class grype: +class Grype: report_tmpl = "{image}.json" @@ -89,7 +89,7 @@ def main(image): inline = InlineScan(image=image, report_dir="inline-reports") inline_vulnerabilities, inline_metadata = inline.vulnerabilities() - grype = grype(image=image, report_dir="grype-reports") + grype = Grype(image=image, report_dir="grype-reports") grype_vulnerabilities, grype_metadata = grype.vulnerabilities() if len(grype_vulnerabilities) == 0 and len(inline_vulnerabilities) == 0: diff --git a/test/validate_schema.py b/test/validate_schema.py new file mode 100644 index 00000000000..285ecce50db --- /dev/null +++ b/test/validate_schema.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python2 +import re +import os +import sys + +pattern = re.compile(r'github.com/anchore/grype-db/pkg/db/v(?P\d+)') + +def main(): + schema_versions_found = set() + + for root, dirs, files in os.walk("."): + for file in files: + if not file.endswith(".go"): + continue + with open(os.path.join(root, file)) as f: + for match in pattern.findall(f.read(), re.MULTILINE): + schema_versions_found.add(match) + + num_schemas = len(schema_versions_found) + if num_schemas != 1: + sys.exit("Found multiple schemas: %s" % repr(schema_versions_found)) + + try: + for x in schema_versions_found: + int(x) + except Exception: + sys.exit("Non-numeric schema found: %s" % repr(schema_versions_found)) + + print("Schemas Found: %s" % repr(schema_versions_found)) + +if __name__ == "__main__": + main()