blob: d4af13335c36e1a96202a519ae4874c84b238af0 [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 govulncheck
import (
"strings"
"golang.org/x/mod/semver"
"golang.org/x/vuln/internal"
"golang.org/x/vuln/internal/result"
isem "golang.org/x/vuln/internal/semver"
"golang.org/x/vuln/internal/vulncheck"
"golang.org/x/vuln/osv"
)
// latestFixed returns the latest fixed version in the list of affected ranges,
// or the empty string if there are no fixed versions.
func latestFixed(as []osv.Affected) string {
v := ""
for _, a := range as {
for _, r := range a.Ranges {
if r.Type == osv.TypeSemver {
for _, e := range r.Events {
if e.Fixed != "" && (v == "" ||
semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) {
v = e.Fixed
}
}
}
}
}
return v
}
func foundVersion(modulePath string, moduleVersions map[string]string) string {
var found string
if v := moduleVersions[modulePath]; v != "" {
found = versionString(modulePath, v[1:])
}
return found
}
func fixedVersion(modulePath string, affected []osv.Affected) string {
fixed := latestFixed(affected)
if fixed != "" {
fixed = versionString(modulePath, fixed)
}
return fixed
}
// versionString prepends a version string prefix (`v` or `go`
// depending on the modulePath) to the given semver-style version string.
func versionString(modulePath, version string) string {
if version == "" {
return ""
}
v := "v" + version
if modulePath == internal.GoStdModulePath || modulePath == internal.GoCmdModulePath {
return semverToGoTag(v)
}
return v
}
// highest returns the highest (one with the smallest index) entry in the call
// stack for which f returns true.
func highest(cs []*result.StackFrame, f func(e *result.StackFrame) bool) int {
for i := 0; i < len(cs); i++ {
if f(cs[i]) {
return i
}
}
return -1
}
// lowest returns the lowest (one with the largest index) entry in the call
// stack for which f returns true.
func lowest(cs []*result.StackFrame, f func(e *result.StackFrame) bool) int {
for i := len(cs) - 1; i >= 0; i-- {
if f(cs[i]) {
return i
}
}
return -1
}
// pkgPath returns the package path from fn.
func pkgPath(fn *vulncheck.FuncNode) string {
if fn.PkgPath != "" {
return fn.PkgPath
}
s := strings.TrimPrefix(fn.RecvType, "*")
if i := strings.LastIndexByte(s, '.'); i > 0 {
s = s[:i]
}
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
}