internal/report: add function Aliases

Function Aliases returns a list of all aliases (CVEs and GHSAs) in the
given vulndb repo. This will be used by the worker in place of the old
x/vuln client.

This change also updates the GetAllExisting function to not use a strict
YAML decoder, which sometimes causes the worker to spuriously fail
if a new YAML field is added and the new worker hasn't yet been
deployed.

Change-Id: I5e1872752ce4954ee89df8c0a0e46b2c9ab1ea4a
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/497038
Run-TryBot: Tatiana Bradley <tatianabradley@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/report/reports.go b/internal/report/reports.go
index 09a3030..3aafb47 100644
--- a/internal/report/reports.go
+++ b/internal/report/reports.go
@@ -5,7 +5,6 @@
 package report
 
 import (
-	"fmt"
 	"path/filepath"
 
 	"github.com/go-git/go-git/v5"
@@ -37,29 +36,25 @@
 	byFile = make(map[string]*Report)
 
 	if err = root.Files().ForEach(func(f *object.File) error {
-		name := f.Name
-		if !(filepath.Dir(name) == YAMLDir || filepath.Dir(name) == ExcludedDir) ||
-			filepath.Ext(name) != ".yaml" {
+		if !isYAMLReport(f) {
 			return nil
 		}
 
-		reader, err := f.Reader()
+		content, err := f.Contents()
 		if err != nil {
 			return err
 		}
-		d := yaml.NewDecoder(reader)
-		d.KnownFields(true)
 		var r Report
-		if err := d.Decode(&r); err != nil {
-			return fmt.Errorf("yaml.Decode: %v", err)
+		if err := yaml.Unmarshal([]byte(content), &r); err != nil {
+			return err
 		}
 
-		_, _, iss, err := ParseFilepath(name)
+		_, _, iss, err := ParseFilepath(f.Name)
 		if err != nil {
 			return err
 		}
 
-		byFile[name] = &r
+		byFile[f.Name] = &r
 		byIssue[iss] = &r
 
 		return nil
@@ -97,3 +92,43 @@
 	}
 	return matches
 }
+
+// Aliases returns a sorted list of all aliases (CVEs and GHSAs) in vulndb,
+// including those in the excluded directory.
+func Aliases(repo *git.Repository) (_ []string, err error) {
+	defer derrors.Wrap(&err, "Aliases()")
+	root, err := gitrepo.Root(repo)
+	if err != nil {
+		return nil, err
+	}
+
+	var aliases []string
+	if err = root.Files().ForEach(func(f *object.File) error {
+		if !isYAMLReport(f) {
+			return nil
+		}
+
+		content, err := f.Contents()
+		if err != nil {
+			return err
+		}
+		var r Report
+		if err := yaml.Unmarshal([]byte(content), &r); err != nil {
+			return err
+		}
+
+		aliases = append(aliases, r.GetAliases()...)
+
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	slices.Sort(aliases)
+	return aliases, nil
+}
+
+func isYAMLReport(f *object.File) bool {
+	dir, ext := filepath.Dir(f.Name), filepath.Ext(f.Name)
+	return (dir == YAMLDir || dir == ExcludedDir) && ext == ".yaml"
+}
diff --git a/internal/report/reports_test.go b/internal/report/reports_test.go
index a672481..9ce38f1 100644
--- a/internal/report/reports_test.go
+++ b/internal/report/reports_test.go
@@ -42,7 +42,7 @@
 		Modules: []*Module{
 			{Module: "example.com/adiff/module"},
 		},
-		CVEs: []string{"CVE-9999-0002"},
+		CVEs: []string{"CVE-9999-0005"},
 	}
 )
 
@@ -157,3 +157,24 @@
 		})
 	}
 }
+
+func TestAliases(t *testing.T) {
+	repo, err := gitrepo.ReadTxtarRepo("testdata/repo.txtar", time.Now())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got, err := Aliases(repo)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	want := []string{"CVE-9999-0001",
+		"CVE-9999-0002",
+		"CVE-9999-0005",
+		"GHSA-9999-abcd-efgh"}
+
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Errorf("Aliases() mismatch (-want, +got): %s", diff)
+	}
+}
diff --git a/internal/report/testdata/repo.txtar b/internal/report/testdata/repo.txtar
index 9157bec..778b9fb 100644
--- a/internal/report/testdata/repo.txtar
+++ b/internal/report/testdata/repo.txtar
@@ -26,4 +26,4 @@
 modules:
   - module: example.com/adiff/module
 cves:
-  - CVE-9999-0002
\ No newline at end of file
+  - CVE-9999-0005
\ No newline at end of file