cmd/govulncheck: work with Go 1.17
The govulncheck command will now work on source if compiled with
Go 1.17. It will fail if run on a binary.
Run the tests that match command output only on 1.18 or higher.
Change-Id: Ia3e23a130f822c55dee94b95dc2a01c96b6269e5
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/411454
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
diff --git a/cmd/govulncheck/binary_118.go b/cmd/govulncheck/binary_118.go
new file mode 100644
index 0000000..6c13fb7
--- /dev/null
+++ b/cmd/govulncheck/binary_118.go
@@ -0,0 +1,19 @@
+// 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.
+
+//go:build go1.18
+// +build go1.18
+
+package main
+
+import (
+ "context"
+ "io"
+
+ "golang.org/x/vuln/vulncheck"
+)
+
+func binary(ctx context.Context, exe io.ReaderAt, cfg *vulncheck.Config) (_ *vulncheck.Result, err error) {
+ return vulncheck.Binary(ctx, exe, cfg)
+}
diff --git a/cmd/govulncheck/binary_not118.go b/cmd/govulncheck/binary_not118.go
new file mode 100644
index 0000000..04bd8a6
--- /dev/null
+++ b/cmd/govulncheck/binary_not118.go
@@ -0,0 +1,20 @@
+// 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.
+
+//go:build !go1.18
+// +build !go1.18
+
+package main
+
+import (
+ "context"
+ "errors"
+ "io"
+
+ "golang.org/x/vuln/vulncheck"
+)
+
+func binary(ctx context.Context, exe io.ReaderAt, cfg *vulncheck.Config) (_ *vulncheck.Result, err error) {
+ return nil, errors.New("compile with Go 1.18 or higher to analyze binary files")
+}
diff --git a/cmd/govulncheck/doc.go b/cmd/govulncheck/doc.go
index 04f097e..00bfdea 100644
--- a/cmd/govulncheck/doc.go
+++ b/cmd/govulncheck/doc.go
@@ -2,16 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
/*
Command govulncheck reports known vulnerabilities that affect Go code. It uses
static analysis or a binary's symbol table to narrow down reports to only those
that potentially affect the application. For more information about the API
behind govulncheck, see https://go.dev/security/vulncheck.
-
By default, govulncheck uses the Go vulnerability database at
https://vuln.go.dev. Set the GOVULNDB environment variable to specify a different database.
The database must follow the specification at https://go.dev/security/vulndb.
@@ -21,7 +17,7 @@
WARNING: govulncheck is still EXPERIMENTAL and neither its output or the vulnerability
database should be relied on to be stable or comprehensive.
-Usage
+# Usage
To analyze source code, run govulncheck from the module directory, using the
same package path syntax that the go command uses:
@@ -52,7 +48,7 @@
Its output and exit codes are as described above, except that without source it cannot
produce call stacks.
-Other Modes
+# Other Modes
A few flags control govulncheck's output. Regardless of output, govulncheck
exits with code 0 if there are no vulnerabilities and 3 if there are.
@@ -65,7 +61,7 @@
The -json flag outputs a JSON object with vulnerability information. The output
corresponds to the type golang.org/x/vuln/vulncheck.Result.
-Weaknesses
+# Weaknesses
Govulncheck uses static analysis, which is inherently imprecise. If govulncheck
identifies a sequence of calls in your program that leads to a vulnerable
diff --git a/cmd/govulncheck/formatting.go b/cmd/govulncheck/formatting.go
index d1a7b02..10814a4 100644
--- a/cmd/govulncheck/formatting.go
+++ b/cmd/govulncheck/formatting.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package main
import (
diff --git a/cmd/govulncheck/formatting_test.go b/cmd/govulncheck/formatting_test.go
index d91dd0f..48fdd9e 100644
--- a/cmd/govulncheck/formatting_test.go
+++ b/cmd/govulncheck/formatting_test.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package main
import (
diff --git a/cmd/govulncheck/html.go b/cmd/govulncheck/html.go
index f66751a..552479b 100644
--- a/cmd/govulncheck/html.go
+++ b/cmd/govulncheck/html.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package main
import (
diff --git a/cmd/govulncheck/internal/govulncheck/cache.go b/cmd/govulncheck/internal/govulncheck/cache.go
index 404c356..0948837 100644
--- a/cmd/govulncheck/internal/govulncheck/cache.go
+++ b/cmd/govulncheck/internal/govulncheck/cache.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
// Package govulncheck supports the govulncheck command.
package govulncheck
diff --git a/cmd/govulncheck/internal/govulncheck/cache_test.go b/cmd/govulncheck/internal/govulncheck/cache_test.go
index 5a25c78..0917ae8 100644
--- a/cmd/govulncheck/internal/govulncheck/cache_test.go
+++ b/cmd/govulncheck/internal/govulncheck/cache_test.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package govulncheck
import (
diff --git a/cmd/govulncheck/internal/govulncheck/source.go b/cmd/govulncheck/internal/govulncheck/source.go
index 23028b9..01255fc 100644
--- a/cmd/govulncheck/internal/govulncheck/source.go
+++ b/cmd/govulncheck/internal/govulncheck/source.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package govulncheck
import (
diff --git a/cmd/govulncheck/internal/govulncheck/util.go b/cmd/govulncheck/internal/govulncheck/util.go
index baa2d96..6699236 100644
--- a/cmd/govulncheck/internal/govulncheck/util.go
+++ b/cmd/govulncheck/internal/govulncheck/util.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package govulncheck
import (
diff --git a/cmd/govulncheck/internal/govulncheck/util_test.go b/cmd/govulncheck/internal/govulncheck/util_test.go
index 3288cd8..67e7772 100644
--- a/cmd/govulncheck/internal/govulncheck/util_test.go
+++ b/cmd/govulncheck/internal/govulncheck/util_test.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package govulncheck
import (
diff --git a/cmd/govulncheck/main.go b/cmd/govulncheck/main.go
index 1d80457..21e5225 100644
--- a/cmd/govulncheck/main.go
+++ b/cmd/govulncheck/main.go
@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
-
package main
import (
@@ -37,7 +34,7 @@
govulncheck [flags] {package pattern...}
- govulncheck [flags] {binary path}
+ govulncheck [flags] {binary path} (if built with Go 1.18 or higher)
Flags:
@@ -101,7 +98,7 @@
die("govulncheck: %v", err)
}
defer f.Close()
- r, err = vulncheck.Binary(ctx, f, vcfg)
+ r, err = binary(ctx, f, vcfg)
if err != nil {
die("govulncheck: %v", err)
}
diff --git a/cmd/govulncheck/main_test.go b/cmd/govulncheck/main_test.go
index 32dd1c2..fa07746 100644
--- a/cmd/govulncheck/main_test.go
+++ b/cmd/govulncheck/main_test.go
@@ -2,93 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//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/cmd/govulncheck/internal/govulncheck"
- "golang.org/x/vuln/internal/buildtest"
"golang.org/x/vuln/osv"
)
-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")
- }
- cmd.Env = append(os.Environ(), "GOVULNDB=file://"+testDir+"/testdata/vulndb")
- out, err := cmd.CombinedOutput()
- out = filterGoFilePaths(out)
- return out, err
- }
-
- // Build test module binaries.
- moduleDirs, err := filepath.Glob("testdata/modules/*")
- if err != nil {
- t.Fatal(err)
- }
- for _, md := range moduleDirs {
- 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":]`)
-
-// 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[1:len(s)-1]), s[len(s)-1]))
- })
-}
-
func TestLatestFixed(t *testing.T) {
for _, test := range []struct {
name string
diff --git a/cmd/govulncheck/main_test_command_118.go b/cmd/govulncheck/main_test_command_118.go
new file mode 100644
index 0000000..ec51145
--- /dev/null
+++ b/cmd/govulncheck/main_test_command_118.go
@@ -0,0 +1,91 @@
+// 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")
+ }
+ cmd.Env = append(os.Environ(), "GOVULNDB=file://"+testDir+"/testdata/vulndb")
+ out, err := cmd.CombinedOutput()
+ out = filterGoFilePaths(out)
+ return out, err
+ }
+
+ // Build test module binaries.
+ moduleDirs, err := filepath.Glob("testdata/modules/*")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, md := range moduleDirs {
+ 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":]`)
+
+// 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[1:len(s)-1]), s[len(s)-1]))
+ })
+}
diff --git a/cmd/govulncheck/testdata/usage.ct b/cmd/govulncheck/testdata/usage.ct
index b2db75b..b5bbecc 100644
--- a/cmd/govulncheck/testdata/usage.ct
+++ b/cmd/govulncheck/testdata/usage.ct
@@ -5,7 +5,7 @@
govulncheck [flags] {package pattern...}
- govulncheck [flags] {binary path}
+ govulncheck [flags] {binary path} (if built with Go 1.18 or higher)
Flags:
@@ -34,7 +34,7 @@
govulncheck [flags] {package pattern...}
- govulncheck [flags] {binary path}
+ govulncheck [flags] {binary path} (if built with Go 1.18 or higher)
Flags: