all: update for osv schema change

Update golang.org/x/vuln to latest.

/vuln/list, /vuln: Set "Affects:" header to a list of stdlib package
names and non-stdlib module names. For example, "net/http, golang.org/x/net".

/vuln: Find list of affected packages from ecosystem_specific.imports field.
Perhaps this page should list modules, or modules and packages within the
module, but for now just update the list to reflect the changed location
of package names in the OSV.

Package page, package versions tab: No changes to content, but use the
ecosystem_specific.imports field for package names.

Change-Id: Ib34e5f9388829c0ae3448ee14e32a725fb24cb48
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/425202
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/cmd/frontend/vulndb_cache.go b/cmd/frontend/vulndb_cache.go
index 22dd913..b333105 100644
--- a/cmd/frontend/vulndb_cache.go
+++ b/cmd/frontend/vulndb_cache.go
@@ -10,6 +10,7 @@
 	"time"
 
 	lru "github.com/hashicorp/golang-lru"
+	vulnc "golang.org/x/vuln/client"
 	"golang.org/x/vuln/osv"
 )
 
@@ -18,7 +19,7 @@
 type vulndbCache struct {
 	mu         sync.Mutex
 	dbName     string // support only one DB
-	index      osv.DBIndex
+	index      vulnc.DBIndex
 	retrieved  time.Time
 	entryCache *lru.Cache
 }
@@ -35,7 +36,7 @@
 
 // ReadIndex returns the index for dbName from the cache, or returns zero values
 // if it is not present.
-func (c *vulndbCache) ReadIndex(dbName string) (osv.DBIndex, time.Time, error) {
+func (c *vulndbCache) ReadIndex(dbName string) (vulnc.DBIndex, time.Time, error) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 	if err := c.checkDB(dbName); err != nil {
@@ -45,7 +46,7 @@
 }
 
 // WriteIndex puts the index and retrieved time into the cache.
-func (c *vulndbCache) WriteIndex(dbName string, index osv.DBIndex, retrieved time.Time) error {
+func (c *vulndbCache) WriteIndex(dbName string, index vulnc.DBIndex, retrieved time.Time) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 	if err := c.checkDB(dbName); err != nil {
diff --git a/go.mod b/go.mod
index 38d2621..e7aeb7a 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@
 	github.com/go-redis/redis/v8 v8.11.4
 	github.com/go-redis/redis_rate/v9 v9.1.2
 	github.com/golang-migrate/migrate/v4 v4.15.1
-	github.com/google/go-cmp v0.5.6
+	github.com/google/go-cmp v0.5.8
 	github.com/google/go-replayers/httpreplay v1.0.0
 	github.com/google/licensecheck v0.3.1
 	github.com/google/safehtml v0.0.3-0.20211026203422-d6f0e11a5516
@@ -36,12 +36,12 @@
 	github.com/yuin/goldmark v1.4.13
 	github.com/yuin/goldmark-emoji v1.0.1
 	go.opencensus.io v0.23.0
-	golang.org/x/mod v0.5.1
-	golang.org/x/net v0.0.0-20211013171255-e13a2654a71e
-	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
+	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
+	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
 	golang.org/x/text v0.3.7
-	golang.org/x/tools v0.1.5
-	golang.org/x/vuln v0.0.0-20211104165457-3710d685f6c2
+	golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3
+	golang.org/x/vuln v0.0.0-20220823182523-5ae4b706aeb8
 	google.golang.org/api v0.63.0
 	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
 	google.golang.org/grpc v1.43.0
@@ -102,7 +102,7 @@
 	go.uber.org/atomic v1.6.0 // indirect
 	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
-	golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
+	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index 1030ddd..dbee01a 100644
--- a/go.sum
+++ b/go.sum
@@ -563,8 +563,9 @@
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-github/v35 v35.2.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/go-replayers/httpreplay v1.0.0 h1:8SmT8fUYM4nueF+UnXIX8LJxNTb1vpPuknXz+yTWzL4=
@@ -1171,8 +1172,8 @@
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1233,8 +1234,9 @@
 golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211013171255-e13a2654a71e h1:Xj+JO91noE97IN6F/7WZxzC5QE6yENAQPrwIYhW3bsA=
 golang.org/x/net v0.0.0-20211013171255-e13a2654a71e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1267,8 +1269,9 @@
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1374,11 +1377,12 @@
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk=
 golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1466,10 +1470,11 @@
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/vuln v0.0.0-20211104165457-3710d685f6c2 h1:NW7zCHSSPK1imIHQ2syrERqdvROo86oIxSNB10/En1E=
-golang.org/x/vuln v0.0.0-20211104165457-3710d685f6c2/go.mod h1:vGjwvr4zd0JNGaeuScTP00A/lDhN8Ao3GFprqIUiIcM=
+golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3 h1:aE4T3aJwdCNz+s35ScSQYUzeGu7BOLDHZ1bBHVurqqY=
+golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/vuln v0.0.0-20220823182523-5ae4b706aeb8 h1:3XpOL48IUVS6mZLG5/twvtWSRL0VwLxX3wrA3nlEABU=
+golang.org/x/vuln v0.0.0-20220823182523-5ae4b706aeb8/go.mod h1:7tDfEDtOLlzHQRi4Yzfg5seVBSvouUIjyPzBx4q5CxQ=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index 9863338..d0c97e0 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -230,7 +230,7 @@
 	}
 
 	if getVulnEntries != nil {
-		addVulns(results, getVulnEntries)
+		addVulns(ctx, results, getVulnEntries)
 	}
 
 	var numResults int
@@ -508,7 +508,7 @@
 
 // addVulns adds vulnerability information to search results by consulting the
 // vulnerability database.
-func addVulns(rs []*SearchResult, getVulnEntries vulnEntriesFunc) {
+func addVulns(ctx context.Context, rs []*SearchResult, getVulnEntries vulnEntriesFunc) {
 	// Get all vulns concurrently.
 	var wg sync.WaitGroup
 	// TODO(golang/go#48223): throttle concurrency?
@@ -517,7 +517,7 @@
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
-			r.Vulns = VulnsForPackage(r.ModulePath, r.Version, r.PackagePath, getVulnEntries)
+			r.Vulns = VulnsForPackage(ctx, r.ModulePath, r.Version, r.PackagePath, getVulnEntries)
 		}()
 	}
 	wg.Wait()
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index ef6f6bb..ae6034f 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -157,7 +157,7 @@
 			}},
 		}}
 
