cmd/vulnreport: populate report with exported symbols

The fix subcommand will re-populate the symbols fields of the report
with all of the vulnerable exported symbols.

Change-Id: I5b0e097b367e74c52ea123022e268b91e54aec17
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/379776
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Jonathan Amsterdam 2022-01-19 14:51:25 -05:00
Родитель 377549d6a3
Коммит fa811db071
5 изменённых файлов: 277 добавлений и 5 удалений

Просмотреть файл

@ -0,0 +1,99 @@
// Copyright 2021 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"
"errors"
"fmt"
"os"
"strings"
"golang.org/x/exp/vulncheck"
"golang.org/x/tools/go/packages"
vdbclient "golang.org/x/vuln/client"
"golang.org/x/vuln/osv"
"golang.org/x/vulndb/internal/database"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/report"
)
// A reportClient is a vulndb.Client that returns the Entry for a single report.
type reportClient struct {
vdbclient.Client
entry *osv.Entry
entriesByModule map[string][]*osv.Entry
}
// newReportClient creates a reportClient from a given report.
func newReportClient(r *report.Report) *reportClient {
entries := map[string][]*osv.Entry{}
entry, modules := database.GenerateOSVEntry("?", "?", *r)
for _, m := range modules {
entries[m] = append(entries[m], &entry)
}
return &reportClient{entry: &entry, entriesByModule: entries}
}
// GetByModule implements vdbclient.Client.GetByModule.
func (e *reportClient) GetByModule(ctx context.Context, m string) ([]*osv.Entry, error) {
return e.entriesByModule[m], nil
}
// exportedFunctions returns a set of vulnerable functions exported by a set of packages
// from the same module.
func exportedFunctions(pkgs []*packages.Package, rc *reportClient) (_ map[string]bool, err error) {
defer derrors.Wrap(&err, "exportedFunctions(%q)", pkgs[0].PkgPath)
if pkgs[0].Module == nil {
return nil, errors.New("pkgs[0] is missing Module")
}
if !affected(rc.entry, pkgs[0].Module.Version) {
fmt.Fprintf(os.Stderr, "version %s of module %s is not affected by this vuln\n",
pkgs[0].Module.Version, pkgs[0].Module.Path)
return map[string]bool{}, nil
}
vpkgs := vulncheck.Convert(pkgs)
res, err := vulncheck.Source(context.Background(), vpkgs, &vulncheck.Config{Client: rc})
if err != nil {
return nil, err
}
// Return the name of all entry points.
// Note that "main" and "init" are both possible entries.
// Both have clear meanings: "main" means that invoking
// the program is a problem, and "init" means that very likely
// some global state is altered, and so every exported function
// is vulnerable. For now, we leave it to consumers to use this
// information as they wish.
names := map[string]bool{}
for _, ei := range res.Calls.Entries {
e := res.Calls.Functions[ei]
if e.PkgPath == pkgs[0].PkgPath {
names[symbolName(e)] = true
}
}
return names, nil
}
func symbolName(fn *vulncheck.FuncNode) string {
if fn.RecvType == "" {
return fn.Name
}
// Remove package path from type.
i := strings.LastIndexByte(fn.RecvType, '.')
if i < 0 {
return fn.RecvType + "." + fn.Name
}
return fn.RecvType[i+1:] + "." + fn.Name
}
func affected(e *osv.Entry, version string) bool {
for _, a := range e.Affected {
if a.Ranges.AffectsSemver(version) {
return true
}
}
return false
}

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright 2021 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 (
"path"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/vulndb/internal/report"
)
func TestExportedFunctions(t *testing.T) {
e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
{
Name: "example.com/m",
Files: map[string]interface{}{
"p/a.go": `
package p
func vuln() {}
func ok() {}
`,
"p/b.go": `
package p
func Exp() { vuln() }
func Trans() { Exp() }
func Fine() { ok() }
`,
},
},
})
defer e.Cleanup()
rc := newReportClient(&report.Report{
Module: "example.com/m",
Package: "example.com/m/p",
Symbols: []string{"vuln"},
})
pkgs, err := loadPackage(e.Config, path.Join(e.Temp(), "m/p"))
if err != nil {
t.Fatal(err)
}
// Clear Module.Dir so vulncheck doesn't think that the module is local and ignore it.
// Set Module.Version so vulncheck doesn't filter it out.
for _, p := range pkgs {
p.Module.Dir = ""
p.Module.Version = "v1.0.0"
}
got, err := exportedFunctions(pkgs, rc)
if err != nil {
t.Fatal(err)
}
want := map[string]bool{"Exp": true, "Trans": true}
if !cmp.Equal(got, want) {
t.Errorf("\ngot\n\t%v\nwant\n\t%v", got, want)
}
}

Просмотреть файл

