blob: 7ad946aaf5cf75972b64f5ecd3bbff5b27d90d0e [file] [log] [blame]
// Copyright 2022 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 report
import (
"path/filepath"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"golang.org/x/exp/slices"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/gitrepo"
"gopkg.in/yaml.v3"
)
var (
// YAMLDir is the name of the directory in the vulndb repo that
// contains reports.
YAMLDir = filepath.Join(dataFolder, reportsFolder)
// ExcludedDir is the name of the directory in the vulndb repo that
// contains excluded reports.
ExcludedDir = filepath.Join(dataFolder, excludedFolder)
)
const (
dataFolder, reportsFolder, excludedFolder = "data", "reports", "excluded"
)
// All returns all the reports in the repo, indexed by issue and by filename.
func All(repo *git.Repository) (byIssue map[int]*Report, byFile map[string]*Report, err error) {
defer derrors.Wrap(&err, "All()")
root, err := gitrepo.Root(repo)
if err != nil {
return nil, nil, err
}
byIssue = make(map[int]*Report)
byFile = make(map[string]*Report)
if err = root.Files().ForEach(func(f *object.File) error {
if !isYAMLReport(f) {
return nil
}
content, err := f.Contents()
if err != nil {
return err
}
var r Report
if err := yaml.Unmarshal([]byte(content), &r); err != nil {
return err
}
_, _, iss, err := ParseFilepath(f.Name)
if err != nil {
return err
}
byFile[f.Name] = &r
byIssue[iss] = &r
return nil
}); err != nil {
return nil, nil, err
}
return byIssue, byFile, nil
}
// XRef returns cross-references for a report: in this case, a map from
// filenames to aliases (CVE & GHSA IDs) and modules (excluding std and cmd).
func XRef(r *Report, existingByFile map[string]*Report) (matches map[string][]string) {
mods := make(map[string]bool)
for _, m := range r.Modules {
if mod := m.Module; mod != "" && mod != "std" && mod != "cmd" {
mods[m.Module] = true
}
}
// matches is a map from filename -> alias/module
matches = make(map[string][]string)
for fname, rr := range existingByFile {
for _, alias := range rr.Aliases() {
if slices.Contains(r.Aliases(), alias) {
matches[fname] = append(matches[fname], alias)
}
}
for _, m := range rr.Modules {
if mods[m.Module] {
k := "Module " + m.Module
matches[fname] = append(matches[fname], k)
}
}
}
return matches
}
// Aliases returns a sorted list of all aliases (CVEs and GHSAs) in vulndb,
// including those in the excluded directory.
func Aliases(repo *git.Repository) (_ []string, err error) {
defer derrors.Wrap(&err, "Aliases()")
root, err := gitrepo.Root(repo)
if err != nil {
return nil, err
}
var aliases []string
if err = root.Files().ForEach(func(f *object.File) error {
if !isYAMLReport(f) {
return nil
}
content, err := f.Contents()
if err != nil {
return err
}
var r Report
if err := yaml.Unmarshal([]byte(content), &r); err != nil {
return err
}
aliases = append(aliases, r.Aliases()...)
return nil
}); err != nil {
return nil, err
}
slices.Sort(aliases)
return aliases, nil
}
func isYAMLReport(f *object.File) bool {
dir, ext := filepath.Dir(f.Name), filepath.Ext(f.Name)
return (dir == YAMLDir || dir == ExcludedDir) && ext == ".yaml"
}