-		getVulnEntries = func(modulePath string) ([]*osv.Entry, error) {
+		getVulnEntries = func(_ context.Context, modulePath string) ([]*osv.Entry, error) {
 			if modulePath == moduleFoo.ModulePath {
 				return vulnEntries, nil
 			}
@@ -230,7 +230,7 @@
 						DisplayVersion: moduleFoo.Version,
 						Licenses:       []string{"MIT"},
 						CommitTime:     elapsedTime(moduleFoo.CommitTime),
-						Vulns:          []Vuln{{ID: "test", Details: "vuln", FixedVersion: "v1.9.0"}},
+						Vulns:          []Vuln{{ID: "test", Details: "vuln"}},
 					},
 				},
 			},
diff --git a/internal/frontend/unit.go b/internal/frontend/unit.go
index 1614a43..6eddc07 100644
--- a/internal/frontend/unit.go
+++ b/internal/frontend/unit.go
@@ -235,7 +235,7 @@
 
 	// Get vulnerability information.
 	if s.vulnClient != nil {
-		page.Vulns = VulnsForPackage(um.ModulePath, um.Version, um.Path, s.vulnClient.GetByModule)
+		page.Vulns = VulnsForPackage(ctx, um.ModulePath, um.Version, um.Path, s.vulnClient.GetByModule)
 	}
 	s.servePage(ctx, w, tabSettings.TemplateName, page)
 	return nil
diff --git a/internal/frontend/versions.go b/internal/frontend/versions.go
index 73b0b3e..a364033 100644
--- a/internal/frontend/versions.go
+++ b/internal/frontend/versions.go
@@ -195,7 +195,7 @@
 		if sv := sh.SymbolsAtVersion(mi.Version); sv != nil {
 			vs.Symbols = symbolsForVersion(linkify(mi), sv)
 		}
-		vs.Vulns = VulnsForPackage(mi.ModulePath, mi.Version, "", getVulnEntries)
+		vs.Vulns = VulnsForPackage(ctx, mi.ModulePath, mi.Version, "", getVulnEntries)
 		vl := lists[key]
 		if vl == nil {
 			seenLists = append(seenLists, key)
diff --git a/internal/frontend/versions_test.go b/internal/frontend/versions_test.go
index 858a13f..62d0e12 100644
--- a/internal/frontend/versions_test.go
+++ b/internal/frontend/versions_test.go
@@ -98,7 +98,7 @@
 			}},
 		}},
 	}
