internal/report,cmd/vulnreport: add GitHub Security Advisories
Reports now contain a list of GitHub Security Advisory IDs
that correspond to its CVEs.
The vulnreport fix command will populate this field.
Change-Id: I9488a603f2aab6f91ff84ec5a7e55fb5cde0085d
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/388676
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/vulnreport/main.go b/cmd/vulnreport/main.go
index 8111e31..e9afb3f 100644
--- a/cmd/vulnreport/main.go
+++ b/cmd/vulnreport/main.go
@@ -18,11 +18,13 @@
"sort"
"strconv"
"strings"
+ "time"
"github.com/go-git/go-git/v5"
"golang.org/x/tools/go/packages"
"golang.org/x/vulndb/internal/cvelistrepo"
"golang.org/x/vulndb/internal/derrors"
+ "golang.org/x/vulndb/internal/ghsa"
"golang.org/x/vulndb/internal/gitrepo"
"golang.org/x/vulndb/internal/issues"
"golang.org/x/vulndb/internal/report"
@@ -37,6 +39,7 @@
)
func main() {
+ ctx := context.Background()
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "usage: vulnreport [cmd] [filename.yaml]\n")
fmt.Fprintf(flag.CommandLine.Output(), " create [githubIssueNumber]: creates a new vulnerability YAML report\n")
@@ -72,7 +75,7 @@
if *localRepoPath != "" {
repoPath = *localRepoPath
}
- if err := create(context.Background(), githubID, *githubToken, *issueRepo, repoPath); err != nil {
+ if err := create(ctx, githubID, *githubToken, *issueRepo, repoPath); err != nil {
log.Fatal(err)
}
case "lint":
@@ -84,11 +87,24 @@
log.Fatal(err)
}
case "fix":
- if err := multi(fix, names); err != nil {
+ var GHSAsByCVE map[string][]string
+ if *githubToken == "" {
+ fmt.Println("flag -ghtoken not provided, so not fixing GHSAs")
+ } else {
+ fmt.Println("querying GitHub for GHSAs...")
+ var err error
+ GHSAsByCVE, err = loadGHSAsByCVE(ctx, *githubToken)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println("fixing...")
+ }
+ f := func(name string) error { return fix(name, GHSAsByCVE) }
+ if err := multi(f, names); err != nil {
log.Fatal(err)
}
case "set-dates":
- repo, err := gitrepo.Open(context.Background(), ".")
+ repo, err := gitrepo.Open(ctx, ".")
if err != nil {
log.Fatal(err)
}
@@ -194,7 +210,7 @@
return nil
}
-func fix(filename string) (err error) {
+func fix(filename string, GHSAsByCVE map[string][]string) (err error) {
defer derrors.Wrap(&err, "fix(%q)", filename)
r, err := report.Read(filename)
if err != nil {
@@ -208,6 +224,9 @@
return err
}
}
+ if GHSAsByCVE != nil {
+ fixGHSAs(r, GHSAsByCVE)
+ }
// Write unconditionally in order to format.
return r.Write(filename)
@@ -343,3 +362,36 @@
e.SetIndent("", "\t")
return e.Encode(cve)
}
+
+// loadGHSAsByCVE returns a map from CVE ID to GHSA IDs.
+// It does this by using the GitHub API to list all Go security
+// advisories with CVEs.
+func loadGHSAsByCVE(ctx context.Context, accessToken string) (_ map[string][]string, err error) {
+ defer derrors.Wrap(&err, "loadGHSAsByCVE")
+
+ const withCVE = true
+ sas, err := ghsa.List(ctx, accessToken, time.Time{}, withCVE)
+ if err != nil {
+ return nil, err
+ }
+ m := map[string][]string{}
+ for _, sa := range sas {
+ for _, id := range sa.Identifiers {
+ if id.Type == "CVE" {
+ m[id.Value] = append(m[id.Value], sa.PrettyID())
+ }
+ }
+ }
+ return m, nil
+}
+
+// fixGHSAs replaces r.GHSAs with a sorted list of GitHub Security
+// Advisory IDs that correspond to the CVEs.
+func fixGHSAs(r *report.Report, GHSAsByCVE map[string][]string) {
+ var gids []string
+ for _, cid := range r.CVEs {
+ gids = append(gids, GHSAsByCVE[cid]...)
+ }
+ sort.Strings(gids)
+ r.GHSAs = gids
+}
diff --git a/internal/ghsa/ghsa.go b/internal/ghsa/ghsa.go
index 973ba84..02ccd33 100644
--- a/internal/ghsa/ghsa.go
+++ b/internal/ghsa/ghsa.go
@@ -37,7 +37,7 @@
}
// An Identifier identifies an advisory according to some scheme or
-// organization, given by the Type field. Examples are GitHub and CVE.
+// organization, given by the Type field. Example types are GHSA and CVE.
type Identifier struct {
Type string
Value string
diff --git a/internal/report/report.go b/internal/report/report.go
index a0130ee..2f48343 100644
--- a/internal/report/report.go
+++ b/internal/report/report.go
@@ -68,11 +68,14 @@
LastModified *time.Time `yaml:"last_modified,omitempty"`
Withdrawn *time.Time `yaml:",omitempty"`
+ // CVE are CVE IDs for existing CVEs.
// If we are assigning a CVE ID ourselves, use CVEMetdata.ID instead.
- // CVE are CVE IDs for existing CVEs, if there is more than one.
- // Use either CVE or CVEs, but not both.
- CVEs []string `yaml:",omitempty"`
- Credit string `yaml:",omitempty"`
+ CVEs []string `yaml:",omitempty"`
+ // GHSAs are the IDs of GitHub Security Advisories that match
+ // the above CVEs.
+ GHSAs []string `yaml:",omitempty"`
+
+ Credit string `yaml:",omitempty"`
// Symbols originally identified as vulnerable.
Symbols []string `yaml:",omitempty"`
// Additional vulnerable symbols, computed from Symbols via static analysis