blob: c7f080581377e34498a3261338f920359d897ab6 [file] [log] [blame]
// 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 vulncheck
import (
"context"
"fmt"
"sort"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/vuln/client"
"golang.org/x/vuln/osv"
)
type mockClient struct {
client.Client
ret map[string][]*osv.Entry
}
func (mc *mockClient) GetByModule(ctx context.Context, a string) ([]*osv.Entry, error) {
return mc.ret[a], nil
}
// testClient contains the following test vulnerabilities
//
// golang.org/amod/avuln.{VulnData.Vuln1, vulnData.Vuln2}
// golang.org/bmod/bvuln.{Vuln}
var testClient = &mockClient{
ret: map[string][]*osv.Entry{
"golang.org/amod": []*osv.Entry{
{
ID: "VA",
Affected: []osv.Affected{{
Package: osv.Package{Name: "golang.org/amod/avuln"},
Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "1.0.4"}, {Introduced: "1.1.2"}}}},
EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"VulnData.Vuln1", "VulnData.Vuln2"}},
}},
},
},
"golang.org/bmod": []*osv.Entry{
{
ID: "VB",
Affected: []osv.Affected{{
Package: osv.Package{Name: "golang.org/bmod/bvuln"},
Ranges: osv.Affects{{Type: osv.TypeSemver}},
EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"Vuln"}},
}},
},
},
},
}
func moduleVulnerabilitiesToString(mv moduleVulnerabilities) string {
var s string
for _, m := range mv {
s += fmt.Sprintf("mod: %v\n", m.mod)
for _, v := range m.vulns {
s += fmt.Sprintf("\t%v\n", v)
}
}
return s
}
func vulnsToString(vulns []*osv.Entry) string {
var s string
for _, v := range vulns {
s += fmt.Sprintf("\t%v\n", v)
}
return s
}
func impGraphToStrMap(ig *ImportGraph) map[string][]string {
m := make(map[string][]string)
for _, n := range ig.Packages {
for _, predId := range n.ImportedBy {
pred := ig.Packages[predId]
m[pred.Path] = append(m[pred.Path], n.Path)
}
}
sortStrMap(m)
return m
}
func reqGraphToStrMap(rg *RequireGraph) map[string][]string {
m := make(map[string][]string)
for _, n := range rg.Modules {
for _, predId := range n.RequiredBy {
pred := rg.Modules[predId]
m[pred.Path] = append(m[pred.Path], n.Path)
}
}
sortStrMap(m)
return m
}
func callGraphToStrMap(cg *CallGraph) map[string][]string {
type edge struct {
// src and dest are ids ofr source and
// destination nodes in a callgraph edge.
src, dst int
}
// seen edges, to avoid repetitions
seen := make(map[edge]bool)
m := make(map[string][]string)
for _, n := range cg.Functions {
fName := n.String()
for _, callsite := range n.CallSites {
e := edge{src: callsite.Parent, dst: n.ID}
if seen[e] {
continue
}
caller := cg.Functions[e.src]
callerName := caller.String()
m[callerName] = append(m[callerName], fName)
}
}
sortStrMap(m)
return m
}
func pkgPathToImports(pkgs []*Package) map[string][]string {
m := make(map[string][]string)
seen := make(map[*Package]bool)
var visit func(*Package)
visit = func(p *Package) {
if seen[p] {
return
}
seen[p] = true
var imports []string
for _, i := range p.Imports {
imports = append(imports, i.PkgPath)
visit(i)
}
m[p.PkgPath] = imports
}
for _, p := range pkgs {
visit(p)
}
sortStrMap(m)
return m
}
func modulePathToVersion(pkgs []*Package) map[string]string {
m := make(map[string]string)
seen := make(map[*Package]bool)
var visit func(*Package)
visit = func(p *Package) {
if seen[p] || p.Module == nil {
return
}
seen[p] = true
for _, i := range p.Imports {
visit(i)
}
m[p.Module.Path] = p.Module.Version
}
for _, p := range pkgs {
visit(p)
}
return m
}
// sortStrMap sorts the map string slice values to make them deterministic.
func sortStrMap(m map[string][]string) {
for _, strs := range m {
sort.Strings(strs)
}
}
func loadPackages(e *packagestest.Exported, patterns ...string) ([]*packages.Package, error) {
e.Config.Mode |= packages.NeedModule | packages.NeedName | packages.NeedFiles |
packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes |
packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps
return packages.Load(e.Config, patterns...)
}