blob: 2a1021e63b5d953c2f2365efa0c70f042f34a4c5 [file] [log] [blame]
// 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 symbols
import (
"errors"
"fmt"
"path/filepath"
"strings"
"golang.org/x/exp/slices"
"golang.org/x/vulndb/internal/report"
)
// Populate attempts to populate the report with symbols derived
// from the patch link(s) in the report.
func Populate(r *report.Report, update bool) error {
return populate(r, update, Patched)
}
func populate(r *report.Report, update bool, patched func(string, string, string) (map[string][]string, error)) error {
var errs []error
for _, mod := range r.Modules {
hasFixLink := len(mod.FixLinks) >= 0
fixLinks := mod.FixLinks
if len(fixLinks) == 0 {
c := r.CommitLinks()
if len(c) == 0 {
errs = append(errs, fmt.Errorf("no commit fix links found for module %s", mod.Module))
continue
}
fixLinks = c
}
foundSymbols := false
for _, fixLink := range fixLinks {
found, err := populateFromFixLink(fixLink, mod, update, patched)
if err != nil {
errs = append(errs, err)
}
foundSymbols = foundSymbols || found
}
if !foundSymbols && fixLinks != nil {
errs = append(errs, fmt.Errorf("no vulnerable symbols found for module %s", mod.Module))
}
// Sort fix links for testing/deterministic output
if !hasFixLink && update {
slices.Sort(mod.FixLinks)
}
}
return errors.Join(errs...)
}
// populateFromFixLink takes a fixLink and a module and returns true if any symbols
// are found for the given fix/module pair.
func populateFromFixLink(fixLink string, m *report.Module, update bool, patched func(string, string, string) (map[string][]string, error)) (foundSymbols bool, err error) {
fixHash := filepath.Base(fixLink)
fixRepo := strings.TrimSuffix(fixLink, "/commit/"+fixHash)
pkgsToSymbols, err := patched(m.Module, fixRepo, fixHash)
if err != nil {
return false, err
}
modPkgs := m.AllPackages()
for pkg, symbols := range pkgsToSymbols {
foundSymbols = true
if modPkg, exists := modPkgs[pkg]; exists {
// Ensure there are no duplicate symbols
for _, s := range symbols {
if !slices.Contains(modPkg.Symbols, s) {
modPkg.Symbols = append(modPkg.Symbols, s)
}
}
} else {
m.Packages = append(m.Packages, &report.Package{
Package: pkg,
Symbols: symbols,
})
}
}
if update && foundSymbols {
m.FixLinks = append(m.FixLinks, fixLink)
}
return foundSymbols, nil
}