Skip to content

Commit

Permalink
cmd/vulnreport: add command unexclude
Browse files Browse the repository at this point in the history
Command `vulnreport unexclude` converts an excluded report to a
regular report. Will be used as part of an experiment to add back reports
that were previously excluded from vulndb.

Change-Id: I420a8262144fe6b0f459cb1a8438062931a15700
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/559815
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
tatianab committed Feb 2, 2024
1 parent 3d58640 commit 7cb175d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 25 deletions.
61 changes: 36 additions & 25 deletions cmd/vulnreport/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,22 +218,10 @@ func createReport(ctx context.Context, cfg *createCfg, iss *issues.Issue) (r *re
return nil, err
}

aliases := allAliases(ctx, parsed.aliases, cfg.ghsaClient)
if alias, ok := pickBestAlias(aliases, *preferCVE); ok {
log.Infof("creating report %s based on %s (picked from [%s])", parsed.id, alias, strings.Join(aliases, ", "))
r, err = reportFromAlias(ctx, parsed.id, parsed.modulePath, alias, cfg)
if err != nil {
return nil, err
}
} else {
log.Infof("no alias found, creating basic report for %s", parsed.id)
r = &report.Report{
ID: parsed.id,
Modules: []*report.Module{
{
Module: parsed.modulePath,
},
}}
r, err = reportFromAliases(ctx, parsed.id, parsed.modulePath, parsed.aliases,
cfg.proxyClient, cfg.ghsaClient, cfg.aiClient)
if err != nil {
return nil, err
}

if parsed.excluded != "" {
Expand All @@ -250,14 +238,38 @@ func createReport(ctx context.Context, cfg *createCfg, iss *issues.Issue) (r *re
}
}

addTODOs(r)
return r, nil
}

