зеркало из https://github.com/golang/vulndb.git
cmd/vulnreport: exhaustively search for aliases
Change-Id: I1bcb1f185bb3227d2eca1b1d1adb87fa8166314b Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/530597 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Родитель
4a6d9fe05a
Коммит
fa8260e475
|
@ -0,0 +1,89 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/vulndb/internal/cveschema5"
|
||||
"golang.org/x/vulndb/internal/ghsa"
|
||||
"golang.org/x/vulndb/internal/report"
|
||||
)
|
||||
|
||||
// addMissingAliases uses the existing aliases in a report to find
|
||||
// any missing aliases, and adds them to the report.
|
||||
func addMissingAliases(ctx context.Context, r *report.Report, gc *ghsa.Client) (added int) {
|
||||
return r.AddAliases(allAliases(ctx, r.Aliases(), gc))
|
||||
}
|
||||
|
||||
// allAliases returns a list of all aliases associated with the given knownAliases,
|
||||
// (including the knownAliases themselves).
|
||||
func allAliases(ctx context.Context, knownAliases []string, gc *ghsa.Client) []string {
|
||||
aliasesFor := func(ctx context.Context, alias string) ([]string, error) {
|
||||
switch {
|
||||
case ghsa.IsGHSA(alias):
|
||||
return aliasesForGHSA(ctx, alias, gc)
|
||||
case cveschema5.IsCVE(alias):
|
||||
return aliasesForCVE(ctx, alias, gc)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
}
|
||||
}
|
||||
return aliasesBFS(ctx, knownAliases, aliasesFor)
|
||||
}
|
||||
|
||||
func aliasesBFS(ctx context.Context, knownAliases []string,
|
||||
aliasesFor func(ctx context.Context, alias string) ([]string, error)) (all []string) {
|
||||
var queue []string
|
||||
var seen = make(map[string]bool)
|
||||
queue = append(queue, knownAliases...)
|
||||
|
||||
for len(queue) > 0 {
|
||||
alias := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
if seen[alias] {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[alias] = true
|
||||
all = append(all, alias)
|
||||
aliases, err := aliasesFor(ctx, alias)
|
||||
if err != nil {
|
||||
errlog.Printf(err.Error())
|
||||
continue
|
||||
}
|
||||
queue = append(queue, aliases...)
|
||||
}
|
||||
|
||||
slices.Sort(all)
|
||||
return slices.Compact(all)
|
||||
}
|
||||
|
||||
func aliasesForGHSA(ctx context.Context, alias string, gc *ghsa.Client) (aliases []string, err error) {
|
||||
sa, err := gc.FetchGHSA(ctx, alias)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch GHSA record for %s", alias)
|
||||
}
|
||||
for _, id := range sa.Identifiers {
|
||||
if id.Type == "CVE" || id.Type == "GHSA" {
|
||||
aliases = append(aliases, id.Value)
|
||||
}
|
||||
}
|
||||
return aliases, nil
|
||||
}
|
||||
|
||||
func aliasesForCVE(ctx context.Context, cve string, gc *ghsa.Client) (aliases []string, err error) {
|
||||
sas, err := gc.ListForCVE(ctx, cve)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find GHSAs for CVE %s", cve)
|
||||
}
|
||||
for _, sa := range sas {
|
||||
aliases = append(aliases, sa.ID)
|
||||
}
|
||||
return aliases, nil
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestAliasesBFS(t *testing.T) {
|
||||
tests := []struct {
|
||||
knownAliases []string
|
||||
aliasesFor func(ctx context.Context, alias string) ([]string, error)
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
knownAliases: []string{"CVE-2023-0001"},
|
||||
aliasesFor: func(ctx context.Context, alias string) ([]string, error) {
|
||||
switch alias {
|
||||
case "CVE-2023-0001":
|
||||
return []string{"GHSA-1234567890"}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
}
|
||||
},
|
||||
want: []string{"CVE-2023-0001", "GHSA-1234567890"},
|
||||
},
|
||||
{
|
||||
knownAliases: []string{"CVE-2023-0001", "GHSA-1234567890"},
|
||||
aliasesFor: func(ctx context.Context, alias string) ([]string, error) {
|
||||
switch alias {
|
||||
case "CVE-2023-0001":
|
||||
return []string{"GHSA-1234567890"}, nil
|
||||
case "GHSA-1234567890":
|
||||
return []string{"CVE-2023-0001"}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
}
|
||||
},
|
||||
want: []string{"CVE-2023-0001", "GHSA-1234567890"},
|
||||
},
|
||||
{
|
||||
knownAliases: []string{"CVE-2023-0001", "GHSA-1234567890"},
|
||||
aliasesFor: func(ctx context.Context, alias string) ([]string, error) {
|
||||
switch alias {
|
||||
case "CVE-2023-0001":
|
||||
return []string{"GHSA-1234567890", "CVE-2023-0002"}, nil
|
||||
case "GHSA-1234567890":
|
||||
return []string{"CVE-2023-0001", "CVE-2023-0002"}, nil
|
||||
case "CVE-2023-0002":
|
||||
return []string{"CVE-2023-0001", "GHSA-1234567890"}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
}
|
||||
},
|
||||
want: []string{"CVE-2023-0001", "CVE-2023-0002", "GHSA-1234567890"},
|
||||
},
|
||||
{
|
||||
knownAliases: []string{"CVE-2023-0001"},
|
||||
aliasesFor: func(ctx context.Context, alias string) ([]string, error) {
|
||||
switch alias {
|
||||
case "CVE-2023-0001":
|
||||
return []string{"GHSA-1234567890"}, nil
|
||||
case "GHSA-1234567890":
|
||||
return []string{"CVE-2023-0002"}, nil
|
||||
case "CVE-2023-0002":
|
||||
return []string{"GHSA-1234567890"}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
}
|
||||
},
|
||||
want: []string{"CVE-2023-0001", "CVE-2023-0002", "GHSA-1234567890"},
|
||||
},
|
||||
{
|
||||
knownAliases: []string{},
|
||||
aliasesFor: func(ctx context.Context, alias string) ([]string, error) {
|
||||
return nil, fmt.Errorf("unsupported alias %s", alias)
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := aliasesBFS(context.Background(), test.knownAliases, test.aliasesFor)
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("aliasesBFS(%v) = %v, want %v", test.knownAliases, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -508,17 +508,12 @@ func newReport(ctx context.Context, cfg *createCfg, parsed *parsedIssue) (*repor
|
|||
}
|
||||
r.ID = parsed.id
|
||||
|
||||
if err := addAliases(ctx, r, cfg.ghsaClient); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fill an any CVEs and GHSAs we found that may have been missed
|
||||
// in report creation.
|
||||
if r.CVEMetadata == nil {
|
||||
r.CVEs = dedupeAndSort(append(r.CVEs, parsed.cves...))
|
||||
}
|
||||
r.GHSAs = dedupeAndSort(append(r.GHSAs, parsed.ghsas...))
|
||||
// Ensure all source aliases are added to the report.
|
||||
r.AddAliases(parsed.ghsas)
|
||||
r.AddAliases(parsed.cves)
|
||||
|
||||
// Find any additional aliases referenced by the source aliases.
|
||||
addMissingAliases(ctx, r, cfg.ghsaClient)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
@ -765,8 +760,8 @@ func fix(ctx context.Context, filename string, ghsaClient *ghsa.Client, pc *prox
|
|||
}
|
||||
if !*skipAlias {
|
||||
infolog.Printf("%s: checking for missing GHSAs and CVEs (use -skip-alias to skip this)", r.ID)
|
||||
if err := addAliases(ctx, r, ghsaClient); err != nil {
|
||||
return err
|
||||
if added := addMissingAliases(ctx, r, ghsaClient); added > 0 {
|
||||
infolog.Printf("%s: added %d missing aliases", r.ID, added)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1206,64 +1201,3 @@ func setDates(_ context.Context, filename string, dates map[string]gitrepo.Dates
|
|||
r.Published = d.Oldest
|
||||
return r.Write(filename)
|
||||
}
|
||||
|
||||
func dedupeAndSort[T constraints.Ordered](s []T) []T {
|
||||
s = slices.Clone(s)
|
||||
slices.Sort(s)
|
||||
return slices.Compact(s)
|
||||
}
|
||||
|
||||
// addAliases finds and adds missing GHSAs and CVEs that correspond to the
|
||||
// aliases already in the report.
|
||||
// For now, this function does not do an exhaustive search for aliases (i.e., it does not
|
||||
// search for aliases associated with the new ones we found), as it is relatively rare
|
||||
// for a vuln to have more than one CVE and GHSA.
|
||||
func addAliases(ctx context.Context, r *report.Report, gc *ghsa.Client) error {
|
||||
origG := len(r.GHSAs)
|
||||
r.GHSAs = dedupeAndSort(append(r.GHSAs, findGHSAs(ctx, r, gc)...))
|
||||
|
||||
origC := len(r.CVEs)
|
||||
r.CVEs = dedupeAndSort(append(r.CVEs, findCVEs(ctx, r, gc)...))
|
||||
|
||||
if newG, newC := len(r.GHSAs)-origG, len(r.CVEs)-origC; newG+newC > 0 {
|
||||
infolog.Printf("%s: found %d new GHSAs and %d new CVEs\n", r.ID, newG, newC)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findGHSAs(ctx context.Context, r *report.Report, gc *ghsa.Client) []string {
|
||||
var ghsas []string
|
||||
for _, cve := range r.AllCVEs() {
|
||||
sas, err := gc.ListForCVE(ctx, cve)
|
||||
if err != nil {
|
||||
errlog.Printf("%s: could not find GHSAs for %s", r.ID, cve)
|
||||
continue
|
||||
}
|
||||
for _, sa := range sas {
|
||||
ghsas = append(ghsas, sa.ID)
|
||||
}
|
||||
}
|
||||
return ghsas
|
||||
}
|
||||
|
||||
func findCVEs(ctx context.Context, r *report.Report, gc *ghsa.Client) []string {
|
||||
var cves []string
|
||||
for _, ghsa := range r.GHSAs {
|
||||
sa, err := gc.FetchGHSA(ctx, ghsa)
|
||||
if err != nil {
|
||||
errlog.Printf("%s: could not fetch record for %s", r.ID, ghsa)
|
||||
continue
|
||||
}
|
||||
for _, id := range sa.Identifiers {
|
||||
if id.Type == "CVE" {
|
||||
// Skip CVEs that we assigned, as we already know about them,
|
||||
// and they are stored in "cve_metadata" instead of "cves".
|
||||
if id.Value == r.GoCVE() {
|
||||
continue
|
||||
}
|
||||
cves = append(cves, id.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cves
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче