Skip to content

Commit 3754b2a

Browse files
committed
cmd/vulnreport: various vulnreport changes for batch automation
* When creating or unexcluding a report, add a note to the report that an error occurred in addition to logging a warning. * When unexcluding reports, fix and lint the report before writing it. * In Fix, attempt to add an advisory if the report needs one. All of these changes are intended to make it easier to batch create & unexclude reports, and notice when there are issues in the automated process. Change-Id: I33ec94a619cf0b89112a682386439a11381b6c57 Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/562246 Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent abd3526 commit 3754b2a

File tree

5 files changed

+56
-15
lines changed

5 files changed

+56
-15
lines changed

cmd/vulnreport/create.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,18 +220,23 @@ func reportFromAliases(ctx context.Context, id, modulePath string, aliases []str
220220
if ac != nil {
221221
suggestions, err := suggestions(ctx, ac, r, 1)
222222
if err != nil {
223+
r.AddNote(report.NoteTypeCreate, "failed to get AI-generated suggestions")
223224
log.Warnf("failed to get AI-generated suggestions for %s: %v\n", r.ID, err)
224-
} else if len(suggestions) == 0 {
225-
log.Warnf("failed to get AI-generated suggestions for %s (none generated)\n", r.ID)
226225
} else {
227226
log.Infof("applying AI-generated suggestion for %s", r.ID)
228227
applySuggestion(r, suggestions[0])
229228
}
230229
}
231230

232231
if *populateSymbols {
232+
log.Infof("attempting to auto-populate symbols for %s (this may take a while...)", r.ID)
233233
if err := symbols.Populate(r); err != nil {
234+
r.AddNote(report.NoteTypeCreate, "failed to auto-populate symbols")
234235
log.Warnf("could not auto-populate symbols: %s", err)
236+
} else {
237+
if err := checkReportSymbols(r); err != nil {
238+
log.Warnf("auto-populated symbols have error(s): %s", err)
239+
}
235240
}
236241
}
237242

cmd/vulnreport/unexclude.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ func (u *unexclude) run(ctx context.Context, filename string) (err error) {
8989
// Remove description because this is a "basic" report.
9090
newR.Description = ""
9191

92+
r.Fix(u.pc)
93+
if hasLints := newR.LintAsNotes(u.pc); hasLints {
94+
log.Warnf("unexcluded report %s has lint errors that need to be fixed manually", filename)
95+
}
96+
9297
if err := os.Remove(filename); err != nil {
9398
log.Errf("could not remove excluded report: %v", err)
9499
}

internal/genericosv/report.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,7 @@ func (osv *Entry) ToReport(goID string, pc *proxy.Client) *report.Report {
3636
case ghsa.IsGHSA(alias):
3737
r.GHSAs = append(r.GHSAs, alias)
3838
default:
39-
r.Notes = append(r.Notes, &report.Note{
40-
Body: fmt.Sprintf("found alias %s that is not a GHSA or CVE", alias),
41-
Type: report.NoteTypeCreate,
42-
})
39+
r.AddNote(report.NoteTypeCreate, "found alias %s that is not a GHSA or CVE", alias)
4340
}
4441
}
4542
addAlias(osv.ID)

internal/report/fix.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313

1414
"golang.org/x/exp/slices"
15+
"golang.org/x/vulndb/internal/osv"
1516
"golang.org/x/vulndb/internal/proxy"
1617
"golang.org/x/vulndb/internal/version"
1718
)
@@ -21,6 +22,7 @@ func (r *Report) Fix(pc *proxy.Client) {
2122
m.FixVersions(pc)
2223
}
2324
r.FixText()
25+
r.FixReferences()
2426
}
2527

2628
func (r *Report) FixText() {
@@ -32,9 +34,25 @@ func (r *Report) FixText() {
3234
if r.CVEMetadata != nil {
3335
fixLines(&r.CVEMetadata.Description)
3436
}
37+
}
38+
39+
func (r *Report) FixReferences() {
3540
for _, ref := range r.References {
3641
ref.URL = fixURL(ref.URL)
3742
}
43+
if r.missingAdvisory(r.countAdvisories()) {
44+
r.addAdvisory()
45+
}
46+
}
47+
48+
func (r *Report) addAdvisory() {
49+
// For now, only add an advisory if there is a CVE.
50+
if len(r.CVEs) > 0 {
51+
r.References = append(r.References, &Reference{
52+
Type: osv.ReferenceTypeAdvisory,
53+
URL: fmt.Sprintf("%s%s", NISTPrefix, r.CVEs[0]),
54+
})
55+
}
3856
}
3957

4058
// FixVersions replaces each version with its canonical form (if possible),

internal/report/lint.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,14 @@ func (ref *Reference) lint(l *linter, r *Report) {
219219
}
220220

221221
func (r *Report) lintReferences(l *linter) {
222-
advisoryCount := 0
223222
for i, ref := range r.References {
224223
rl := l.Group(name("references", i, ref.URL))
225224
ref.lint(rl, r)
226-
if ref.Type == osv.ReferenceTypeAdvisory {
227-
advisoryCount++
228-
}
229225
}
230226

231227
// Find missing references.
232228
rl := l.Group("references")
229+
advisoryCount := r.countAdvisories()
233230
if advisoryCount > 1 {
234231
rl.Errorf("too many advisories (found %d, want <=1)", advisoryCount)
235232
}
@@ -258,12 +255,27 @@ func (r *Report) lintReferences(l *linter) {
258255
rl.Errorf("must contain an announcement link matching regex %q", announceRegex)
259256
}
260257
}
261-
if advisoryCount == 0 && r.Description == "" && r.CVEMetadata == nil {
258+
if r.missingAdvisory(advisoryCount) {
262259
rl.Error("missing advisory (required because report has no description)")
263260
}
264261
}
265262
}
266263

264+
func (r *Report) countAdvisories() int {
265+
advisoryCount := 0
266+
for _, ref := range r.References {
267+
if ref.Type == osv.ReferenceTypeAdvisory {
268+
advisoryCount++
269+
}
270+
}
271+
return advisoryCount
272+
}
273+
274+
func (r *Report) missingAdvisory(advisoryCount int) bool {
275+
return !r.IsExcluded() && !r.IsFirstParty() &&
276+
advisoryCount == 0 && r.Description == "" && r.CVEMetadata == nil
277+
}
278+
267279
func (d *Description) lint(l *linter, r *Report) {
268280
desc := d.String()
269281

@@ -417,17 +429,21 @@ func (r *Report) LintAsNotes(pc *proxy.Client) bool {
417429
if lints := r.Lint(pc); len(lints) > 0 {
418430
slices.Sort(lints)
419431
for _, lint := range lints {
420-
r.Notes = append(r.Notes, &Note{
421-
Body: lint,
422-
Type: NoteTypeLint,
423-
})
432+
r.AddNote(NoteTypeLint, lint)
424433
}
425434
return true
426435
}
427436

428437
return false
429438
}
430439

440+
func (r *Report) AddNote(t NoteType, format string, v ...any) {
441+
r.Notes = append(r.Notes, &Note{
442+
Body: fmt.Sprintf(format, v...),
443+
Type: t,
444+
})
445+
}
446+
431447
// LintOffline performs all lint checks that don't require a network connection.
432448
func (r *Report) LintOffline() []string {
433449
return r.lint(nil)

0 commit comments

Comments
 (0)