Skip to content

Commit 4d71195

Browse files
committed
all: cleanup govulncheck packages
This CL does not add any new code. It simply - renames files and moves code accordingly - makes private those symbols that do not need to be public anymore For golang/go#56042 Change-Id: I827a84540f0c7200f4d68bf59ce1f6a59a52877f Reviewed-on: https://go-review.googlesource.com/c/vuln/+/448775 Run-TryBot: Zvonimir Pavlinovic <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 2a1d7fa commit 4d71195

File tree

8 files changed

+290
-301
lines changed

8 files changed

+290
-301
lines changed

cmd/govulncheck/errors.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,42 @@ import (
1515
)
1616

1717
var (
18-
// ErrErrGoVersionMismatch is used to indicate that there is a mismatch between
18+
// errGoVersionMismatch is used to indicate that there is a mismatch between
1919
// the Go version used to build govulncheck and the one currently on PATH.
20-
ErrGoVersionMismatch = errors.New(`Loading packages failed, possibly due to a mismatch between the Go version
20+
errGoVersionMismatch = errors.New(`Loading packages failed, possibly due to a mismatch between the Go version
2121
used to build govulncheck and the Go version on PATH. Consider rebuilding
2222
govulncheck with the current Go version.`)
2323

24-
// ErrNoGoSum indicates that a go.mod file was not found in this module.
25-
ErrNoGoMod = errors.New(`no go.mod file
24+
// errNoGoSum indicates that a go.mod file was not found in this module.
25+
errNoGoMod = errors.New(`no go.mod file
2626
2727
govulncheck only works Go with modules. Try navigating to your module directory.
2828
Otherwise, run go mod init to make your project a module.
2929
3030
See https://go.dev/doc/modules/managing-dependencies for more information.`)
3131

32-
// ErrNoGoSum indicates that a go.sum file was not found in this module.
33-
ErrNoGoSum = errors.New(`no go.sum file
32+
// errNoGoSum indicates that a go.sum file was not found in this module.
33+
errNoGoSum = errors.New(`no go.sum file
3434
3535
Your module is missing a go.sum file. Try running go mod tidy.
3636
3737
See https://go.dev/doc/modules/managing-dependencies for more information.`)
3838

39-
// ErrNoModVersion indicates that govulncheck cannot access module version information.
40-
ErrNoModVersion = errors.New(`no module version information
39+
// errNoModVersion indicates that govulncheck cannot access module version information.
40+
errNoModVersion = errors.New(`no module version information
4141
4242
This can happen when running govulncheck in GOPATH mode. govulncheck needs module
4343
versions to correctly identify vulnerabilities.
4444
4545
See https://go.dev/doc/modules/managing-dependencies for more information.`)
4646
)
4747

48-
// A PackageError contains errors from loading a set of packages.
49-
type PackageError struct {
48+
// packageError contains errors from loading a set of packages.
49+
type packageError struct {
5050
Errors []packages.Error
5151
}
5252

53-
func (e *PackageError) Error() string {
53+
func (e *packageError) Error() string {
5454
var b strings.Builder
5555
fmt.Fprintln(&b, "Packages contain errors:")
5656
for _, e := range e.Errors {

cmd/govulncheck/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ func doGovulncheck(patterns []string, sourceAnalysis bool) error {
103103
if err != nil {
104104
// Try to provide a meaningful and actionable error message.
105105
if !fileExists(filepath.Join(dir, "go.mod")) {
106-
return ErrNoGoMod
106+
return errNoGoMod
107107
}
108108
if !fileExists(filepath.Join(dir, "go.sum")) {
109-
return ErrNoGoSum
109+
return errNoGoSum
110110
}
111111
if isGoVersionMismatchError(err) {
112-
return fmt.Errorf("%v\n\n%v", ErrGoVersionMismatch, err)
112+
return fmt.Errorf("%v\n\n%v", errGoVersionMismatch, err)
113113
}
114114
return err
115115
}
@@ -181,7 +181,7 @@ func die(format string, args ...interface{}) {
181181

182182
// loadPackages loads the packages matching patterns at dir using build tags
183183
// provided by tagsFlag. Uses load mode needed for vulncheck analysis. If the
184-
// packages contain errors, a PackageError is returned containing a list of
184+
// packages contain errors, a packageError is returned containing a list of
185185
// the errors, along with the packages themselves.
186186
func loadPackages(patterns []string, dir string) ([]*vulncheck.Package, error) {
187187
var buildFlags []string
@@ -205,7 +205,7 @@ func loadPackages(patterns []string, dir string) ([]*vulncheck.Package, error) {
205205
perrs = append(perrs, p.Errors...)
206206
})
207207
if len(perrs) > 0 {
208-
err = &PackageError{perrs}
208+
err = &packageError{perrs}
209209
}
210210
return vpkgs, err
211211
}

internal/govulncheck/inits.go renamed to internal/govulncheck/callstacks.go

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strconv"
1212
"strings"
1313

14+
"golang.org/x/vuln/internal"
1415
"golang.org/x/vuln/vulncheck"
1516
)
1617

@@ -114,24 +115,61 @@ func isInit(f *vulncheck.FuncNode) bool {
114115
return f.Name == "init" || strings.HasPrefix(f.Name, "init#")
115116
}
116117

117-
// pkgMap creates a map from package paths to packages for all pkgs
118-
// and their transitive imports.
119-
func pkgMap(pkgs []*vulncheck.Package) map[string]*vulncheck.Package {
120-
m := make(map[string]*vulncheck.Package)
121-
var visit func(*vulncheck.Package)
122-
visit = func(p *vulncheck.Package) {
123-
if _, ok := m[p.PkgPath]; ok {
124-
return
125-
}
126-
m[p.PkgPath] = p
118+
// summarizeCallStack returns a short description of the call stack.
119+
// It uses one of two forms, depending on what the lowest function F in topPkgs
120+
// calls:
121+
// - If it calls a function V from the vulnerable package, then summarizeCallStack
122+
// returns "F calls V".
123+
// - If it calls a function G in some other package, which eventually calls V,
124+
// it returns "F calls G, which eventually calls V".
125+
//
126+
// If it can't find any of these functions, summarizeCallStack returns the empty string.
127+
func summarizeCallStack(cs CallStack, topPkgs map[string]bool, vulnPkg string) string {
128+
// Find the lowest function in the top packages.
129+
iTop := lowest(cs.Frames, func(e *StackFrame) bool {
130+
return topPkgs[e.PkgPath]
131+
})
132+
if iTop < 0 {
133+
return ""
134+
}
135+
// Find the highest function in the vulnerable package that is below iTop.
136+
iVuln := highest(cs.Frames[iTop+1:], func(e *StackFrame) bool {
137+
return e.PkgPath == vulnPkg
138+
})
139+
if iVuln < 0 {
140+
return ""
141+
}
142+
iVuln += iTop + 1 // adjust for slice in call to highest.
143+
topName := cs.Frames[iTop].Name()
144+
topPos := internal.AbsRelShorter(cs.Frames[iTop].Pos())
145+
if topPos != "" {
146+
topPos += ": "
147+
}
148+
vulnName := cs.Frames[iVuln].Name()
149+
if iVuln == iTop+1 {
150+
return fmt.Sprintf("%s%s calls %s", topPos, topName, vulnName)
151+
}
152+
return fmt.Sprintf("%s%s calls %s, which eventually calls %s",
153+
topPos, topName, cs.Frames[iTop+1].Name(), vulnName)
154+
}
127155

128-
for _, i := range p.Imports {
129-
visit(i)
130-
}
156+
// uniqueCallStack returns the first unique call stack among css, if any.
157+
// Unique means that the call stack does not go through symbols of vg.
158+
func uniqueCallStack(v *vulncheck.Vuln, css []vulncheck.CallStack, vg []*vulncheck.Vuln, r *vulncheck.Result) vulncheck.CallStack {
159+
vulnFuncs := make(map[*vulncheck.FuncNode]bool)
160+
for _, v := range vg {
161+
vulnFuncs[r.Calls.Functions[v.CallSink]] = true
131162
}
132163

133-
for _, p := range pkgs {
134-
visit(p)
164+
vulnFunc := r.Calls.Functions[v.CallSink]
165+
callstack:
166+
for _, cs := range css {
167+
for _, e := range cs {
168+
if e.Function != vulnFunc && vulnFuncs[e.Function] {
169+
continue callstack
170+
}
171+
}
172+
return cs
135173
}
136-
return m
174+
return nil
137175
}

internal/govulncheck/inits_test.go renamed to internal/govulncheck/callstacks_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"path"
1111
"path/filepath"
12+
"strings"
1213
"testing"
1314

1415
"github.com/google/go-cmp/cmp"
@@ -18,6 +19,98 @@ import (
1819
"golang.org/x/vuln/vulncheck"
1920
)
2021

22+
func TestUniqueCallStack(t *testing.T) {
23+
a := &vulncheck.FuncNode{Name: "A"}
24+
b := &vulncheck.FuncNode{Name: "B"}
25+
v1 := &vulncheck.FuncNode{Name: "V1"}
26+
v2 := &vulncheck.FuncNode{Name: "V2"}
27+
v3 := &vulncheck.FuncNode{Name: "V3"}
28+
29+
vuln1 := &vulncheck.Vuln{Symbol: "V1", CallSink: 1}
30+
vuln2 := &vulncheck.Vuln{Symbol: "V2", CallSink: 2}
31+
vuln3 := &vulncheck.Vuln{Symbol: "V3", CallSink: 3}
32+
33+
vr := &vulncheck.Result{
34+
Calls: &vulncheck.CallGraph{
35+
Functions: map[int]*vulncheck.FuncNode{1: v1, 2: v2, 3: v3},
36+
},
37+
Vulns: []*vulncheck.Vuln{vuln1, vuln2, vuln3},
38+
}
39+
40+
callStack := func(fs ...*vulncheck.FuncNode) vulncheck.CallStack {
41+
var cs vulncheck.CallStack
42+
for _, f := range fs {
43+
cs = append(cs, vulncheck.StackEntry{Function: f})
44+
}
45+
return cs
46+
}
47+
48+
// V1, V2, and V3 are vulnerable symbols
49+
skip := []*vulncheck.Vuln{vuln1, vuln2, vuln3}
50+
for _, test := range []struct {
51+
vuln *vulncheck.Vuln
52+
css []vulncheck.CallStack
53+
want vulncheck.CallStack
54+
}{
55+
// [A -> B -> V3 -> V1, A -> V1] ==> A -> V1 since the first stack goes through V3
56+
{vuln1, []vulncheck.CallStack{callStack(a, b, v3, v1), callStack(a, v1)}, callStack(a, v1)},
57+
// [A -> V1 -> V2] ==> nil since the only candidate call stack goes through V1
58+
{vuln2, []vulncheck.CallStack{callStack(a, v1, v2)}, nil},
59+
// [A -> V1 -> V3, A -> B -> v3] ==> A -> B -> V3 since the first stack goes through V1
60+
{vuln3, []vulncheck.CallStack{callStack(a, v1, v3), callStack(a, b, v3)}, callStack(a, b, v3)},
61+
} {
62+
t.Run(test.vuln.Symbol, func(t *testing.T) {
63+
got := uniqueCallStack(test.vuln, test.css, skip, vr)
64+
if diff := cmp.Diff(test.want, got); diff != "" {
65+
t.Fatalf("mismatch (-want, +got):\n%s", diff)
66+
}
67+
})
68+
}
69+
}
70+
71+
func TestSummarizeCallStack(t *testing.T) {
72+
topPkgs := map[string]bool{"t1": true, "t2": true}
73+
vulnPkg := "v"
74+
75+
for _, test := range []struct {
76+
in, want string
77+
}{
78+
{"a.F", ""},
79+
{"t1.F", ""},
80+
{"v.V", ""},
81+
{
82+
"t1.F v.V",
83+
"t1.F calls v.V",
84+
},
85+
{
86+
"t1.F t2.G v.V1 v.v2",
87+
"t2.G calls v.V1",
88+
},
89+
{
90+
"t1.F x.Y t2.G a.H b.I c.J v.V",
91+
"t2.G calls a.H, which eventually calls v.V",
92+
},
93+
} {
94+
in := stringToCallStack(test.in)
95+
got := summarizeCallStack(in, topPkgs, vulnPkg)
96+
if got != test.want {
97+
t.Errorf("%s:\ngot %s\nwant %s", test.in, got, test.want)
98+
}
99+
}
100+
}
101+
102+
func stringToCallStack(s string) CallStack {
103+
var cs CallStack
104+
for _, e := range strings.Fields(s) {
105+
parts := strings.Split(e, ".")
106+
cs.Frames = append(cs.Frames, &StackFrame{
107+
PkgPath: parts[0],
108+
FuncName: parts[1],
109+
})
110+
}
111+
return cs
112+
}
113+
21114
// TestInits checks for correct positions of init functions
22115
// and their respective calls (see #51575).
23116
func TestInits(t *testing.T) {

internal/govulncheck/run.go

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func createSourceResult(vr *vulncheck.Result, pkgs []*vulncheck.Package) *Result
9494
Frames: stackFramesfromEntries(vcs),
9595
Symbol: vv.Symbol,
9696
}
97-
cs.Summary = SummarizeCallStack(cs, topPkgs, p.Path)
97+
cs.Summary = summarizeCallStack(cs, topPkgs, p.Path)
9898
p.CallStacks = []CallStack{cs}
9999
}
100100
}
@@ -253,37 +253,3 @@ func stackFramesfromEntries(vcs vulncheck.CallStack) []*StackFrame {
253253
}
254254
return frames
255255
}
256-
257-
// uniqueCallStack returns the first unique call stack among css, if any.
258-
// Unique means that the call stack does not go through symbols of vg.
259-
func uniqueCallStack(v *vulncheck.Vuln, css []vulncheck.CallStack, vg []*vulncheck.Vuln, r *vulncheck.Result) vulncheck.CallStack {
260-
vulnFuncs := make(map[*vulncheck.FuncNode]bool)
261-
for _, v := range vg {
262-
vulnFuncs[r.Calls.Functions[v.CallSink]] = true
263-
}
264-
265-
vulnFunc := r.Calls.Functions[v.CallSink]
266-
callstack:
267-
for _, cs := range css {
268-
for _, e := range cs {
269-
if e.Function != vulnFunc && vulnFuncs[e.Function] {
270-
continue callstack
271-
}
272-
}
273-
return cs
274-
}
275-
return nil
276-
}
277-
278-
// moduleVersionMap builds a map from module paths to versions.
279-
func moduleVersionMap(mods []*vulncheck.Module) map[string]string {
280-
moduleVersions := map[string]string{}
281-
for _, m := range mods {
282-
v := m.Version
283-
if m.Replace != nil {
284-
v = m.Replace.Version
285-
}
286-
moduleVersions[m.Path] = v
287-
}
288-
return moduleVersions
289-
}

0 commit comments

Comments
 (0)