internal/worker: keep going on module scan failure

If a module can't be scanned (because, for example, its packages can't
be loaded), continue scanning other modules.

Change-Id: I0e79d4083fb4bd2a1f158b38593c81832a36c255
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/393836
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/worker/scan_modules.go b/internal/worker/scan_modules.go
index 79ca291..8d0981c 100644
--- a/internal/worker/scan_modules.go
+++ b/internal/worker/scan_modules.go
@@ -7,6 +7,7 @@
 import (
 	"archive/zip"
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -36,6 +37,18 @@
 	"golang.org/x/vuln", "golang.org/x/vulndb", "golang.org/x/website",
 }
 
+type scanError struct {
+	err error
+}
+
+func (s scanError) Error() string {
+	return s.err.Error()
+}
+
+func (s scanError) Unwrap() error {
+	return s.err
+}
+
 // ScanModules scans a list of Go modules for vulnerabilities.
 // It assumes the root of each repo is a module, and there are no nested modules.
 func ScanModules(ctx context.Context, st store.Store, force bool) error {
@@ -50,7 +63,10 @@
 			return err
 		}
 		if err := processModule(ctx, modulePath, latest, dbClient, st, force); err != nil {
-			return err
+			if errors.As(err, new(scanError)) {
+				return err
+			}
+			// Otherwise, if the error was in the scanning itself, keep going.
 		}
 		latestTagged, err := latestTaggedVersion(ctx, modulePath)
 		if err != nil {
@@ -88,7 +104,7 @@
 		return err2
 	}
 	if err != nil {
-		return err
+		return scanError{err}
 	}
 	log.Infof(ctx, "%s@%s has %d vulns", modulePath, version, len(res.Vulns))
 	for _, v := range res.Vulns {
diff --git a/internal/worker/scan_modules_test.go b/internal/worker/scan_modules_test.go
index 82296c7..f53b77d 100644
--- a/internal/worker/scan_modules_test.go
+++ b/internal/worker/scan_modules_test.go
@@ -6,7 +6,9 @@
 
 import (
 	"context"
+	"errors"
 	"flag"
+	"io"
 	"os"
 	"testing"
 
@@ -31,6 +33,16 @@
 	}
 }
 
+func TestAsScanError(t *testing.T) {
+	check := func(err error, want bool) {
+		if got := errors.As(err, new(scanError)); got != want {
+			t.Errorf("%T: got %t, want %t", err, got, want)
+		}
+	}
+	check(io.EOF, false)
+	check(scanError{io.EOF}, true)
+}
+
 func TestScanModule(t *testing.T) {
 	ctx := event.WithExporter(context.Background(),
 		event.NewExporter(log.NewLineHandler(os.Stderr), nil))