@ -9,14 +9,18 @@ package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"go/build"
"log"
"os"
"reflect"
"sort"
"strconv"
"strings"
"os"
"golang.org/x/tools/go/packages"
"golang.org/x/vulndb/internal/cvelistrepo"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/gitrepo"
@ -172,14 +176,113 @@ func fix(filename string) (err error) {
if err != nil {
return err
}
fixed := false
if lints := r.Lint(); len(lints) > 0 {
r.Fix()
fixed = true
}
added, err := addExportedReportSymbols(r)
if err != nil {
return err
}
if fixed || added {
return r.Write(filename)
}
return nil
}
func addExportedReportSymbols(r *report.Report) (bool, error) {
if r.Module == "" || len(r.Symbols) == 0 {
return false, nil
}
if len(r.OS) > 0 || len(r.Arch) > 0 {
return false, errors.New("specific GOOS/GOARCH not yet implemented")
}
rc := newReportClient(r)
added, err := addExportedSymbols(r.Module, r.Package, &r.Symbols, rc)
if err != nil {
return false, err
}
for i, ap := range r.AdditionalPackages {
// Need to take pointer from r because r.AdditionalPackages is a slice of values.
a, err := addExportedSymbols(ap.Module, ap.Package, &r.AdditionalPackages[i].Symbols, rc)
if err != nil {
return false, err
}
if a {
added = true
}
}
return added, nil
}
func addExportedSymbols(module, pkgPath string, symbols *[]string, c *reportClient) (added bool, err error) {
defer derrors.Wrap(&err, "addExportedSymbols(%q, %q)", module, pkgPath)
if pkgPath == "" {
pkgPath = module
}
pkgs, err := loadPackage(&packages.Config{}, pkgPath)
if err != nil {
return false, err
}
if len(pkgs) == 0 {
return false, errors.New("no packages found")
}
// First package should match package path and module.
if pkgs[0].PkgPath != pkgPath {
return false, fmt.Errorf("first package had import path %s, wanted %s", pkgs[0].PkgPath, pkgPath)
}
if pm := pkgs[0].Module; pm == nil || pm.Path != module {
return false, fmt.Errorf("got module %v, expected %s", pm, module)
}
newsyms, err := exportedFunctions(pkgs, c)
if err != nil {
return false, err
}
oldsyms := map[string]bool{}
for _, s := range *symbols {
oldsyms[s] = true
}
if reflect.DeepEqual(newsyms, oldsyms) {
return false, nil
}
for _, s := range *symbols {
newsyms[s] = true
}
var newslice []string
for s := range newsyms {
newslice = append(newslice, s)
}
sort.Strings(newslice)
*symbols = newslice
return true, nil
}
// loadPackage loads the package at the given import path, with enough
// information for constructing a call graph.
func loadPackage(cfg *packages.Config, importPath string) ([]*packages.Package, error) {
cfg.Mode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes |
packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps |
packages.NeedModule
cfg.BuildFlags = []string{fmt.Sprintf("-tags=%s", strings.Join(build.Default.BuildTags, ","))}
pkgs, err := packages.Load(cfg, importPath)
if err != nil {
return nil, err
}
var msgs []string
packages.Visit(pkgs, nil, func(pkg *packages.Package) {
for _, err := range pkg.Errors {
msgs = append(msgs, err.Msg)
}
})
if len(msgs) > 0 {
return nil, fmt.Errorf("packages.Load:\n%s", strings.Join(msgs, "\n"))
}
return pkgs, nil
}
func newCVE(filename string) (err error) {
defer derrors.Wrap(&err, "newCVE(%q)", filename)
cve, err := report.ToCVE(filename)

3
go.mod
Просмотреть файл

@ -18,6 +18,7 @@ require (
github.com/google/safehtml v0.0.2
github.com/jba/templatecheck v0.6.0
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
@ -57,7 +58,7 @@ require (
go.opencensus.io v0.23.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351 // indirect

11
go.sum
Просмотреть файл

@ -165,6 +165,7 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@ -225,6 +226,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg=
github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@ -470,6 +473,7 @@ go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzox
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
@ -511,8 +515,11 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps=
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013 h1:pOVOgyxif1oxpIkCWpWqNehz3kSt+/hRyqV+qehuLbo=
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013/go.mod h1:M50CtfS+xv2iy/epuEazynj250ScQ0/DOjcsin9UE8k=
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c h1:9ESGI8ZFZ81F1nPfavAX6U97Zte77knZknUnGTrSS6w=
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c/go.mod h1:HF28XewMFGXG3D7EgmemgILbLRiYH0qjmXOQM5HuF+g=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -682,8 +689,9 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -762,6 +770,7 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/vuln v0.0.0-20211207171702-7209860d2c63/go.mod h1:zIQqHjf9sHpn0TOli6Vdy9mrmuePs9lmnGBRAzECxz0=
golang.org/x/vuln v0.0.0-20211220180837-4e75679f7993 h1:+QWJBIQ63o9opsIqWfZsj+iJheV4Yq2c56nWwh3aP4g=
golang.org/x/vuln v0.0.0-20211220180837-4e75679f7993/go.mod h1:S6B12KDXRRbuVwwScAnv6c9S3pmk/FRmBMcsb2sVcqI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=