blob: 8db725719ed8b8c1e8b954454ac06f41550c6484 [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 main
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/exp/slices"
"golang.org/x/pkgsite-metrics/internal/buildtest"
"golang.org/x/pkgsite-metrics/internal/derrors"
"golang.org/x/pkgsite-metrics/internal/govulncheck"
"golang.org/x/pkgsite-metrics/internal/worker"
govulncheckapi "golang.org/x/vuln/exp/govulncheck"
)
func Test(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("cannot run on Windows")
}
tempDir, err := os.MkdirTemp("", "installGovulncheck")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := os.RemoveAll(tempDir); err != nil {
t.Fatal(err)
}
}()
govulncheckPath, err := buildtest.BuildGovulncheck(tempDir)
if err != nil {
t.Fatal(err)
}
checkVuln := func(t *testing.T, res *govulncheckapi.Result) {
wantID := "GO-2021-0113"
i := slices.IndexFunc(res.Vulns, func(v *govulncheckapi.Vuln) bool {
return v.OSV.ID == wantID
})
if i < 0 {
t.Fatalf("no vuln with ID %s. Result:\n%+v", wantID, res)
}
}
// govulncheck binary requires a full path to the vuln db. Otherwise, one
// gets "[file://testdata/vulndb], opts): file URL specifies non-local host."
vulndb, err := filepath.Abs("testdata/vulndb")
if err != nil {
t.Fatal(err)
}
t.Run("source", func(t *testing.T) {
resp, err := runTest([]string{govulncheckPath, worker.ModeGovulncheck, "testdata/module"}, vulndb)
if err != nil {
t.Fatal(err)
}
checkVuln(t, &resp.Res)
if resp.Stats.ScanSeconds <= 0 {
t.Errorf("got %f; want >0 scan seconds", resp.Stats.ScanSeconds)
}
if resp.Stats.ScanMemory <= 0 {
t.Errorf("got %d; want >0 scan memory", resp.Stats.ScanMemory)
}
})
t.Run("binary", func(t *testing.T) {
t.Skip("govulncheck may not support the Go version")
const binary = "testdata/module/vuln"
cmd := exec.Command("go build")
cmd.Dir = "testdata/module"
if _, err := cmd.Output(); err != nil {
t.Fatal(derrors.IncludeStderr(err))
}
defer os.Remove(binary)
resp, err := runTest([]string{govulncheckPath, worker.ModeBinary, binary}, vulndb)
if err != nil {
t.Fatal(err)
}
checkVuln(t, &resp.Res)
})
// Errors
for _, test := range []struct {
name string
args []string
vulndb string
want string
}{
{
name: "too few args",
args: []string{"testdata/module"},
vulndb: vulndb,
want: "need three args",
},
{
name: "no vulndb",
args: []string{govulncheckPath, worker.ModeGovulncheck, "testdata/module"},
vulndb: "does not exist",
want: "exit status 1",
},
{
name: "no mode",
args: []string{govulncheckPath, "MODE", "testdata/module"},
vulndb: vulndb,
want: "not a valid mode",
},
{
name: "no module",
args: []string{govulncheckPath, worker.ModeGovulncheck, "testdata/nosuchmodule"},
vulndb: vulndb,
want: "no such file",
},
} {
t.Run(test.name, func(t *testing.T) {
_, err := runTest(test.args, test.vulndb)
if err == nil {
t.Fatal("got nil, want error")
}
if g, w := err.Error(), test.want; !strings.Contains(g, w) {
t.Fatalf("error %q does not contain %q", g, w)
}
})
}
}
func runTest(args []string, vulndbDir string) (*govulncheck.GovulncheckResponse, error) {
var buf bytes.Buffer
run(&buf, args, vulndbDir)
return govulncheck.UnmarshalGovulncheckSandboxResponse(buf.Bytes())
}