-	getVulnEntries := func(m string) ([]*osv.Entry, error) {
+	getVulnEntries := func(_ context.Context, m string) ([]*osv.Entry, error) {
 		if m == modulePath1 {
 			return []*osv.Entry{vulnEntry}, nil
 		}
@@ -142,8 +142,7 @@
 					func() *VersionList {
 						vl := makeList(v1Path, modulePath1, "v1", []string{"v1.3.0", "v1.2.3", "v1.2.1"}, false)
 						vl.Versions[2].Vulns = []Vuln{{
-							Details:      vulnEntry.Details,
-							FixedVersion: "v" + vulnFixedVersion,
+							Details: vulnEntry.Details,
 						}}
 						return vl
 					}(),
diff --git a/internal/frontend/vulns.go b/internal/frontend/vulns.go
index 9a42cfc..159b8cd 100644
--- a/internal/frontend/vulns.go
+++ b/internal/frontend/vulns.go
@@ -5,6 +5,7 @@
 package frontend
 
 import (
+	"context"
 	"fmt"
 	"net/http"
 	"sort"
@@ -31,33 +32,31 @@
 	ID string
 	// A description of the vulnerability, or the problem in obtaining it.
 	Details string
-	// The version is which the vulnerability has been fixed.
-	FixedVersion string
 }
 
-type vulnEntriesFunc func(string) ([]*osv.Entry, error)
+type vulnEntriesFunc func(context.Context, string) ([]*osv.Entry, error)
 
 // VulnsForPackage obtains vulnerability information for the given package.
 // If packagePath is empty, it returns all entries for the module at version.
 // The getVulnEntries function should retrieve all entries for the given module path.
 // It is passed to facilitate testing.
 // If there is an error, VulnsForPackage returns a single Vuln that describes the error.
-func VulnsForPackage(modulePath, version, packagePath string, getVulnEntries vulnEntriesFunc) []Vuln {
-	vs, err := vulnsForPackage(modulePath, version, packagePath, getVulnEntries)
+func VulnsForPackage(ctx context.Context, modulePath, version, packagePath string, getVulnEntries vulnEntriesFunc) []Vuln {
+	vs, err := vulnsForPackage(ctx, modulePath, version, packagePath, getVulnEntries)
 	if err != nil {
 		return []Vuln{{Details: fmt.Sprintf("could not get vulnerability data: %v", err)}}
 	}
 	return vs
 }
 
-func vulnsForPackage(modulePath, version, packagePath string, getVulnEntries vulnEntriesFunc) (_ []Vuln, err error) {
+func vulnsForPackage(ctx context.Context, modulePath, version, packagePath string, getVulnEntries vulnEntriesFunc) (_ []Vuln, err error) {
 	defer derrors.Wrap(&err, "vulns(%q, %q, %q)", modulePath, version, packagePath)
 
 	if getVulnEntries == nil {
 		return nil, nil
 	}
 	// Get all the vulns for this module.
-	entries, err := getVulnEntries(modulePath)
+	entries, err := getVulnEntries(ctx, modulePath)
 	if err != nil {
 		return nil, err
 	}
@@ -75,46 +74,83 @@
 // VulnListPage holds the information for a page that lists all vuln entries.
 type VulnListPage struct {
 	basePage
-	Entries []*osv.Entry
+	Entries []OSVEntry
 }
 
 // VulnPage holds the information for a page that displays a single vuln entry.
 type VulnPage struct {
 	basePage
-	Entry            *osv.Entry
+	Entry            OSVEntry
 	AffectedPackages []*AffectedPackage
 	AliasLinks       []link
 	AdvisoryLinks    []link
 }
 
 type AffectedPackage struct {
-	Path     string // Package.Name in the osv.Entry
-	Versions string
+	PackagePath string
+	Versions    string
+}
+
+// OSVEntry holds an OSV entry and provides additional methods.
+type OSVEntry struct {
+	*osv.Entry
+}
+
+// AffectedModulesAndPackages returns a list of names affected by a vuln.
+func (e OSVEntry) AffectedModulesAndPackages() []string {
+	var affected []string
+	for _, a := range e.Affected {
+		switch a.Package.Name {
+		case "stdlib", "toolchain":
+			// Name specific standard library packages and tools.
+			for _, p := range a.EcosystemSpecific.Imports {
+				affected = append(affected, p.Path)
+			}
+		default:
+			// Outside the standard library, name the module.
+			affected = append(affected, a.Package.Name)
+		}
+	}
+	return affected
 }
 
 func entryVuln(e *osv.Entry, packagePath, version string) (Vuln, bool) {
 	for _, a := range e.Affected {
-		if (packagePath == "" || a.Package.Name == packagePath) && a.Ranges.AffectsSemver(version) {
-			// Choose the latest fixed version, if any.
-			var fixed string
-			for _, r := range a.Ranges {
-				if r.Type == osv.TypeGit {
-					continue
-				}
-				for _, re := range r.Events {
-					if re.Fixed != "" && (fixed == "" || semver.Compare(re.Fixed, fixed) > 0) {
-						fixed = re.Fixed
-					}
+		if !a.Ranges.AffectsSemver(version) {
+			continue
+		}
+		if packageMatches := func() bool {
+			if packagePath == "" {
+				return true //  match module only
+			}
+			if len(a.EcosystemSpecific.Imports) == 0 {
+				return true // no package info available, so match on module
+			}
+			for _, p := range a.EcosystemSpecific.Imports {
+				if packagePath == p.Path {
+					return true // package matches
 				}
 			}
-			fixed = addVersionPrefix(fixed, packagePath)
-			return Vuln{
-				ID:      e.ID,
-				Details: e.Details,
-				// TODO(golang/go#48223): handle stdlib versions
-				FixedVersion: fixed,
-			}, true
+			return false
+		}(); !packageMatches {
+			continue
 		}
+		// Choose the latest fixed version, if any.
+		var fixed string
+		for _, r := range a.Ranges {
+			if r.Type == osv.TypeGit {
+				continue
+			}
+			for _, re := range r.Events {
+				if re.Fixed != "" && (fixed == "" || semver.Compare(re.Fixed, fixed) > 0) {
+					fixed = re.Fixed
+				}
+			}
+		}
+		return Vuln{
+			ID:      e.ID,
+			Details: e.Details,
+		}, true
 	}
 	return Vuln{}, false
 }
@@ -123,7 +159,7 @@
 	switch r.URL.Path {
 	case "/":
 		// Serve a list of most recent entries.
-		vulnListPage, err := newVulnListPage(s.vulnClient)
+		vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient)
 		if err != nil {
 			return &serverError{status: derrors.ToStatus(err)}
 		}
@@ -134,7 +170,7 @@
 		s.servePage(r.Context(), w, "vuln/main", vulnListPage)
 	case "/list":
 		// Serve a list of all entries.
-		vulnListPage, err := newVulnListPage(s.vulnClient)
+		vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient)
 		if err != nil {
 			return &serverError{status: derrors.ToStatus(err)}
 		}
@@ -151,7 +187,7 @@
 				responseText: "invalid Go vuln ID; should be GO-YYYY-NNNN",
 			}
 		}
-		vulnPage, err := newVulnPage(s.vulnClient, id)
+		vulnPage, err := newVulnPage(r.Context(), s.vulnClient, id)
 		if err != nil {
 			return &serverError{status: derrors.ToStatus(err)}
 		}
@@ -161,8 +197,8 @@
 	return nil
 }
 
-func newVulnPage(client vulnc.Client, id string) (*VulnPage, error) {
-	entry, err := client.GetByID(id)
+func newVulnPage(ctx context.Context, client vulnc.Client, id string) (*VulnPage, error) {
+	entry, err := client.GetByID(ctx, id)
 	if err != nil {
 		return nil, err
 	}
@@ -170,24 +206,24 @@
 		return nil, derrors.NotFound
 	}
 	return &VulnPage{
-		Entry:            entry,
+		Entry:            OSVEntry{entry},
 		AffectedPackages: affectedPackages(entry),
 		AliasLinks:       aliasLinks(entry),
 		AdvisoryLinks:    advisoryLinks(entry),
 	}, nil
 }
 
-func newVulnListPage(client vulnc.Client) (*VulnListPage, error) {
+func newVulnListPage(ctx context.Context, client vulnc.Client) (*VulnListPage, error) {
 	const concurrency = 4
 
-	ids, err := client.ListIDs()
+	ids, err := client.ListIDs(ctx)
 	if err != nil {
 		return nil, err
 	}
 	// Sort from most to least recent.
 	sort.Slice(ids, func(i, j int) bool { return ids[i] > ids[j] })
 
-	entries := make([]*osv.Entry, len(ids))
+	entries := make([]OSVEntry, len(ids))
 	sem := make(chan struct{}, concurrency)
 	var g errgroup.Group
 	for i, id := range ids {
@@ -196,11 +232,11 @@
 		sem <- struct{}{}
 		g.Go(func() error {
 			defer func() { <-sem }()
-			e, err := client.GetByID(id)
+			e, err := client.GetByID(ctx, id)
 			if err != nil {
 				return err
 			}
-			entries[i] = e
+			entries[i] = OSVEntry{e}
 			return nil
 		})
 	}
@@ -273,10 +309,12 @@
 			}
 			vs = append(vs, s)
 		}
-		affs = append(affs, &AffectedPackage{
-			Path:     a.Package.Name,
-			Versions: strings.Join(vs, ", "),
-		})
+		for _, p := range a.EcosystemSpecific.Imports {
+			affs = append(affs, &AffectedPackage{
+				PackagePath: p.Path,
+				Versions:    strings.Join(vs, ", "),
+			})
+		}
 	}
 	return affs
 }
@@ -314,13 +352,3 @@
 	}
 	return links
 }
