internal/worker: resolve panic in govulncheck pipeline

A previous refactoring would return a pointer response object instead of
a list. We forgot to handle the case where response is nil. This lead to
some panics.

Change-Id: I8501940b972a683461e030bec0878b3f3a29d966
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/590238
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Maceo Thompson <maceothompson@google.com>
diff --git a/internal/worker/govulncheck_scan.go b/internal/worker/govulncheck_scan.go
index a040072..cd7c48a 100644
--- a/internal/worker/govulncheck_scan.go
+++ b/internal/worker/govulncheck_scan.go
@@ -363,9 +363,7 @@
 func (s *scanner) CheckModule(ctx context.Context, w http.ResponseWriter, sreq *govulncheck.Request, baseRow *govulncheck.Result) (*govulncheck.WorkState, error) {
 	log.Infof(ctx, "running scanner.runScanModule: %s@%s", sreq.Path(), sreq.Version)
 	response, err := s.runScanModule(ctx, sreq.Module, baseRow.Version, sreq.Mode)
-	baseRow.ScanSeconds = response.Stats.ScanSeconds
-	baseRow.ScanMemory = int64(response.Stats.ScanMemory)
-	// classify scan error
+	// classify scan error first
 	if err != nil {
 		switch {
 		case isGovulncheckLoadError(err) || isBuildIssue(err):
@@ -399,25 +397,29 @@
 		default:
 			err = fmt.Errorf("%v: %w", err, derrors.ScanModuleGovulncheckError)
 		}
-		baseRow.AddError(err)
 	}
 
-	// create a row for each precision level
+	// create a row for each precision level, error or not
 	var rows []bigquery.Row
 	for _, sm := range []string{scanModeSourceSymbol, scanModeSourcePackage, scanModeSourceModule} {
 		row := *baseRow
 		row.ScanMode = sm
 
-		// We use govulncheck command execution time as the approx. time for symbol level analysis.
-		// We currently don't have a way of approximating time for measuring time for module and
-		// package level scans. We could run govulncheck with -scan package and -scan module, but
-		// that would put more pressure on the pipeline and use more resources.
-		if sm != ModeGovulncheck {
-			row.ScanSeconds = 0
-			row.ScanMemory = 0
+		if err != nil {
+			row.AddError(err)
+			log.Infof(ctx, "scanner.runScanModule returned err=%v for %s in scan mode=%s", err, sreq.Path(), sm)
+		} else {
+			// We use govulncheck command execution time as the approx. time for symbol level analysis.
+			// We currently don't have a way of approximating time for measuring time for module and
+			// package level scans. We could run govulncheck with -scan package and -scan module, but
+			// that would put more pressure on the pipeline and use more resources.
+			if sm == ModeGovulncheck {
+				row.ScanSeconds = response.Stats.ScanSeconds
+				row.ScanMemory = int64(response.Stats.ScanMemory)
+			}
+			row.Vulns = vulnsForScanMode(response, sm)
+			log.Infof(ctx, "scanner.runScanModule returned %d findings for %s with row.Vulns=%d in scan mode=%s", len(response.Findings), sreq.Path(), len(row.Vulns), sm)
 		}
-		row.Vulns = vulnsForScanMode(response, sm)
-		log.Infof(ctx, "scanner.runScanModule returned %d findings and err=%v for %s with row.Vulns=%d in scan mode=%s", len(response.Findings), err, sreq.Path(), len(row.Vulns), sm)
 		rows = append(rows, &row)
 	}