| // 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. | 
 |  | 
 | // Only run this on Go 1.18 or higher, because govulncheck can't | 
 | // run on binaries before 1.18. | 
 |  | 
 | //go:build go1.18 | 
 | // +build go1.18 | 
 |  | 
 | package main | 
 |  | 
 | import ( | 
 | 	"errors" | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"os" | 
 | 	"os/exec" | 
 | 	"path/filepath" | 
 | 	"regexp" | 
 | 	"testing" | 
 |  | 
 | 	"github.com/google/go-cmdtest" | 
 | 	"golang.org/x/vuln/internal/buildtest" | 
 | ) | 
 |  | 
 | var update = flag.Bool("update", false, "update test files with results") | 
 |  | 
 | func TestCommand(t *testing.T) { | 
 | 	testDir, err := os.Getwd() | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	ts, err := cmdtest.Read("testdata") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	ts.DisableLogging = false | 
 | 	// Define a command that lets us cd into a module directory. | 
 | 	// The modules for these tests live under testdata/modules. | 
 | 	ts.Commands["cdmodule"] = func(args []string, inputFile string) ([]byte, error) { | 
 | 		if len(args) != 1 { | 
 | 			return nil, errors.New("need exactly 1 argument") | 
 | 		} | 
 | 		return nil, os.Chdir(filepath.Join(testDir, "testdata", "modules", args[0])) | 
 | 	} | 
 | 	// Define a command that runs govulncheck with our local DB. We can't use | 
 | 	// cmdtest.Program for this because it doesn't let us set the environment, | 
 | 	// and that is the only way to tell govulncheck about an alternative vuln | 
 | 	// database. | 
 | 	binary, cleanup := buildtest.GoBuild(t, ".") // build govulncheck | 
 | 	defer cleanup() | 
 | 	ts.Commands["govulncheck"] = func(args []string, inputFile string) ([]byte, error) { | 
 | 		cmd := exec.Command(binary, args...) | 
 | 		if inputFile != "" { | 
 | 			return nil, errors.New("input redirection makes no sense") | 
 | 		} | 
 | 		// We set GOVERSION to always get the same results regardless of the underlying Go build system. | 
 | 		cmd.Env = append(os.Environ(), "GOVULNDB=file://"+testDir+"/testdata/vulndb", "GOVERSION=go1.18") | 
 | 		out, err := cmd.CombinedOutput() | 
 | 		out = filterGoFilePaths(out) | 
 | 		out = filterStdlibVersions(out) | 
 | 		out = filterHeapGo(out) | 
 | 		return out, err | 
 | 	} | 
 |  | 
 | 	// Build test module binaries. | 
 | 	moduleDirs, err := filepath.Glob("testdata/modules/*") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 |  | 
 | 	// skipBuild contains names of module directories | 
 | 	// that should not be Go built. For instance, they | 
 | 	// might contain expected build errors. | 
 | 	skipBuild := map[string]bool{ | 
 | 		"nogomod": true, | 
 | 		"nogosum": true, | 
 | 	} | 
 |  | 
 | 	for _, md := range moduleDirs { | 
 | 		if skipBuild[filepath.Base(md)] { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		binary, cleanup := buildtest.GoBuild(t, md) | 
 | 		defer cleanup() | 
 | 		// Set an environment variable to the path to the binary, so tests | 
 | 		// can refer to it. | 
 | 		varName := filepath.Base(md) + "_binary" | 
 | 		os.Setenv(varName, binary) | 
 | 	} | 
 | 	ts.Run(t, *update) | 
 | } | 
 |  | 
 | var ( | 
 | 	goFileRegexp        = regexp.MustCompile(`[^\s"]*\.go[\s":]`) | 
 | 	stdlibVersionRegexp = regexp.MustCompile(`("Path": "stdlib",\s*"Version": ")v[^\s]+"`) | 
 | 	heapGoRegexp        = regexp.MustCompile(`heap\.go:(\d+)`) | 
 | ) | 
 |  | 
 | // filterGoFilePaths modifies paths to Go files by replacing their directory with "...". | 
 | // For example,/a/b/c.go becomes .../c.go . | 
 | // This makes it possible to compare govulncheck output across systems, because | 
 | // Go filenames include setup-specific paths. | 
 | func filterGoFilePaths(data []byte) []byte { | 
 | 	return goFileRegexp.ReplaceAllFunc(data, func(b []byte) []byte { | 
 | 		s := string(b) | 
 | 		return []byte(fmt.Sprintf(`.../%s%c`, filepath.Base(s[:len(s)-1]), s[len(s)-1])) | 
 | 	}) | 
 | } | 
 |  | 
 | // filterStdlibVersions removes Go standard library versions from JSON output, | 
 | // since they depend on the system running the test. Some have different | 
 | // versions than others, and on some we are unable to extract a version from | 
 | // the binary so the version is empty. | 
 | func filterStdlibVersions(data []byte) []byte { | 
 | 	return stdlibVersionRegexp.ReplaceAll(data, []byte(`${1}"`)) | 
 | } | 
 |  | 
 | // There was a one-line change in container/heap/heap.go between 1.18 | 
 | // and 1.19 that makes the stack traces different. Ignore it. | 
 | func filterHeapGo(data []byte) []byte { | 
 | 	return heapGoRegexp.ReplaceAll(data, []byte(`N`)) | 
 | } |