-
-func addVersionPrefix(semver, packagePath string) (res string) {
-	if semver == "" {
-		return ""
-	}
-	if packagePath != "" && stdlib.Contains(packagePath) {
-		return "go" + semver
-	}
-	return "v" + semver
-}
diff --git a/internal/frontend/vulns_test.go b/internal/frontend/vulns_test.go
index eccb66d..ee91d62 100644
--- a/internal/frontend/vulns_test.go
+++ b/internal/frontend/vulns_test.go
@@ -5,6 +5,7 @@
 package frontend
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"reflect"
@@ -17,6 +18,7 @@
 )
 
 func TestVulnsForPackage(t *testing.T) {
+	ctx := context.Background()
 	e := osv.Entry{
 		Details: "bad",
 		Affected: []osv.Affected{{
@@ -25,10 +27,15 @@
 				Type:   osv.TypeSemver,
 				Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "1.2.3"}},
 			}},
+			EcosystemSpecific: osv.EcosystemSpecific{
+				Imports: []osv.EcosystemSpecificImport{{
+					Path: "bad.com",
+				}},
+			},
 		}},
 	}
 
-	get := func(modulePath string) ([]*osv.Entry, error) {
+	get := func(_ context.Context, modulePath string) ([]*osv.Entry, error) {
 		switch modulePath {
 		case "good.com":
 			return nil, nil
@@ -39,20 +46,19 @@
 		}
 	}
 
-	got := VulnsForPackage("good.com", "v1.0.0", "good.com", get)
+	got := VulnsForPackage(ctx, "good.com", "v1.0.0", "good.com", get)
 	if got != nil {
 		t.Errorf("got %v, want nil", got)
 	}
-	got = VulnsForPackage("bad.com", "v1.0.0", "bad.com", get)
+	got = VulnsForPackage(ctx, "bad.com", "v1.0.0", "bad.com", get)
 	want := []Vuln{{
-		Details:      "bad",
-		FixedVersion: "v1.2.3",
+		Details: "bad",
 	}}
 	if diff := cmp.Diff(want, got); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
 
-	got = VulnsForPackage("bad.com", "v1.3.0", "bad.com", get)
+	got = VulnsForPackage(ctx, "bad.com", "v1.3.0", "bad.com", get)
 	if got != nil {
 		t.Errorf("got %v, want nil", got)
 	}
@@ -69,15 +75,16 @@
 }
 
 func TestNewVulnListPage(t *testing.T) {
+	ctx := context.Background()
 	c := &vulndbTestClient{entries: testEntries}
-	got, err := newVulnListPage(c)
+	got, err := newVulnListPage(ctx, c)
 	if err != nil {
 		t.Fatal(err)
 	}
 	// testEntries is already sorted by ID, but it should be reversed.
-	var wantEntries []*osv.Entry
+	var wantEntries []OSVEntry
 	for i := len(testEntries) - 1; i >= 0; i-- {
-		wantEntries = append(wantEntries, testEntries[i])
+		wantEntries = append(wantEntries, OSVEntry{testEntries[i]})
 	}
 	want := &VulnListPage{Entries: wantEntries}
 	if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(VulnListPage{})); diff != "" {
@@ -86,12 +93,13 @@
 }
 
 func TestNewVulnPage(t *testing.T) {
+	ctx := context.Background()
 	c := &vulndbTestClient{entries: testEntries}
-	got, err := newVulnPage(c, "GO-1990-02")
+	got, err := newVulnPage(ctx, c, "GO-1990-02")
 	if err != nil {
 		t.Fatal(err)
 	}
-	want := &VulnPage{Entry: testEntries[1]}
+	want := &VulnPage{Entry: OSVEntry{testEntries[1]}}
 	if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(VulnPage{})); diff != "" {
 		t.Errorf("mismatch (-want, +got):\n%s", diff)
 	}
