cmd/vulnreport,internal/cveclient: use cve v5 in vulnreport
The vulnreport command now fetches CVE records in JSON 5.0 format
instead of the legacy 4.0 format.
This change also adds a new function, Fetch, which makes an
unauthenticated HTTP request to the CVE5 database to grab a CVE
record.
This function is used in vulnreport instead of the much-slower
cvelistrepo.FetchCVE (which clones the whole cvelistrepo).
Change-Id: Ic255e98d7c1a52301810dc53712fc2ab4a648e70
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/547560
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/cmd/vulnreport/main.go b/cmd/vulnreport/main.go
index 36fc7ef..7ae0ec3 100644
--- a/cmd/vulnreport/main.go
+++ b/cmd/vulnreport/main.go
@@ -20,16 +20,13 @@
"runtime/pprof"
"strconv"
"strings"
- "sync"
"time"
- "github.com/go-git/go-git/v5"
"github.com/google/go-cmp/cmp"
"golang.org/x/exp/constraints"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
- "golang.org/x/vulndb/internal/cvelistrepo"
- "golang.org/x/vulndb/internal/cveschema"
+ "golang.org/x/vulndb/internal/cveclient"
"golang.org/x/vulndb/internal/cveschema5"
"golang.org/x/vulndb/internal/database"
"golang.org/x/vulndb/internal/derrors"
@@ -45,18 +42,17 @@
)
var (
- localRepoPath = flag.String("local-cve-repo", "", "path to local repo, instead of cloning remote")
- issueRepo = flag.String("issue-repo", "github.com/golang/vulndb", "repo to create issues in")
- githubToken = flag.String("ghtoken", "", "GitHub access token (default: value of VULN_GITHUB_ACCESS_TOKEN)")
- skipSymbols = flag.Bool("skip-symbols", false, "for lint and fix, don't load package for symbols checks")
- skipAlias = flag.Bool("skip-alias", false, "for fix, skip adding new GHSAs and CVEs")
- graphQL = flag.Bool("graphql", false, "for create, fetch GHSAs from the Github GraphQL API instead of the OSV database")
- preferCVE = flag.Bool("cve", false, "for create, prefer CVEs over GHSAs as canonical source")
- updateIssue = flag.Bool("up", false, "for commit, create a CL that updates (doesn't fix) the tracking bug")
- closedOk = flag.Bool("closed-ok", false, "for create & create-excluded, allow closed issues to be created")
- cpuprofile = flag.String("cpuprofile", "", "write cpuprofile to file")
- quiet = flag.Bool("q", false, "quiet mode (suppress info logs)")
- force = flag.Bool("f", false, "for fix, force Fix to run even if there are no lint errors")
+ issueRepo = flag.String("issue-repo", "github.com/golang/vulndb", "repo to create issues in")
+ githubToken = flag.String("ghtoken", "", "GitHub access token (default: value of VULN_GITHUB_ACCESS_TOKEN)")
+ skipSymbols = flag.Bool("skip-symbols", false, "for lint and fix, don't load package for symbols checks")
+ skipAlias = flag.Bool("skip-alias", false, "for fix, skip adding new GHSAs and CVEs")
+ graphQL = flag.Bool("graphql", false, "for create, fetch GHSAs from the Github GraphQL API instead of the OSV database")
+ preferCVE = flag.Bool("cve", false, "for create, prefer CVEs over GHSAs as canonical source")
+ updateIssue = flag.Bool("up", false, "for commit, create a CL that updates (doesn't fix) the tracking bug")
+ closedOk = flag.Bool("closed-ok", false, "for create & create-excluded, allow closed issues to be created")
+ cpuprofile = flag.String("cpuprofile", "", "write cpuprofile to file")
+ quiet = flag.Bool("q", false, "quiet mode (suppress info logs)")
+ force = flag.Bool("f", false, "for fix, force Fix to run even if there are no lint errors")
)
var (
@@ -277,28 +273,6 @@
allowClosed bool
}
-var (
- once sync.Once
- cveRepo *git.Repository
-)
-
-func loadCVERepo(ctx context.Context) *git.Repository {
- // Loading the CVE git repo takes a while, so do it on demand only.
- once.Do(func() {
- infolog.Println("cloning CVE repo (this takes a while)")
- repoPath := cvelistrepo.URLv4
- if *localRepoPath != "" {
- repoPath = *localRepoPath
- }
- var err error
- cveRepo, err = gitrepo.CloneOrOpen(ctx, repoPath)
- if err != nil {
- log.Fatal(err)
- }
- })
- return cveRepo
-}
-
func setupCreate(ctx context.Context, args []string) ([]int, *createCfg, error) {
if *githubToken == "" {
return nil, nil, fmt.Errorf("githubToken must be provided")
@@ -518,31 +492,45 @@
// For now, it prefers the first GHSA in the list, followed by the first CVE in the list
// (if no GHSA is present). If no GHSAs or CVEs are present, it returns a new empty Report.
func reportFromAlias(ctx context.Context, id, modulePath, alias string, cfg *createCfg) (*report.Report, error) {
- var r *report.Report
switch {
case ghsa.IsGHSA(alias) && *graphQL:
ghsa, err := cfg.ghsaClient.FetchGHSA(ctx, alias)
if err != nil {
return nil, err
}
- r = report.GHSAToReport(ghsa, modulePath, cfg.proxyClient)
+ r := report.GHSAToReport(ghsa, modulePath, cfg.proxyClient)
+ r.ID = id
+ return r, nil
case ghsa.IsGHSA(alias):
ghsa, err := genericosv.Fetch(alias)
if err != nil {
return nil, err
}
- r = ghsa.ToReport(id, cfg.proxyClient)
+ return ghsa.ToReport(id, cfg.proxyClient), nil
case cveschema5.IsCVE(alias):
- cve := &cveschema.CVE{}
- if err := cvelistrepo.FetchCVE(ctx, loadCVERepo(ctx), alias, cve); err != nil {
- return nil, err
+ cve, err := cveclient.Fetch(alias)
+ if err != nil {
+ // If a CVE is not found, it is most likely a CVE we reserved but haven't
+ // published yet.
+ infolog.Printf("no published record found for %s, creating basic report", alias)
+ return basicReport(id, modulePath), nil
}
- r = report.CVEToReport(cve, id, modulePath, cfg.proxyClient)
- default:
- r = &report.Report{}
+ return report.CVE5ToReport(cve, id, modulePath, cfg.proxyClient), nil
}
- r.ID = id
- return r, nil
+
+ infolog.Printf("alias %s is not a CVE or GHSA, creating basic report", alias)
+ return basicReport(id, modulePath), nil
+}
+
+func basicReport(id, modulePath string) *report.Report {
+ return &report.Report{
+ ID: id,
+ Modules: []*report.Module{
+ {
+ Module: modulePath,
+ },
+ },
+ }
}
type parsedIssue struct {
diff --git a/internal/cveclient/fetch.go b/internal/cveclient/fetch.go
new file mode 100644
index 0000000..3fc1f83
--- /dev/null
+++ b/internal/cveclient/fetch.go
@@ -0,0 +1,17 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cveclient
+
+import "golang.org/x/vulndb/internal/cveschema5"
+
+// Fetch returns the CVE record associated with the ID.
+// It is intended one-off (non-batch) requests, and
+// is much faster than cvelistrepo.FetchCVE.
+func Fetch(id string) (*cveschema5.CVERecord, error) {
+ c := New(Config{
+ Endpoint: ProdEndpoint,
+ })
+ return c.RetrieveRecord(id)
+}