vulncheck: include proper filtering of vulnerabilities

Cherry-picked: https://go-review.googlesource.com/c/exp/+/370457

Change-Id: Icb61985cb7c22cf6d2b99a5eea05cb8ba31b8f45
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/395048
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/vulncheck/binary.go b/vulncheck/binary.go
index bd2f7d7..17b5d16 100644
--- a/vulncheck/binary.go
+++ b/vulncheck/binary.go
@@ -7,6 +7,7 @@
 import (
 	"context"
 	"io"
+	"runtime"
 
 	"golang.org/x/tools/go/packages"
 	"golang.org/x/vuln/vulncheck/internal/binscan"
@@ -23,6 +24,7 @@
 	if err != nil {
 		return nil, err
 	}
+	modVulns = modVulns.Filter(lookupEnv("GOOS", runtime.GOOS), lookupEnv("GOARCH", runtime.GOARCH))
 
 	result := &Result{}
 	for pkg, symbols := range packageSymbols {
diff --git a/vulncheck/source.go b/vulncheck/source.go
index fa12790..cd20d50 100644
--- a/vulncheck/source.go
+++ b/vulncheck/source.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"runtime"
 
 	"golang.org/x/tools/go/callgraph"
 	"golang.org/x/tools/go/ssa"
@@ -24,6 +25,7 @@
 	if err != nil {
 		return nil, err
 	}
+	modVulns = modVulns.Filter(lookupEnv("GOOS", runtime.GOOS), lookupEnv("GOARCH", runtime.GOARCH))
 
 	result := &Result{
 		Imports:  &ImportGraph{Packages: make(map[int]*PkgNode)},
diff --git a/vulncheck/source_test.go b/vulncheck/source_test.go
index 7398392..c97adf0 100644
--- a/vulncheck/source_test.go
+++ b/vulncheck/source_test.go
@@ -6,11 +6,13 @@
 
 import (
 	"context"
+	"os"
 	"path"
 	"reflect"
 	"testing"
 
 	"golang.org/x/tools/go/packages/packagestest"
+	"golang.org/x/vuln/osv"
 )
 
 // TestImportsOnly checks for module and imports graph correctness
@@ -379,3 +381,89 @@
 		t.Errorf("want %v call graph; got %v", wantCalls, callStrMap)
 	}
 }
+
+func TestFiltering(t *testing.T) {
+	e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
+		{
+			Name: "golang.org/entry",
+			Files: map[string]interface{}{
+				"x/x.go": `
+			package x
+
+			import "golang.org/vmod/vuln"
+
+			func X() {
+				vuln.V()
+			}`,
+			},
+		},
+		{
+			Name: "golang.org/vmod@v1.2.3",
+			Files: map[string]interface{}{"vuln/vuln.go": `
+			package vuln
+
+			func V() {}
+			`},
+		},
+	})
+	defer e.Cleanup()
+
+	client := &mockClient{
+		ret: map[string][]*osv.Entry{
+			"golang.org/vmod": []*osv.Entry{
+				{
+					ID: "V",
+					Affected: []osv.Affected{{
+						Package: osv.Package{Name: "golang.org/vmod/vuln"},
+						Ranges:  osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.2.0"}}}},
+						EcosystemSpecific: osv.EcosystemSpecific{
+							Symbols: []string{"V"},
+							GOOS:    []string{"linux"},
+							GOARCH:  []string{"amd64"},
+						},
+					}},
+				},
+			},
+		},
+	}
+
+	// Make sure local vulns can be loaded.
+	fetchingInTesting = true
+	// Load x as entry package.
+	pkgs, err := loadPackages(e, path.Join(e.Temp(), "entry/x"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(pkgs) != 1 {
+		t.Fatal("failed to load x test package")
+	}
+
+	cfg := &Config{
+		Client:      client,
+		ImportsOnly: true,
+	}
+
+	os.Setenv("GOOS", "linux")
+	os.Setenv("GOARCH", "amd64")
+
+	result, err := Source(context.Background(), Convert(pkgs), cfg)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(result.Vulns) != 1 {
+		t.Errorf("want 1 Vuln, got %d", len(result.Vulns))
+	}
+
+	os.Setenv("GOOS", "freebsd")
+	os.Setenv("GOARCH", "arm64")
+
+	result, err = Source(context.Background(), Convert(pkgs), cfg)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(result.Vulns) != 0 {
+		t.Errorf("want 0 Vulns, got %d", len(result.Vulns))
+	}
+}
diff --git a/vulncheck/utils.go b/vulncheck/utils.go
index d120f56..70d37f5 100644
--- a/vulncheck/utils.go
+++ b/vulncheck/utils.go
@@ -8,6 +8,7 @@
 	"bytes"
 	"go/token"
 	"go/types"
+	"os"
 	"strings"
 
 	"golang.org/x/tools/go/callgraph"
@@ -214,3 +215,10 @@
 	types.WriteType(buf, v.Type(), nil)
 	return buf.String()
 }
+
+func lookupEnv(key, defaultValue string) string {
+	if v, ok := os.LookupEnv(key); ok {
+		return v
+	}
+	return defaultValue
+}