func reportFromAliases(ctx context.Context, id, modulePath string, aliases []string,
pc *proxy.Client, gc *ghsa.Client, ac *genai.GeminiClient) (r *report.Report, err error) {
aliases = allAliases(ctx, aliases, gc)
if alias, ok := pickBestAlias(aliases, *preferCVE); ok {
log.Infof("creating report %s based on %s (picked from [%s])", id, alias, strings.Join(aliases, ", "))
r, err = reportFromAlias(ctx, id, modulePath, alias, pc, gc)
if err != nil {
return nil, err
}
} else {
log.Infof("no alias found, creating basic report for %s", id)
r = &report.Report{
ID: id,
Modules: []*report.Module{
{
Module: modulePath,
},
}}
}

// Ensure all source aliases are added to the report.
r.AddAliases(aliases)

// Find any additional aliases referenced by the source aliases.
addMissingAliases(ctx, r, cfg.ghsaClient)
addMissingAliases(ctx, r, gc)

if cfg.aiClient != nil {
suggestions, err := suggest(ctx, cfg.aiClient, r, 1)
if ac != nil {
suggestions, err := suggest(ctx, ac, r, 1)
if err != nil {
log.Warnf("failed to get AI-generated suggestions for %s: %v\n", r.ID, err)
} else if len(suggestions) == 0 {
Expand All @@ -268,7 +280,6 @@ func createReport(ctx context.Context, cfg *createCfg, iss *issues.Issue) (r *re
}
}

addTODOs(r)
return r, nil
}

Expand Down Expand Up @@ -353,22 +364,22 @@ Adds excluded reports:
// reportFromBestAlias returns a new report created from the "best" alias in the list.
// 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) {
func reportFromAlias(ctx context.Context, id, modulePath, alias string, pc *proxy.Client, gc *ghsa.Client) (*report.Report, error) {
switch {
case ghsa.IsGHSA(alias) && *graphQL:
ghsa, err := cfg.ghsaClient.FetchGHSA(ctx, alias)
ghsa, err := gc.FetchGHSA(ctx, alias)
if err != nil {
return nil, err
}
r := report.GHSAToReport(ghsa, modulePath, cfg.proxyClient)
r := report.GHSAToReport(ghsa, modulePath, pc)
r.ID = id
return r, nil
case ghsa.IsGHSA(alias):
ghsa, err := genericosv.Fetch(alias)
if err != nil {
return nil, err
}
return ghsa.ToReport(id, cfg.proxyClient), nil
return ghsa.ToReport(id, pc), nil
case cveschema5.IsCVE(alias):
cve, err := cveclient.Fetch(alias)
if err != nil {
Expand All @@ -377,7 +388,7 @@ func reportFromAlias(ctx context.Context, id, modulePath, alias string, cfg *cre
log.Infof("no published record found for %s, creating basic report", alias)
return basicReport(id, modulePath), nil
}
return report.CVE5ToReport(cve, id, modulePath, cfg.proxyClient), nil
return report.CVE5ToReport(cve, id, modulePath, pc), nil
}

log.Infof("alias %s is not a CVE or GHSA, creating basic report", alias)
Expand Down
12 changes: 12 additions & 0 deletions cmd/vulnreport/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"runtime/pprof"

vlog "golang.org/x/vulndb/cmd/vulnreport/log"
"golang.org/x/vulndb/internal/genai"
"golang.org/x/vulndb/internal/ghsa"
"golang.org/x/vulndb/internal/gitrepo"
"golang.org/x/vulndb/internal/proxy"
Expand Down Expand Up @@ -135,6 +136,17 @@ func main() {
log.Fatal(err)
}
cmdFunc = func(ctx context.Context, name string) error { return setDates(ctx, name, commitDates) }
case "unexclude":
var ac *genai.GeminiClient
var err error
if *useAI {
ac, err = genai.NewGeminiClient(ctx)
if err != nil {
log.Fatal(err)
}
defer ac.Close()
}
cmdFunc = func(ctx context.Context, name string) error { return unexclude(ctx, name, ghsaClient, pc, ac) }
case "xref":
repo, err := gitrepo.Open(ctx, ".")
if err != nil {
Expand Down
72 changes: 72 additions & 0 deletions cmd/vulnreport/unexclude.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 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 main

import (
"context"
"os"

"golang.org/x/vulndb/cmd/vulnreport/log"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/genai"
"golang.org/x/vulndb/internal/ghsa"
"golang.org/x/vulndb/internal/proxy"
"golang.org/x/vulndb/internal/report"
)

// unexclude converts an excluded report into a regular report.
func unexclude(ctx context.Context, filename string, gc *ghsa.Client, pc *proxy.Client, ac *genai.GeminiClient) (err error) {
defer derrors.Wrap(&err, "unexclude(%s)", filename)

log.Infof("unexclude %s", filename)

r, err := report.Read(filename)
if err != nil {
return err
}

if !r.IsExcluded() {
log.Infof("report %s is not excluded, can't unexclude", r.ID)
return nil
}

// Usually, we only unexclude reports that are effectively private or not importable.
if r.Excluded != "EFFECTIVELY_PRIVATE" && r.Excluded != "NOT_IMPORTABLE" {
if *force {
log.Warnf("report %s is excluded for reason %q, but -f was specified, continuing", r.ID, r.Excluded)
} else {
log.Infof("report %s is excluded for reason %q - we don't unexclude these report types (use -f to force)", r.ID, r.Excluded)
return nil
}
}

log.Infof("creating regular report based on excluded report %s", filename)
aliases := r.Aliases()
id := r.ID
var modulePath string
if len(r.Modules) > 0 {
modulePath = r.Modules[0].Module
}
newR, err := reportFromAliases(ctx, id, modulePath, aliases, pc, gc, ac)
if err != nil {
return err
}

// Remove description because this is a "basic" report.
newR.Description = ""

if err := os.Remove(filename); err != nil {
log.Errf("could not remove excluded report: %v", err)
}
log.Infof("removed excluded report %s", filename)

newFilename, err := writeReport(newR)
if err != nil {
return err
}
log.Out(newFilename)

return nil
}

0 comments on commit 7cb175d

Please sign in to comment.