@@ -102,11 +110,11 @@
 	entries []*osv.Entry
 }
 
-func (c *vulndbTestClient) GetByModule(string) ([]*osv.Entry, error) {
+func (c *vulndbTestClient) GetByModule(context.Context, string) ([]*osv.Entry, error) {
 	return nil, errors.New("unimplemented")
 }
 
-func (c *vulndbTestClient) GetByID(id string) (*osv.Entry, error) {
+func (c *vulndbTestClient) GetByID(_ context.Context, id string) (*osv.Entry, error) {
 	for _, e := range c.entries {
 		if e.ID == id {
 			return e, nil
@@ -115,7 +123,7 @@
 	return nil, nil
 }
 
-func (c *vulndbTestClient) ListIDs() ([]string, error) {
+func (c *vulndbTestClient) ListIDs(context.Context) ([]string, error) {
 	var ids []string
 	for _, e := range c.entries {
 		ids = append(ids, e.ID)
diff --git a/static/frontend/vuln/entry/entry.tmpl b/static/frontend/vuln/entry/entry.tmpl
index 90f4291..53ba67a 100644
--- a/static/frontend/vuln/entry/entry.tmpl
+++ b/static/frontend/vuln/entry/entry.tmpl
@@ -59,7 +59,7 @@
     <tbody>
       {{range .}}
 	<tr>
-	  <td><a href="/{{.Path}}">{{.Path}}</a></td>
+	  <td><a href="/{{.PackagePath}}">{{.PackagePath}}</a></td>
 	  <td>{{.Versions}}</td>
 	</tr>
       {{end}}
diff --git a/static/frontend/vuln/vuln.tmpl b/static/frontend/vuln/vuln.tmpl
index 6adfad4..cb70161 100644
--- a/static/frontend/vuln/vuln.tmpl
+++ b/static/frontend/vuln/vuln.tmpl
@@ -22,7 +22,7 @@
 {{end}}
 
 {{define "vuln-details"}}
-  {{/* . is osv.Entry */}}
+  {{/* . is OSVEntry */}}
   <div class="Vuln-details">
     <ul class="Vuln-detailsMetadata">
       {{if .Aliases}}
@@ -31,9 +31,11 @@
         </li>
       {{end}}
       <li class="go-textSubtle">Affects:
-        {{range $i, $v := .Affected}}
-          {{- if lt $i 2}}{{- if ne $i 0}}, {{end -}}{{$v.Package.Name}}{{end -}}
-          {{- if eq $i 2}}, and {{subtract (len $.Affected) 2}} more{{end -}}
+       {{with $packages := .AffectedModulesAndPackages}}
+          {{range $i, $name := $packages}}
+            {{- if lt $i 2}}{{- if ne $i 0}}, {{end -}}{{$name}}{{end -}}
+            {{- if eq $i 2}}, and {{subtract (len $packages) 2}} more{{end -}}
+          {{end}}
         {{end}}
       </li>
       <li class="go-textSubtle">Published: {{.Published.Format "Jan 02, 2006"}}</li>
@@ -61,4 +63,4 @@
     <input name="q" class="go-Input" placeholder="Search GO IDs" />
     <button class="go-Button">Submit</button>
   </form>
-{{end}}
\ No newline at end of file
+{{end}}