| // 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 |
| } |