Skip to content

Commit 6a2e4b6

Browse files
author
Maceo Thompson
committed
vulnreport: add 'symbols' cmd to vulnreport
This change integrates symbols.Patched into vulnreport. One can call `vulnreport symbols [REPORTNUMBER]` to use this. Additionally, if vulnreport is unable to find the correct fix link (this may happen in cases where there are multiple modules or the report's fix link is a pull request and not a commit hash), they can populate the FixLink field per-module. Change-Id: I6566c67538c4de29547432648a28f50cb735b3ce Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/559156 Reviewed-by: Tatiana Bradley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent af9fbfb commit 6a2e4b6

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

cmd/vulnreport/main.go

+74
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func main() {
7575
fmt.Fprintf(flag.CommandLine.Output(), "usage: vulnreport [cmd] [filename.yaml]\n")
7676
fmt.Fprintf(flag.CommandLine.Output(), " create [githubIssueNumber]: creates a new vulnerability YAML report\n")
7777
fmt.Fprintf(flag.CommandLine.Output(), " create-excluded: creates and commits all open github issues marked as excluded\n")
78+
fmt.Fprintf(flag.CommandLine.Output(), " symbols filename.yaml: finds and populates possible vulnerable symbols for a given report\n")
7879
fmt.Fprintf(flag.CommandLine.Output(), " lint filename.yaml ...: lints vulnerability YAML reports\n")
7980
fmt.Fprintf(flag.CommandLine.Output(), " cve filename.yaml ...: creates and saves CVE 5.0 record from the provided YAML reports\n")
8081
fmt.Fprintf(flag.CommandLine.Output(), " fix filename.yaml ...: fixes and reformats YAML reports\n")
@@ -161,6 +162,8 @@ func main() {
161162
cmdFunc = func(ctx context.Context, name string) error { return cveCmd(ctx, name) }
162163
case "fix":
163164
cmdFunc = func(ctx context.Context, name string) error { return fix(ctx, name, ghsaClient, pc, *force) }
165+
case "symbols":
166+
cmdFunc = func(ctx context.Context, name string) error { return findSymbols(ctx, name) }
164167
case "osv":
165168
cmdFunc = func(ctx context.Context, name string) error { return osvCmd(ctx, name, pc) }
166169
case "set-dates":
@@ -741,6 +744,77 @@ func addReferenceTODOs(r *report.Report) {
741744
}
742745
}
743746

747+
func findSymbols(_ context.Context, filename string) (err error) {
748+
defer derrors.Wrap(&err, "findSymbols(%q)", filename)
749+
infolog.Printf("findSymbols %s\n", filename)
750+
r, err := report.Read(filename)
751+
if err != nil {
752+
return err
753+
}
754+
var defaultFixes []string
755+
756+
for _, ref := range r.References {
757+
if ref.Type == osv.ReferenceTypeFix {
758+
if filepath.Base(filepath.Dir(ref.URL)) == "commit" {
759+
defaultFixes = append(defaultFixes, ref.URL)
760+
}
761+
}
762+
}
763+
if len(defaultFixes) == 0 {
764+
return fmt.Errorf("no commit fix links found")
765+
}
766+
767+
for _, mod := range r.Modules {
768+
hasFixLink := mod.FixLink != ""
769+
if hasFixLink {
770+
defaultFixes = append(defaultFixes, mod.FixLink)
771+
}
772+
numFixedSymbols := make([]int, len(defaultFixes))
773+
for i, fixLink := range defaultFixes {
774+
fixHash := filepath.Base(fixLink)
775+
fixRepo := strings.TrimSuffix(fixLink, "/commit/"+fixHash)
776+
pkgsToSymbols, err := symbols.Patched(mod.Module, fixRepo, fixHash, errlog)
777+
if err != nil {
778+
errlog.Print(err)
779+
continue
780+
}
781+
packages := mod.AllPackages()
782+
for pkg, symbols := range pkgsToSymbols {
783+
if _, exists := packages[pkg]; exists {
784+
packages[pkg].Symbols = append(packages[pkg].Symbols, symbols...)
785+
} else {
786+
mod.Packages = append(mod.Packages, &report.Package{
787+
Package: pkg,
788+
Symbols: symbols,
789+
})
790+
}
791+
numFixedSymbols[i] += len(symbols)
792+
}
793+
}
794+
// if the module's link field wasn't already populated, populate it with
795+
// the link that results in the most symbols
796+
if hasFixLink {
797+
defaultFixes = defaultFixes[:len(defaultFixes)-1]
798+
} else {
799+
mod.FixLink = defaultFixes[indexMax(numFixedSymbols)]
800+
}
801+
}
802+
return r.Write(filename)
803+
}
804+
805+
// indexMax takes a slice of nonempty ints and returns the index of the maximum value
806+
func indexMax(s []int) (index int) {
807+
maxVal := s[0]
808+
index = 0
809+
for i, val := range s {
810+
if val > maxVal {
811+
maxVal = val
812+
index = i
813+
}
814+
}
815+
return index
816+
}
817+
744818
func lint(_ context.Context, filename string, pc *proxy.Client) (err error) {
745819
defer derrors.Wrap(&err, "lint(%q)", filename)
746820
infolog.Printf("lint %s\n", filename)

internal/report/report.go

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ type Module struct {
5454
// It is rare that we need to specify this.
5555
VulnerableAtRequires []string `yaml:"vulnerable_at_requires,omitempty"`
5656
Packages []*Package `yaml:",omitempty"`
57+
// Used to automatically determine vulnerable symbols for a given module.
58+
// Will be auto-populated from the reports "fix" link if none is specified.
59+
FixLink string `yaml:"fix_link,omitempty"`
5760
}
5861

5962
type Package struct {
@@ -258,6 +261,15 @@ func (r *Report) AllCVEs() []string {
258261
return all
259262
}
260263

264+
// AllPkgs returns all affected packages in a given module.
265+
func (m *Module) AllPackages() map[string]*Package {
266+
pkgs := make(map[string]*Package)
267+
for _, pkg := range m.Packages {
268+
pkgs[pkg.Package] = pkg
269+
}
270+
return pkgs
271+
}
272+
261273
// Aliases returns all aliases (e.g., CVEs, GHSAs) for a report.
262274
func (r *Report) Aliases() []string {
263275
return append(r.AllCVEs(), r.GHSAs...)

0 commit comments

Comments
 (0)