From b1b25f4f67c341e7625adb6f0f0ce818f00f81ec Mon Sep 17 00:00:00 2001 From: Colin Douglas <16968564+cmdpdx@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:03:22 -0700 Subject: [PATCH] rumble: refactor to use vuln scan results from `prod-enforce` (#1823) ## Type of change Late breaking 24i issue. ### What should this PR do? Vuln scanning has moved from `prod-images` to `prod-enforce`. The CVE comparison pages on edu.chainguard.dev have been broken for a while since the scanners have been turned down from `prod-images`. This PR updates the location of the datasets used, as well as refactors to take into consideration some of the changes to the data. ### Why are we making this change? Fix CVE comparisons between external and Chainguard equivalent images. ### What are the acceptance criteria? * CVE data is pulled from the correct bigquery source (`prod-enforce`), and we have data for both external and Chainguard images for each requested. ### How should this PR be tested? This change chan be run locally without uploading the results to test the datasources are being queried and correlated properly: ``` go run main.go vulns --project=prod-enforce-fabc --db=cloudevents_grype_scan_results go run main.go image-csv --project=prod-enforce-fabc --db=cloudevents_grype_scan_results --rumble-json-path=../../data/rumble.json go run main.go legacy-csv --project=prod-enforce-fabc --db=cloudevents_grype_scan_results ``` Signed-off-by: Colin Douglas --- .../workflows/rumble-vulnerability-data.yaml | 14 +-- data/rumble.json | 2 +- tools/rumble/cmd/image_csvs.go | 86 ++++++++++--------- tools/rumble/cmd/legacy_csv.go | 56 +++--------- tools/rumble/cmd/options.go | 48 +++++++++++ tools/rumble/cmd/vulns.go | 67 ++++++--------- tools/rumble/go.mod | 2 +- tools/rumble/pkg/bigquery/bigquery.go | 4 +- tools/rumble/pkg/bigquery/queries.go | 67 +++++++-------- tools/rumble/pkg/cloudstorage/cloudstorage.go | 7 +- 10 files changed, 174 insertions(+), 179 deletions(-) diff --git a/.github/workflows/rumble-vulnerability-data.yaml b/.github/workflows/rumble-vulnerability-data.yaml index efd68fb2d3..4570796f68 100644 --- a/.github/workflows/rumble-vulnerability-data.yaml +++ b/.github/workflows/rumble-vulnerability-data.yaml @@ -36,7 +36,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # actions/setup-go@v4 with: - go-version: '1.22' + go-version-file: go.mod check-latest: true - name: Authenticate to Google Cloud @@ -49,8 +49,8 @@ jobs: - name: Generate vulnerability JSON files run: | go run main.go vulns \ - --project prod-images-c6e5 \ - --db insights_ds \ + --project prod-enforce-fabc \ + --db cloudevents_grype_scan_results \ --gcs-project chainguard-academy \ --bucket chainguard-academy \ --upload @@ -58,8 +58,8 @@ jobs: - name: Generate image comparison CSVs run: | go run main.go image-csv \ - --project prod-images-c6e5 \ - --db insights_ds \ + --project prod-enforce-fabc \ + --db cloudevents_grype_scan_results \ --gcs-project chainguard-academy \ --bucket chainguard-academy \ --rumble-json-path ../../data/rumble.json \ @@ -68,8 +68,8 @@ jobs: - name: Generate legacy comparison CSV run: | go run main.go legacy-csv \ - --project prod-images-c6e5 \ - --db insights_ds \ + --project prod-enforce-fabc \ + --db cloudevents_grype_scan_results \ --gcs-project chainguard-academy \ --bucket chainguard-academy \ --upload diff --git a/data/rumble.json b/data/rumble.json index d18f345971..3a35bf5d89 100644 --- a/data/rumble.json +++ b/data/rumble.json @@ -3,7 +3,7 @@ {"image":"busybox","left":"busybox:latest","right":"cgr.dev/chainguard/busybox:latest"}, {"image":"cassandra","left":"cassandra:latest","right":"cgr.dev/chainguard/cassandra:latest"}, {"image":"curl","left":"curlimages/curl:latest","right":"cgr.dev/chainguard/curl:latest"}, - {"image":"deno","left":"deno:latest","right":"cgr.dev/chainguard/deno:latest"}, + {"image":"deno","left":"denoland/deno:latest","right":"cgr.dev/chainguard/deno:latest"}, {"image":"dotnet-runtime","left":"mcr.microsoft.com/dotnet/runtime:latest","right":"cgr.dev/chainguard/dotnet-runtime:latest"}, {"image":"dotnet-sdk","left":"mcr.microsoft.com/dotnet/sdk:latest","right":"cgr.dev/chainguard/dotnet-sdk:latest"}, {"image":"dex","left":"dexidp/dex:latest","right":"cgr.dev/chainguard/dex:latest"}, diff --git a/tools/rumble/cmd/image_csvs.go b/tools/rumble/cmd/image_csvs.go index 5184b160db..a883f37569 100644 --- a/tools/rumble/cmd/image_csvs.go +++ b/tools/rumble/cmd/image_csvs.go @@ -9,7 +9,6 @@ SPDX-License-Identifier: Apache-2.0 package cmd import ( - "context" "encoding/csv" "encoding/json" "fmt" @@ -19,7 +18,6 @@ import ( "cloud.google.com/go/bigquery" cgbigquery "github.com/chainguard-dev/edu/tools/rumble/pkg/bigquery" - cloudstorage "github.com/chainguard-dev/edu/tools/rumble/pkg/cloudstorage" "github.com/spf13/cobra" ) @@ -30,10 +28,8 @@ type rumbleJson []struct { } type imageCsv struct { - ctx context.Context - bqClient cgbigquery.BqClient - storageClient cloudstorage.GcsClient - opts *options + rumbleBase + rumbleJsonPath string comparisons *rumbleJson } @@ -45,20 +41,10 @@ func cmdImageCsvs(o *options) *cobra.Command { Use: "image-csv", Short: "CSV for a single external/third party image and a Chainguard image", RunE: func(cmd *cobra.Command, args []string) error { - db, _ := cmd.Flags().GetString("db") - project, _ := cmd.Flags().GetString("project") - gcsProject, _ := cmd.Flags().GetString("gcs-project") - bucket, _ := cmd.Flags().GetString("bucket") - up, _ := cmd.Flags().GetBool("upload") - i := imageCsv{ - ctx: cmd.Context(), - opts: &options{ - dbProject: project, - storageProject: gcsProject, - db: db, - storageBucket: bucket, - upload: up, + rumbleBase: rumbleBase{ + ctx: cmd.Context(), + opts: o, }, rumbleJsonPath: rumbleJsonPath, } @@ -71,21 +57,6 @@ func cmdImageCsvs(o *options) *cobra.Command { return cmd } -func (i *imageCsv) setupClients() error { - var err error - - i.bqClient, err = cgbigquery.NewBqClient(i.opts.dbProject, i.opts.db) - if err != nil { - log.Fatalf("error initializing bq client: %v", err) - } - - i.storageClient, err = cloudstorage.NewGcsClient(i.ctx, i.opts.storageBucket) - if err != nil { - log.Fatalf("error initializing gcs client: %v", err) - } - return nil -} - func (i *imageCsv) parseRumbleJson() error { f, err := os.ReadFile(i.rumbleJsonPath) if err != nil { @@ -99,6 +70,16 @@ func (i *imageCsv) parseRumbleJson() error { return nil } +// splitRepoTag splits an image references like foo:bar into +// the repo and tag components. If a ref doesn't have a tag, +// latest is assumed. +func splitRepoTag(ref string) (string, string) { + if repo, tag, ok := strings.Cut(ref, ":"); ok { + return repo, tag + } + return ref, "latest" +} + func (i *imageCsv) generateCsvs() error { if i.rumbleJsonPath == "" { return fmt.Errorf("missing --theirs or --ours argument") @@ -108,14 +89,19 @@ func (i *imageCsv) generateCsvs() error { return err } - if err := i.setupClients(); err != nil { + closer, err := i.setupClients() + if err != nil { return err } - defer i.bqClient.Client.Close() - defer i.storageClient.Client.Close() + defer closer() for _, img := range *i.comparisons { + log.Printf("querying %v", img) + q := i.bqClient.Client.Query(cgbigquery.ImageComparisonCsvQuery) + theirRepo, theirTag := splitRepoTag(img.Theirs) + ourRepo, ourTag := splitRepoTag(img.Ours) + q.DefaultProjectID = i.bqClient.Client.Project() q.Parameters = []bigquery.QueryParameter{ { @@ -124,7 +110,16 @@ func (i *imageCsv) generateCsvs() error { Type: bigquery.StandardSQLDataType{ TypeKind: "STRING", }, - Value: img.Theirs, + Value: theirRepo, + }, + }, + { + Name: "their_tag", + Value: &bigquery.QueryParameterValue{ + Type: bigquery.StandardSQLDataType{ + TypeKind: "STRING", + }, + Value: theirTag, }, }, { @@ -133,7 +128,16 @@ func (i *imageCsv) generateCsvs() error { Type: bigquery.StandardSQLDataType{ TypeKind: "STRING", }, - Value: img.Ours, + Value: ourRepo, + }, + }, + { + Name: "our_tag", + Value: &bigquery.QueryParameterValue{ + Type: bigquery.StandardSQLDataType{ + TypeKind: "STRING", + }, + Value: ourTag, }, }, } @@ -153,7 +157,7 @@ func (i *imageCsv) generateCsvs() error { } defer gcsCsvWriter.Close() w = csv.NewWriter(gcsCsvWriter) - fmt.Printf("Writing %s to GCS bucket\n", fName) + log.Printf("Writing %s to GCS bucket", fName) default: f, err := os.Create(fName) if err != nil { @@ -161,7 +165,7 @@ func (i *imageCsv) generateCsvs() error { } defer f.Close() w = csv.NewWriter(f) - fmt.Printf("Writing to %s\n", f.Name()) + log.Printf("Writing to %s", f.Name()) } err = w.Write(strings.Split(cgbigquery.ImageScanCsvHeader, ",")) diff --git a/tools/rumble/cmd/legacy_csv.go b/tools/rumble/cmd/legacy_csv.go index 065fe947a7..586c1afdae 100644 --- a/tools/rumble/cmd/legacy_csv.go +++ b/tools/rumble/cmd/legacy_csv.go @@ -8,7 +8,6 @@ SPDX-License-Identifier: Apache-2.0 package cmd import ( - "context" "encoding/csv" "fmt" "log" @@ -17,37 +16,17 @@ import ( "strings" cgbigquery "github.com/chainguard-dev/edu/tools/rumble/pkg/bigquery" - cloudstorage "github.com/chainguard-dev/edu/tools/rumble/pkg/cloudstorage" "github.com/spf13/cobra" ) -type legacyCsv struct { - ctx context.Context - bqClient cgbigquery.BqClient - storageClient cloudstorage.GcsClient - opts *options -} - func cmdLegacyCsv(o *options) *cobra.Command { cmd := &cobra.Command{ Use: "legacy-csv", Short: "Single CSV with all scanned images and vulns", RunE: func(cmd *cobra.Command, args []string) error { - db, _ := cmd.Flags().GetString("db") - project, _ := cmd.Flags().GetString("project") - gcsProject, _ := cmd.Flags().GetString("gcs-project") - bucket, _ := cmd.Flags().GetString("bucket") - up, _ := cmd.Flags().GetBool("upload") - - l := legacyCsv{ - ctx: cmd.Context(), - opts: &options{ - dbProject: project, - storageProject: gcsProject, - db: db, - storageBucket: bucket, - upload: up, - }, + l := rumbleBase{ + ctx: cmd.Context(), + opts: o, } return l.generateCsv() }, @@ -55,40 +34,25 @@ func cmdLegacyCsv(o *options) *cobra.Command { return cmd } -func (l *legacyCsv) setupClients() error { - var err error - - l.bqClient, err = cgbigquery.NewBqClient(l.opts.dbProject, l.opts.db) - if err != nil { - log.Fatalf("error initializing bq client: %v", err) - } - - l.storageClient, err = cloudstorage.NewGcsClient(l.ctx, l.opts.storageBucket) +func (c *rumbleBase) generateCsv() error { + closer, err := c.setupClients() if err != nil { - log.Fatalf("error initializing gcs client: %v", err) - } - return nil -} - -func (l *legacyCsv) generateCsv() error { - if err := l.setupClients(); err != nil { return err } - defer l.bqClient.Client.Close() - defer l.storageClient.Client.Close() + defer closer() - q := l.bqClient.Client.Query(cgbigquery.LegacyCsvQuery) + q := c.bqClient.Client.Query(cgbigquery.LegacyCsvQuery) - rows, err := l.bqClient.Query(q, cgbigquery.LegacyScanQueryType) + rows, err := c.bqClient.Query(q, cgbigquery.LegacyScanQueryType) if err != nil { log.Fatalf("error fetching scan results: %v", err) } var w *csv.Writer - switch l.opts.upload { + switch c.opts.upload { case true: fName := "cve-data/data.csv" - gcsCsvWriter, err := l.storageClient.GetCsvWriter(fName) + gcsCsvWriter, err := c.storageClient.GetCsvWriter(fName) if err != nil { return err } diff --git a/tools/rumble/cmd/options.go b/tools/rumble/cmd/options.go index 9899aa58af..62dd32a971 100644 --- a/tools/rumble/cmd/options.go +++ b/tools/rumble/cmd/options.go @@ -1,5 +1,25 @@ +/* +Copyright 2024 Chainguard, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + package cmd +import ( + "context" + "log" + + cgbigquery "github.com/chainguard-dev/edu/tools/rumble/pkg/bigquery" + cloudstorage "github.com/chainguard-dev/edu/tools/rumble/pkg/cloudstorage" +) + +type rumbleBase struct { + ctx context.Context + bqClient cgbigquery.BqClient + storageClient cloudstorage.GcsClient + opts *options +} + type options struct { dbProject string storageProject string @@ -7,3 +27,31 @@ type options struct { storageBucket string upload bool } + +func (c *rumbleBase) setupClients() (func(), error) { + var err error + + c.bqClient, err = cgbigquery.NewBqClient(c.opts.dbProject, c.opts.db) + if err != nil { + log.Fatalf("error initializing bq client: %v", err) + } + + // Only instantiate gcs client if we're uploading + if c.opts.upload { + c.storageClient, err = cloudstorage.NewGcsClient(c.ctx, c.opts.storageBucket) + if err != nil { + log.Fatalf("error initializing gcs client: %v", err) + } + } + + return func() { + if err := c.bqClient.Client.Close(); err != nil { + log.Println(err) + } + if c.storageClient.Client != nil { + if err := c.storageClient.Client.Close(); err != nil { + log.Println(err) + } + } + }, nil +} diff --git a/tools/rumble/cmd/vulns.go b/tools/rumble/cmd/vulns.go index e103fc5a4d..fb1dd656bf 100644 --- a/tools/rumble/cmd/vulns.go +++ b/tools/rumble/cmd/vulns.go @@ -8,7 +8,6 @@ SPDX-License-Identifier: Apache-2.0 package cmd import ( - "context" "encoding/json" "fmt" "log" @@ -17,7 +16,6 @@ import ( "cloud.google.com/go/bigquery" cgbigquery "github.com/chainguard-dev/edu/tools/rumble/pkg/bigquery" - cloudstorage "github.com/chainguard-dev/edu/tools/rumble/pkg/cloudstorage" "github.com/chainguard-dev/edu/tools/rumble/pkg/grype" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" @@ -25,12 +23,10 @@ import ( ) type vulnsJson struct { - ctx context.Context - opts *options - bqClient cgbigquery.BqClient - storageClient cloudstorage.GcsClient - grypeDb grype.GrypeDB - vulns []grype.Vuln + rumbleBase + + grypeDb grype.GrypeDB + vulns []grype.Vuln } // vulnJsonCmd represents the vulnJson command @@ -39,20 +35,10 @@ func cmdVulns(o *options) *cobra.Command { Use: "vulns", Short: "JSON file per discovered CVE", RunE: func(cmd *cobra.Command, args []string) error { - db, _ := cmd.Flags().GetString("db") - project, _ := cmd.Flags().GetString("project") - gcsProject, _ := cmd.Flags().GetString("gcs-project") - bucket, _ := cmd.Flags().GetString("bucket") - up, _ := cmd.Flags().GetBool("upload") - v := &vulnsJson{ - ctx: cmd.Context(), - opts: &options{ - dbProject: project, - storageProject: gcsProject, - db: db, - storageBucket: bucket, - upload: up, + rumbleBase: rumbleBase{ + ctx: cmd.Context(), + opts: o, }, } return v.generateJSON() @@ -61,17 +47,10 @@ func cmdVulns(o *options) *cobra.Command { return cmd } -func (v *vulnsJson) setupClients() error { - var err error - - v.bqClient, err = cgbigquery.NewBqClient(v.opts.dbProject, v.opts.db) - if err != nil { - log.Fatalf("error initializing bq client: %v", err) - } - - v.storageClient, err = cloudstorage.NewGcsClient(v.ctx, v.opts.storageBucket) +func (v *vulnsJson) setupClients() (func(), error) { + closer, err := v.rumbleBase.setupClients() if err != nil { - log.Fatalf("error initializing gcs client: %v", err) + return closer, err } v.grypeDb, err = grype.NewGrypeClient() @@ -79,18 +58,20 @@ func (v *vulnsJson) setupClients() error { log.Fatalf("error initializing grype client: %v", err) } - return nil + return func() { + closer() + if err := v.grypeDb.DB.Close(); err != nil { + log.Println(err) + } + }, nil } func (v *vulnsJson) generateJSON() error { - var err error - - if err := v.setupClients(); err != nil { + closer, err := v.setupClients() + if err != nil { return err } - defer v.bqClient.Client.Close() - defer v.storageClient.Client.Close() - defer v.grypeDb.DB.Close() + defer closer() q := v.bqClient.Client.Query(cgbigquery.AllVulnsQuery) allVulnRecords, err := v.bqClient.Query(q, cgbigquery.CveQueryType) @@ -124,9 +105,9 @@ func (v *vulnsJson) generateJSON() error { return nil } -func (vs *vulnsJson) saveFiles(vulns []grype.Vuln) error { +func (v *vulnsJson) saveFiles(vulns []grype.Vuln) error { eg := new(errgroup.Group) - fmt.Printf("Found %d vulnerabilities\n", len(vulns)) + log.Printf("Found %d vulnerabilities", len(vulns)) eg.SetLimit(50) for _, v := range vulns { vulnerability := v @@ -141,7 +122,7 @@ func (vs *vulnsJson) saveFiles(vulns []grype.Vuln) error { return err } - fmt.Printf("Wrote %s\n", fName) + log.Printf("Wrote %s", fName) return nil }) } @@ -155,7 +136,7 @@ func (v *vulnsJson) queryAffectedImages() error { vulnerability := vuln i := idx eg.Go(func() error { - fmt.Printf("querying %v\n", vulnerability.Id) + log.Printf("querying %v", vulnerability.Id) q := v.bqClient.Client.Query(cgbigquery.AffectedImagesQuery) q.DefaultProjectID = v.bqClient.Client.Project() q.Parameters = []bigquery.QueryParameter{ @@ -204,7 +185,7 @@ func (v *vulnsJson) queryAffectedImages() error { } if err := eg.Wait(); err == nil { - fmt.Println("Successfully saved all vulnerabilities.") + log.Println("Successfully saved all vulnerabilities.") } return nil } diff --git a/tools/rumble/go.mod b/tools/rumble/go.mod index 9cbac1b2be..f2404b9bb0 100644 --- a/tools/rumble/go.mod +++ b/tools/rumble/go.mod @@ -1,6 +1,6 @@ module github.com/chainguard-dev/edu/tools/rumble -go 1.22 +go 1.23.1 require ( cloud.google.com/go/bigquery v1.62.0 diff --git a/tools/rumble/pkg/bigquery/bigquery.go b/tools/rumble/pkg/bigquery/bigquery.go index 771825b4da..eeda45be5b 100644 --- a/tools/rumble/pkg/bigquery/bigquery.go +++ b/tools/rumble/pkg/bigquery/bigquery.go @@ -72,7 +72,7 @@ func (b *BqClient) Query(q *bigquery.Query, queryType string) ([]interface{}, er q.DefaultProjectID = b.Client.Project() it, err := q.Read(b.Ctx) if err != nil { - return nil, fmt.Errorf("%v", err) + return nil, err } var records []interface{} @@ -91,7 +91,7 @@ func (b *BqClient) Query(q *bigquery.Query, queryType string) ([]interface{}, er break } if err != nil { - return nil, fmt.Errorf("%v", err) + return nil, fmt.Errorf("querying %s type: %w", queryType, err) } records = append(records, values) } diff --git a/tools/rumble/pkg/bigquery/queries.go b/tools/rumble/pkg/bigquery/queries.go index f667468f50..288b483c3a 100644 --- a/tools/rumble/pkg/bigquery/queries.go +++ b/tools/rumble/pkg/bigquery/queries.go @@ -6,29 +6,29 @@ SPDX-License-Identifier: Apache-2.0 package cgbigquery const ( - LegacyCsvHeader = `f0_,image,scanner,scanner_version,scanner_db_version,time,low_cve_cnt,med_cve_cnt,high_cve_cnt,crit_cve_cnt,unknown_cve_cnt,tot_cve_cnt,digest` - ImageScanCsvHeader = `image,package,vulnerability,version,type,s` + vulnsTable = "`cloudevents_grype_scan_results.dev_chainguard_image_scan_grype_vulns`" + summaryTable = "`cloudevents_grype_scan_results.dev_chainguard_image_scan_grype_summary`" - AllVulnsQuery = ` -SELECT DISTINCT vulnerability -FROM ` + "`" + `cloudevents_scan_recorder_grype.dev_scan-results_grype_vulns` + "`" + `` + LegacyCsvHeader = `f0_,image,scanner,scanner_version,scanner_db_version,time,low_cve_cnt,med_cve_cnt,high_cve_cnt,crit_cve_cnt,unknown_cve_cnt,tot_cve_cnt,digest` + ImageScanCsvHeader = `image,package,vulnerability,version,type,s` + AllVulnsQuery = ` +SELECT DISTINCT vulnerability +FROM ` + vulnsTable - AffectedImagesQuery = ` -SELECT s1.image, s1.time as time, -FROM ` + "`" + `cloudevents_scan_recorder_grype.dev_scan-results_grype_vulns` + "`" + ` -AS s2 -INNER JOIN ` + "`" + `cloudevents_scan_recorder_grype.dev_scan-results_grype_summary` + "`" + ` -AS s1 -ON s1.id = s2.scan_id -WHERE s2.vulnerability = @vulnerability -AND s1.image NOT LIKE "cgr.dev/chainguard-private/%" -AND s1.image NOT LIKE "cgr.dev/custom%" -GROUP BY s1.time, s1.image -ORDER BY s1.image, s1.time + AffectedImagesQuery = ` +SELECT summ.image, summ.time as time, +FROM ` + vulnsTable + ` AS vulns +INNER JOIN ` + summaryTable + ` AS scan +ON scan.id = vulns.scan_id +WHERE vulns.vulnerability = @vulnerability +AND scan.image NOT LIKE "cgr.dev/chainguard-private/%" +AND scan.image NOT LIKE "cgr.dev/custom%" +GROUP BY scan.time, scan.image +ORDER BY scan.image, scan.time ` - LegacyCsvQuery = ` + LegacyCsvQuery = ` SELECT ROW_NUMBER() OVER (ORDER BY time), image, @@ -43,29 +43,26 @@ SELECT unknown_cve_count as unknown_cve_cnt, low_cve_count + med_cve_count + high_cve_count + crit_cve_count + unknown_cve_count AS tot_cve_cnt, digest -FROM ` + "`" + `cloudevents_scan_recorder_grype.dev_scan-results_grype_summary` + "`" + ` +FROM ` + summaryTable + ` WHERE DATE(time) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) AND CURRENT_DATE() AND scanner = "grype" AND image NOT LIKE "cgr.dev/chainguard-private/%" AND image NOT LIKE "cgr.dev/custom%" ` - - ImageComparisonCsvQuery = ` + + ImageComparisonCsvQuery = ` WITH ruuuumble AS ( - SELECT s1.image, - s1.time as t, - s2.name as package, - s2.vulnerability, - s2.installed as version, - s2.type, - s2.severity - FROM ` + "`" + `cloudevents_scan_recorder_grype.dev_scan-results_grype_vulns` + "`" + ` - AS s2 - INNER JOIN ` + "`" + "cloudevents_scan_recorder_grype.dev_scan-results_grype_summary" + "`" + ` - AS s1 - ON s1.id = s2.scan_id - WHERE s1.image = @theirs - OR s1.image = @ours + SELECT scan.image, + scan.time as t, + vulns.name as package, + vulns.vulnerability, + vulns.installed as version, + vulns.type, + vulns.severity + FROM ` + vulnsTable + ` AS vulns + INNER JOIN ` + summaryTable + ` AS scan ON scan.id = vulns.scan_id + WHERE (scan.image = @theirs AND scan.tags = @their_tag) + OR (scan.image = @ours AND scan.tags = @our_tag) ) SELECT image, package, vulnerability, version, type, severity FROM ruuuumble WHERE DATE(t) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) AND CURRENT_DATE() diff --git a/tools/rumble/pkg/cloudstorage/cloudstorage.go b/tools/rumble/pkg/cloudstorage/cloudstorage.go index 1d972bb8eb..dcd3c0a265 100644 --- a/tools/rumble/pkg/cloudstorage/cloudstorage.go +++ b/tools/rumble/pkg/cloudstorage/cloudstorage.go @@ -9,6 +9,7 @@ import ( "context" "encoding/json" "fmt" + "log" "cloud.google.com/go/storage" "github.com/chainguard-dev/edu/tools/rumble/pkg/grype" @@ -35,7 +36,7 @@ func NewGcsClient(ctx context.Context, bucket string) (g GcsClient, err error) { // this should live over in vulns.go and be a vulnsJson method func (g *GcsClient) SaveVulnJSON(vulns []grype.Vuln) error { eg := new(errgroup.Group) - fmt.Printf("Found %d vulnerabilities\n", len(vulns)) + log.Printf("Found %d vulnerabilities", len(vulns)) eg.SetLimit(50) for _, v := range vulns { vulnerability := v @@ -66,13 +67,13 @@ func (g *GcsClient) SaveVulnJSON(vulns []grype.Vuln) error { return err } - fmt.Printf("Wrote %s\n", fName) + log.Printf("Wrote %s", fName) return nil }) } if err := eg.Wait(); err == nil { - fmt.Println("Successfully saved all vulnerabilities.") + log.Println("Successfully saved all vulnerabilities.") } return nil }