blob: 3a28f5d2eb97e67a7fe33680d4a4a5c4fdb01544 [file] [log] [blame]
// Copyright 2023 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 (
"fmt"
"runtime/debug"
"strings"
"time"
"golang.org/x/vuln/internal/vulncheck"
)
// compact replaces consecutive runs of equal elements with a single copy.
// This is like the uniq command found on Unix.
// compact modifies the contents of the slice s; it does not create a new slice.
//
// Modified (generics removed) from exp/slices/slices.go.
func compact(s []string) []string {
if len(s) == 0 {
return s
}
i := 1
last := s[0]
for _, v := range s[1:] {
if v != last {
s[i] = v
i++
last = v
}
}
return s[:i]
}
func moduleVersionString(modulePath, version string) string {
if version == "" {
return ""
}
return fmt.Sprintf("%s@%s", modulePath, version)
}
// indent returns the output of prefixing n spaces to s at every line break,
// except for empty lines. See TestIndent for examples.
func indent(s string, n int) string {
b := []byte(s)
var result []byte
shouldAppend := true
prefix := strings.Repeat(" ", n)
for _, c := range b {
if shouldAppend && c != '\n' {
result = append(result, prefix...)
}
result = append(result, c)
shouldAppend = c == '\n'
}
return string(result)
}
// depPkgsAndMods returns the number of packages that
// topPkgs depend on and the number of their modules.
func depPkgsAndMods(topPkgs []*vulncheck.Package) (int, int) {
tops := make(map[string]bool)
depPkgs := make(map[string]bool)
depMods := make(map[string]bool)
for _, t := range topPkgs {
tops[t.PkgPath] = true
}
var visit func(*vulncheck.Package, bool)
visit = func(p *vulncheck.Package, top bool) {
path := p.PkgPath
if depPkgs[path] {
return
}
if tops[path] && !top {
// A top package that is a dependency
// will not be in depPkgs, so we skip
// reiterating on it here.
return
}
// We don't count a top-level package as
// a dependency even when they are used
// as a dependent package.
if !tops[path] {
depPkgs[path] = true
if p.Module != nil { // no module for stdlib
depMods[p.Module.Path] = true
}
}
for _, d := range p.Imports {
visit(d, false)
}
}
for _, t := range topPkgs {
visit(t, true)
}
return len(depPkgs), len(depMods)
}
// govulncheckVersion reconstructs the current version of
// govulncheck used from the build info.
func govulncheckVersion(bi *debug.BuildInfo) string {
var r, t string
for _, s := range bi.Settings {
if s.Key == "vcs.revision" {
r = "-" + s.Value[:12]
}
if s.Key == "vcs.time" {
// commit time is of the form 2023-01-25T19:57:54Z
p, err := time.Parse(time.RFC3339, s.Value)
if err == nil {
t = "-" + p.Format("20060102150405")
}
}
}
// TODO: we manually change this after every
// minor revision? bi.Main.Version seems not
// to work (see #29228).
return "v0.0.0" + r + t
}