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 <zpavlinovic@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/govulncheck/errors.go b/cmd/govulncheck/errors.go
index 04fe666..d6506e0 100644
--- a/cmd/govulncheck/errors.go
+++ b/cmd/govulncheck/errors.go
@@ -15,29 +15,29 @@
)
var (
- // ErrErrGoVersionMismatch is used to indicate that there is a mismatch between
+ // errGoVersionMismatch is used to indicate that there is a mismatch between
// the Go version used to build govulncheck and the one currently on PATH.
- ErrGoVersionMismatch = errors.New(`Loading packages failed, possibly due to a mismatch between the Go version
+ errGoVersionMismatch = errors.New(`Loading packages failed, possibly due to a mismatch between the Go version
used to build govulncheck and the Go version on PATH. Consider rebuilding
govulncheck with the current Go version.`)
- // ErrNoGoSum indicates that a go.mod file was not found in this module.
- ErrNoGoMod = errors.New(`no go.mod file
+ // errNoGoSum indicates that a go.mod file was not found in this module.
+ errNoGoMod = errors.New(`no go.mod file
govulncheck only works Go with modules. Try navigating to your module directory.
Otherwise, run go mod init to make your project a module.
See https://go.dev/doc/modules/managing-dependencies for more information.`)
- // ErrNoGoSum indicates that a go.sum file was not found in this module.
- ErrNoGoSum = errors.New(`no go.sum file
+ // errNoGoSum indicates that a go.sum file was not found in this module.
+ errNoGoSum = errors.New(`no go.sum file
Your module is missing a go.sum file. Try running go mod tidy.
See https://go.dev/doc/modules/managing-dependencies for more information.`)
- // ErrNoModVersion indicates that govulncheck cannot access module version information.
- ErrNoModVersion = errors.New(`no module version information
+ // errNoModVersion indicates that govulncheck cannot access module version information.
+ errNoModVersion = errors.New(`no module version information
This can happen when running govulncheck in GOPATH mode. govulncheck needs module
versions to correctly identify vulnerabilities.
@@ -45,12 +45,12 @@
See https://go.dev/doc/modules/managing-dependencies for more information.`)
)
-// A PackageError contains errors from loading a set of packages.
-type PackageError struct {
+// packageError contains errors from loading a set of packages.
+type packageError struct {
Errors []packages.Error
}
-func (e *PackageError) Error() string {
+func (e *packageError) Error() string {
var b strings.Builder
fmt.Fprintln(&b, "Packages contain errors:")
for _, e := range e.Errors {
diff --git a/cmd/govulncheck/main.go b/cmd/govulncheck/main.go
index 6f06b25..1df8011 100644
--- a/cmd/govulncheck/main.go
+++ b/cmd/govulncheck/main.go
@@ -103,13 +103,13 @@
if err != nil {
// Try to provide a meaningful and actionable error message.
if !fileExists(filepath.Join(dir, "go.mod")) {
- return ErrNoGoMod
+ return errNoGoMod
}
if !fileExists(filepath.Join(dir, "go.sum")) {
- return ErrNoGoSum
+ return errNoGoSum
}
if isGoVersionMismatchError(err) {
- return fmt.Errorf("%v\n\n%v", ErrGoVersionMismatch, err)
+ return fmt.Errorf("%v\n\n%v", errGoVersionMismatch, err)
}
return err
}
@@ -181,7 +181,7 @@
// loadPackages loads the packages matching patterns at dir using build tags
// provided by tagsFlag. Uses load mode needed for vulncheck analysis. If the
-// packages contain errors, a PackageError is returned containing a list of
+// packages contain errors, a packageError is returned containing a list of
// the errors, along with the packages themselves.
func loadPackages(patterns []string, dir string) ([]*vulncheck.Package, error) {
var buildFlags []string
@@ -205,7 +205,7 @@
perrs = append(perrs, p.Errors...)
})
if len(perrs) > 0 {
- err = &PackageError{perrs}
+ err = &packageError{perrs}
}
return vpkgs, err
}
diff --git a/internal/govulncheck/inits.go b/internal/govulncheck/callstacks.go
similarity index 64%
rename from internal/govulncheck/inits.go
rename to internal/govulncheck/callstacks.go
index ef18b05..f97e1bc 100644
--- a/internal/govulncheck/inits.go
+++ b/internal/govulncheck/callstacks.go
@@ -11,6 +11,7 @@
"strconv"
"strings"
+ "golang.org/x/vuln/internal"
"golang.org/x/vuln/vulncheck"
)
@@ -114,24 +115,61 @@
return f.Name == "init" || strings.HasPrefix(f.Name, "init#")
}
-// pkgMap creates a map from package paths to packages for all pkgs
-// and their transitive imports.
-func pkgMap(pkgs []*vulncheck.Package) map[string]*vulncheck.Package {
- m := make(map[string]*vulncheck.Package)
- var visit func(*vulncheck.Package)
- visit = func(p *vulncheck.Package) {
- if _, ok := m[p.PkgPath]; ok {
- return
- }
- m[p.PkgPath] = p
+// summarizeCallStack returns a short description of the call stack.
+// It uses one of two forms, depending on what the lowest function F in topPkgs
+// calls:
+// - If it calls a function V from the vulnerable package, then summarizeCallStack
+// returns "F calls V".
+// - If it calls a function G in some other package, which eventually calls V,
+// it returns "F calls G, which eventually calls V".
+//
+// If it can't find any of these functions, summarizeCallStack returns the empty string.
+func summarizeCallStack(cs CallStack, topPkgs map[string]bool, vulnPkg string) string {
+ // Find the lowest function in the top packages.
+ iTop := lowest(cs.Frames, func(e *StackFrame) bool {
+ return topPkgs[e.PkgPath]
+ })
+ if iTop < 0 {
+ return ""
+ }
+ // Find the highest function in the vulnerable package that is below iTop.
+ iVuln := highest(cs.Frames[iTop+1:], func(e *StackFrame) bool {
+ return e.PkgPath == vulnPkg
+ })
+ if iVuln < 0 {
+ return ""
+ }
+ iVuln += iTop + 1 // adjust for slice in call to highest.
+ topName := cs.Frames[iTop].Name()
+ topPos := internal.AbsRelShorter(cs.Frames[iTop].Pos())
+ if topPos != "" {
+ topPos += ": "
+ }
+ vulnName := cs.Frames[iVuln].Name()
+ if iVuln == iTop+1 {
+ return fmt.Sprintf("%s%s calls %s", topPos, topName, vulnName)
+ }
+ return fmt.Sprintf("%s%s calls %s, which eventually calls %s",
+ topPos, topName, cs.Frames[iTop+1].Name(), vulnName)
+}
- for _, i := range p.Imports {
- visit(i)
- }
+// uniqueCallStack returns the first unique call stack among css, if any.
+// Unique means that the call stack does not go through symbols of vg.
+func uniqueCallStack(v *vulncheck.Vuln, css []vulncheck.CallStack, vg []*vulncheck.Vuln, r *vulncheck.Result) vulncheck.CallStack {
+ vulnFuncs := make(map[*vulncheck.FuncNode]bool)
+ for _, v := range vg {
+ vulnFuncs[r.Calls.Functions[v.CallSink]] = true
}
- for _, p := range pkgs {
- visit(p)
+ vulnFunc := r.Calls.Functions[v.CallSink]
+callstack:
+ for _, cs := range css {
+ for _, e := range cs {
+ if e.Function != vulnFunc && vulnFuncs[e.Function] {
+ continue callstack
+ }
+ }
+ return cs
}
- return m
+ return nil
}
diff --git a/internal/govulncheck/inits_test.go b/internal/govulncheck/callstacks_test.go
similarity index 63%
rename from internal/govulncheck/inits_test.go
rename to internal/govulncheck/callstacks_test.go
index 29134a0..8e6aef8 100644
--- a/internal/govulncheck/inits_test.go
+++ b/internal/govulncheck/callstacks_test.go
@@ -9,6 +9,7 @@
"fmt"
"path"
"path/filepath"
+ "strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -18,6 +19,98 @@
"golang.org/x/vuln/vulncheck"
)
+func TestUniqueCallStack(t *testing.T) {
+ a := &vulncheck.FuncNode{Name: "A"}
+ b := &vulncheck.FuncNode{Name: "B"}
+ v1 := &vulncheck.FuncNode{Name: "V1"}
+ v2 := &vulncheck.FuncNode{Name: "V2"}
+ v3 := &vulncheck.FuncNode{Name: "V3"}
+
+ vuln1 := &vulncheck.Vuln{Symbol: "V1", CallSink: 1}
+ vuln2 := &vulncheck.Vuln{Symbol: "V2", CallSink: 2}
+ vuln3 := &vulncheck.Vuln{Symbol: "V3", CallSink: 3}
+
+ vr := &vulncheck.Result{
+ Calls: &vulncheck.CallGraph{
+ Functions: map[int]*vulncheck.FuncNode{1: v1, 2: v2, 3: v3},
+ },
+ Vulns: []*vulncheck.Vuln{vuln1, vuln2, vuln3},
+ }
+
+ callStack := func(fs ...*vulncheck.FuncNode) vulncheck.CallStack {
+ var cs vulncheck.CallStack
+ for _, f := range fs {
+ cs = append(cs, vulncheck.StackEntry{Function: f})
+ }
+ return cs
+ }
+
+ // V1, V2, and V3 are vulnerable symbols
+ skip := []*vulncheck.Vuln{vuln1, vuln2, vuln3}
+ for _, test := range []struct {
+ vuln *vulncheck.Vuln
+ css []vulncheck.CallStack
+ want vulncheck.CallStack
+ }{
+ // [A -> B -> V3 -> V1, A -> V1] ==> A -> V1 since the first stack goes through V3
+ {vuln1, []vulncheck.CallStack{callStack(a, b, v3, v1), callStack(a, v1)}, callStack(a, v1)},
+ // [A -> V1 -> V2] ==> nil since the only candidate call stack goes through V1
+ {vuln2, []vulncheck.CallStack{callStack(a, v1, v2)}, nil},
+ // [A -> V1 -> V3, A -> B -> v3] ==> A -> B -> V3 since the first stack goes through V1
+ {vuln3, []vulncheck.CallStack{callStack(a, v1, v3), callStack(a, b, v3)}, callStack(a, b, v3)},
+ } {
+ t.Run(test.vuln.Symbol, func(t *testing.T) {
+ got := uniqueCallStack(test.vuln, test.css, skip, vr)
+ if diff := cmp.Diff(test.want, got); diff != "" {
+ t.Fatalf("mismatch (-want, +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestSummarizeCallStack(t *testing.T) {
+ topPkgs := map[string]bool{"t1": true, "t2": true}
+ vulnPkg := "v"
+
+ for _, test := range []struct {
+ in, want string
+ }{
+ {"a.F", ""},
+ {"t1.F", ""},
+ {"v.V", ""},
+ {
+ "t1.F v.V",
+ "t1.F calls v.V",
+ },
+ {
+ "t1.F t2.G v.V1 v.v2",
+ "t2.G calls v.V1",
+ },
+ {
+ "t1.F x.Y t2.G a.H b.I c.J v.V",
+ "t2.G calls a.H, which eventually calls v.V",
+ },
+ } {
+ in := stringToCallStack(test.in)
+ got := summarizeCallStack(in, topPkgs, vulnPkg)
+ if got != test.want {
+ t.Errorf("%s:\ngot %s\nwant %s", test.in, got, test.want)
+ }
+ }
+}
+
+func stringToCallStack(s string) CallStack {
+ var cs CallStack
+ for _, e := range strings.Fields(s) {
+ parts := strings.Split(e, ".")
+ cs.Frames = append(cs.Frames, &StackFrame{
+ PkgPath: parts[0],
+ FuncName: parts[1],
+ })
+ }
+ return cs
+}
+
// TestInits checks for correct positions of init functions
// and their respective calls (see #51575).
func TestInits(t *testing.T) {
diff --git a/internal/govulncheck/run.go b/internal/govulncheck/run.go
index dc5c962..bfdfaf1 100644
--- a/internal/govulncheck/run.go
+++ b/internal/govulncheck/run.go
@@ -94,7 +94,7 @@
Frames: stackFramesfromEntries(vcs),
Symbol: vv.Symbol,
}
- cs.Summary = SummarizeCallStack(cs, topPkgs, p.Path)
+ cs.Summary = summarizeCallStack(cs, topPkgs, p.Path)
p.CallStacks = []CallStack{cs}
}
}
@@ -253,37 +253,3 @@
}
return frames
}
-
-// uniqueCallStack returns the first unique call stack among css, if any.
-// Unique means that the call stack does not go through symbols of vg.
-func uniqueCallStack(v *vulncheck.Vuln, css []vulncheck.CallStack, vg []*vulncheck.Vuln, r *vulncheck.Result) vulncheck.CallStack {
- vulnFuncs := make(map[*vulncheck.FuncNode]bool)
- for _, v := range vg {
- vulnFuncs[r.Calls.Functions[v.CallSink]] = true
- }
-
- vulnFunc := r.Calls.Functions[v.CallSink]
-callstack:
- for _, cs := range css {
- for _, e := range cs {
- if e.Function != vulnFunc && vulnFuncs[e.Function] {
- continue callstack
- }
- }
- return cs
- }
- return nil
-}
-
-// moduleVersionMap builds a map from module paths to versions.
-func moduleVersionMap(mods []*vulncheck.Module) map[string]string {
- moduleVersions := map[string]string{}
- for _, m := range mods {
- v := m.Version
- if m.Replace != nil {
- v = m.Replace.Version
- }
- moduleVersions[m.Path] = v
- }
- return moduleVersions
-}
diff --git a/internal/govulncheck/run_test.go b/internal/govulncheck/run_test.go
index bd93c57..0fabf89 100644
--- a/internal/govulncheck/run_test.go
+++ b/internal/govulncheck/run_test.go
@@ -4,155 +4,4 @@
package govulncheck
-import (
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "golang.org/x/vuln/osv"
- "golang.org/x/vuln/vulncheck"
-)
-
-func TestLatestFixed(t *testing.T) {
- for _, test := range []struct {
- name string
- in []osv.Affected
- want string
- }{
- {"empty", nil, ""},
- {
- "no semver",
- []osv.Affected{
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeGit,
- Events: []osv.RangeEvent{
- {Introduced: "v1.0.0", Fixed: "v1.2.3"},
- },
- }},
- },
- },
- "",
- },
- {
- "one",
- []osv.Affected{
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeSemver,
- Events: []osv.RangeEvent{
- {Introduced: "v1.0.0", Fixed: "v1.2.3"},
- },
- }},
- },
- },
- "v1.2.3",
- },
- {
- "several",
- []osv.Affected{
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeSemver,
- Events: []osv.RangeEvent{
- {Introduced: "v1.0.0", Fixed: "v1.2.3"},
- {Introduced: "v1.5.0", Fixed: "v1.5.6"},
- },
- }},
- },
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeSemver,
- Events: []osv.RangeEvent{
- {Introduced: "v1.3.0", Fixed: "v1.4.1"},
- },
- }},
- },
- },
- "v1.5.6",
- },
- {
- "no v prefix",
- []osv.Affected{
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeSemver,
- Events: []osv.RangeEvent{
- {Fixed: "1.17.2"},
- },
- }},
- },
- {
- Ranges: osv.Affects{
- {
- Type: osv.TypeSemver,
- Events: []osv.RangeEvent{
- {Introduced: "1.18.0", Fixed: "1.18.4"},
- },
- }},
- },
- },
- "1.18.4",
- },
- } {
- t.Run(test.name, func(t *testing.T) {
- got := LatestFixed(test.in)
- if got != test.want {
- t.Errorf("got %q, want %q", got, test.want)
- }
- })
- }
-}
-
-func TestUniqueCallStack(t *testing.T) {
- a := &vulncheck.FuncNode{Name: "A"}
- b := &vulncheck.FuncNode{Name: "B"}
- v1 := &vulncheck.FuncNode{Name: "V1"}
- v2 := &vulncheck.FuncNode{Name: "V2"}
- v3 := &vulncheck.FuncNode{Name: "V3"}
-
- vuln1 := &vulncheck.Vuln{Symbol: "V1", CallSink: 1}
- vuln2 := &vulncheck.Vuln{Symbol: "V2", CallSink: 2}
- vuln3 := &vulncheck.Vuln{Symbol: "V3", CallSink: 3}
-
- vr := &vulncheck.Result{
- Calls: &vulncheck.CallGraph{
- Functions: map[int]*vulncheck.FuncNode{1: v1, 2: v2, 3: v3},
- },
- Vulns: []*vulncheck.Vuln{vuln1, vuln2, vuln3},
- }
-
- callStack := func(fs ...*vulncheck.FuncNode) vulncheck.CallStack {
- var cs vulncheck.CallStack
- for _, f := range fs {
- cs = append(cs, vulncheck.StackEntry{Function: f})
- }
- return cs
- }
-
- // V1, V2, and V3 are vulnerable symbols
- skip := []*vulncheck.Vuln{vuln1, vuln2, vuln3}
- for _, test := range []struct {
- vuln *vulncheck.Vuln
- css []vulncheck.CallStack
- want vulncheck.CallStack
- }{
- // [A -> B -> V3 -> V1, A -> V1] ==> A -> V1 since the first stack goes through V3
- {vuln1, []vulncheck.CallStack{callStack(a, b, v3, v1), callStack(a, v1)}, callStack(a, v1)},
- // [A -> V1 -> V2] ==> nil since the only candidate call stack goes through V1
- {vuln2, []vulncheck.CallStack{callStack(a, v1, v2)}, nil},
- // [A -> V1 -> V3, A -> B -> v3] ==> A -> B -> V3 since the first stack goes through V1
- {vuln3, []vulncheck.CallStack{callStack(a, v1, v3), callStack(a, b, v3)}, callStack(a, b, v3)},
- } {
- t.Run(test.vuln.Symbol, func(t *testing.T) {
- got := uniqueCallStack(test.vuln, test.css, skip, vr)
- if diff := cmp.Diff(test.want, got); diff != "" {
- t.Fatalf("mismatch (-want, +got):\n%s", diff)
- }
- })
- }
-}
+// TODO: add tests
diff --git a/internal/govulncheck/util.go b/internal/govulncheck/util.go
index f713444..aba3adb 100644
--- a/internal/govulncheck/util.go
+++ b/internal/govulncheck/util.go
@@ -5,7 +5,6 @@
package govulncheck
import (
- "fmt"
"strings"
"golang.org/x/mod/semver"
@@ -15,11 +14,9 @@
"golang.org/x/vuln/vulncheck"
)
-// LatestFixed returns the latest fixed version in the list of affected ranges,
+// latestFixed returns the latest fixed version in the list of affected ranges,
// or the empty string if there are no fixed versions.
-//
-// TODO: make private
-func LatestFixed(as []osv.Affected) string {
+func latestFixed(as []osv.Affected) string {
v := ""
for _, a := range as {
for _, r := range a.Ranges {
@@ -45,7 +42,7 @@
}
func fixedVersion(modulePath string, affected []osv.Affected) string {
- fixed := LatestFixed(affected)
+ fixed := latestFixed(affected)
if fixed != "" {
fixed = versionString(modulePath, fixed)
}
@@ -65,46 +62,6 @@
return v
}
-// SummarizeCallStack returns a short description of the call stack.
-// It uses one of two forms, depending on what the lowest function F in topPkgs
-// calls:
-// - If it calls a function V from the vulnerable package, then summarizeCallStack
-// returns "F calls V".
-// - If it calls a function G in some other package, which eventually calls V,
-// it returns "F calls G, which eventually calls V".
-//
-// If it can't find any of these functions, summarizeCallStack returns the empty string.
-//
-// TODO: make private
-func SummarizeCallStack(cs CallStack, topPkgs map[string]bool, vulnPkg string) string {
- // Find the lowest function in the top packages.
- iTop := lowest(cs.Frames, func(e *StackFrame) bool {
- return topPkgs[e.PkgPath]
- })
- if iTop < 0 {
- return ""
- }
- // Find the highest function in the vulnerable package that is below iTop.
- iVuln := highest(cs.Frames[iTop+1:], func(e *StackFrame) bool {
- return e.PkgPath == vulnPkg
- })
- if iVuln < 0 {
- return ""
- }
- iVuln += iTop + 1 // adjust for slice in call to highest.
- topName := cs.Frames[iTop].Name()
- topPos := internal.AbsRelShorter(cs.Frames[iTop].Pos())
- if topPos != "" {
- topPos += ": "
- }
- vulnName := cs.Frames[iVuln].Name()
- if iVuln == iTop+1 {
- return fmt.Sprintf("%s%s calls %s", topPos, topName, vulnName)
- }
- return fmt.Sprintf("%s%s calls %s, which eventually calls %s",
- topPos, topName, cs.Frames[iTop+1].Name(), vulnName)
-}
-
// highest returns the highest (one with the smallest index) entry in the call
// stack for which f returns true.
func highest(cs []*StackFrame, f func(e *StackFrame) bool) int {
@@ -127,10 +84,8 @@
return -1
}
-// PkgPath returns the package path from fn.
-//
-// TODO: make private
-func PkgPath(fn *vulncheck.FuncNode) string {
+// pkgPath returns the package path from fn.
+func pkgPath(fn *vulncheck.FuncNode) string {
if fn.PkgPath != "" {
return fn.PkgPath
}
@@ -140,3 +95,38 @@
}
return s
}
+
+// moduleVersionMap builds a map from module paths to versions.
+func moduleVersionMap(mods []*vulncheck.Module) map[string]string {
+ moduleVersions := map[string]string{}
+ for _, m := range mods {
+ v := m.Version
+ if m.Replace != nil {
+ v = m.Replace.Version
+ }
+ moduleVersions[m.Path] = v
+ }
+ return moduleVersions
+}
+
+// pkgMap creates a map from package paths to packages for all pkgs
+// and their transitive imports.
+func pkgMap(pkgs []*vulncheck.Package) map[string]*vulncheck.Package {
+ m := make(map[string]*vulncheck.Package)
+ var visit func(*vulncheck.Package)
+ visit = func(p *vulncheck.Package) {
+ if _, ok := m[p.PkgPath]; ok {
+ return
+ }
+ m[p.PkgPath] = p
+
+ for _, i := range p.Imports {
+ visit(i)
+ }
+ }
+
+ for _, p := range pkgs {
+ visit(p)
+ }
+ return m
+}
diff --git a/internal/govulncheck/util_test.go b/internal/govulncheck/util_test.go
index 6bd4c46..c2a8711 100644
--- a/internal/govulncheck/util_test.go
+++ b/internal/govulncheck/util_test.go
@@ -5,9 +5,9 @@
package govulncheck
import (
- "strings"
"testing"
+ "golang.org/x/vuln/osv"
"golang.org/x/vuln/vulncheck"
)
@@ -29,52 +29,105 @@
"a.com/b",
},
} {
- got := PkgPath(&test.in)
+ got := pkgPath(&test.in)
if got != test.want {
t.Errorf("%+v: got %q, want %q", test.in, got, test.want)
}
}
}
-func TestSummarizeCallStack(t *testing.T) {
- topPkgs := map[string]bool{"t1": true, "t2": true}
- vulnPkg := "v"
-
+func TestLatestFixed(t *testing.T) {
for _, test := range []struct {
- in, want string
+ name string
+ in []osv.Affected
+ want string
}{
- {"a.F", ""},
- {"t1.F", ""},
- {"v.V", ""},
+ {"empty", nil, ""},
{
- "t1.F v.V",
- "t1.F calls v.V",
+ "no semver",
+ []osv.Affected{
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeGit,
+ Events: []osv.RangeEvent{
+ {Introduced: "v1.0.0", Fixed: "v1.2.3"},
+ },
+ }},
+ },
+ },
+ "",
},
{
- "t1.F t2.G v.V1 v.v2",
- "t2.G calls v.V1",
+ "one",
+ []osv.Affected{
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Introduced: "v1.0.0", Fixed: "v1.2.3"},
+ },
+ }},
+ },
+ },
+ "v1.2.3",
},
{
- "t1.F x.Y t2.G a.H b.I c.J v.V",
- "t2.G calls a.H, which eventually calls v.V",
+ "several",
+ []osv.Affected{
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Introduced: "v1.0.0", Fixed: "v1.2.3"},
+ {Introduced: "v1.5.0", Fixed: "v1.5.6"},
+ },
+ }},
+ },
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Introduced: "v1.3.0", Fixed: "v1.4.1"},
+ },
+ }},
+ },
+ },
+ "v1.5.6",
+ },
+ {
+ "no v prefix",
+ []osv.Affected{
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Fixed: "1.17.2"},
+ },
+ }},
+ },
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Introduced: "1.18.0", Fixed: "1.18.4"},
+ },
+ }},
+ },
+ },
+ "1.18.4",
},
} {
- in := stringToCallStack(test.in)
- got := SummarizeCallStack(in, topPkgs, vulnPkg)
- if got != test.want {
- t.Errorf("%s:\ngot %s\nwant %s", test.in, got, test.want)
- }
- }
-}
-
-func stringToCallStack(s string) CallStack {
- var cs CallStack
- for _, e := range strings.Fields(s) {
- parts := strings.Split(e, ".")
- cs.Frames = append(cs.Frames, &StackFrame{
- PkgPath: parts[0],
- FuncName: parts[1],
+ t.Run(test.name, func(t *testing.T) {
+ got := latestFixed(test.in)
+ if got != test.want {
+ t.Errorf("got %q, want %q", got, test.want)
+ }
})
}
- return cs
}