internal,static: add vuln mode to search modes

When searching by vuln ID on the /vuln page a search for
something other than a vuln ID would default to package
search. This change adds a vuln mode to search prevent that.

Change-Id: I3fbf76d6b4c6c548a8dfc83b4e1243d62d9991cf
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/422909
Reviewed-by: Julie Qiu <julieqiu@google.com>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index 4578791..9863338 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -89,7 +89,8 @@
 			},
 		}
 	}
-	if path := searchRequestRedirectPath(ctx, ds, cq); path != "" {
+	mode := searchMode(r)
+	if path := searchRequestRedirectPath(ctx, ds, cq, mode); path != "" {
 		http.Redirect(w, r, path, http.StatusFound)
 		return nil
 	}
@@ -98,7 +99,6 @@
 	if len(filters) > 0 {
 		symbol = filters[0]
 	}
-	mode := searchMode(r)
 	var getVulnEntries vulnEntriesFunc
 	if s.vulnClient != nil {
 		getVulnEntries = s.vulnClient.GetByModule
@@ -153,6 +153,9 @@
 	// by symbols.
 	searchModeSymbol = "symbol"
 
+	// searchModeVuln is the query param for searching by vuln id.
+	searchModeVuln = "vuln"
+
 	// symbolSearchFilter is a filter that can be used to indicate that the query
 	// contains a symbol. For example, searching for "#unmarshal json" indicates
 	// that unmarshal is a symbol.
@@ -316,13 +319,14 @@
 //
 // If the user types a name that is in the form of a Go vulnerability ID, we will
 // redirect to the page for that ID (whether or not it exists).
-func searchRequestRedirectPath(ctx context.Context, ds internal.DataSource, query string) string {
+func searchRequestRedirectPath(ctx context.Context, ds internal.DataSource, query, mode string) string {
 	urlSchemeIdx := strings.Index(query, "://")
 	if urlSchemeIdx > -1 {
 		query = query[urlSchemeIdx+3:]
 	}
-	if goVulnIDRegexp.MatchString(query) {
-		return fmt.Sprintf("/vuln/%s", query)
+	// TODO(go.dev/issue/54465): add support for searching by alias.
+	if goVulnIDRegexp.MatchString(query) || mode == searchModeVuln {
+		return fmt.Sprintf("/vuln/%s?q", query)
 	}
 	requestedPath := path.Clean(query)
 	if !strings.Contains(requestedPath, "/") {
@@ -345,17 +349,19 @@
 	if len(filters) > 0 {
 		return searchModeSymbol
 	}
-	mode := rawSearchMode(r)
-	if mode == searchModePackage {
+	switch rawSearchMode(r) {
+	case searchModePackage:
+		return searchModePackage
+	case searchModeSymbol:
+		return searchModeSymbol
+	case searchModeVuln:
+		return searchModeVuln
+	default:
+		if shouldDefaultToSymbolSearch(q) {
+			return searchModeSymbol
+		}
 		return searchModePackage
 	}
-	if mode == searchModeSymbol {
-		return searchModeSymbol
-	}
-	if shouldDefaultToSymbolSearch(q) {
-		return searchModeSymbol
-	}
-	return searchModePackage
 }
 
 // searchQueryAndFilters returns the search query, trimmed of any filters, and
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index c675fcb..ef6f6bb 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -50,6 +50,12 @@
 			q:              "foo",
 			wantSearchMode: searchModeSymbol,
 		},
+		{
+			name:           "search in vuln mode",
+			m:              searchModeVuln,
+			q:              "foo",
+			wantSearchMode: searchModeVuln,
+		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			u := fmt.Sprintf("/search?q=%s&m=%s", test.q, test.m)
@@ -365,21 +371,23 @@
 		name  string
 		query string
 		want  string
+		mode  string
 	}{
-		{"module", "golang.org/x/tools", "/golang.org/x/tools"},
-		{"directory", "golang.org/x/tools/internal", "/golang.org/x/tools/internal"},
-		{"package", "golang.org/x/tools/internal/lsp", "/golang.org/x/tools/internal/lsp"},
-		{"stdlib package does not redirect", "errors", ""},
-		{"stdlib package does redirect", "cmd/go", "/cmd/go"},
-		{"stdlib directory does redirect", "cmd/go/internal", "/cmd/go/internal"},
-		{"std does not redirect", "std", ""},
-		{"non-existent path does not redirect", "github.com/non-existent", ""},
-		{"trim URL scheme from query", "https://golang.org/x/tools", "/golang.org/x/tools"},
-		{"Go vuln redirects", "GO-1969-0720", "/vuln/GO-1969-0720"},
-		{"not a Go vuln", "somepkg/GO-1969-0720", ""},
+		{"module", "golang.org/x/tools", "/golang.org/x/tools", ""},
+		{"directory", "golang.org/x/tools/internal", "/golang.org/x/tools/internal", ""},
+		{"package", "golang.org/x/tools/internal/lsp", "/golang.org/x/tools/internal/lsp", ""},
+		{"stdlib package does not redirect", "errors", "", ""},
+		{"stdlib package does redirect", "cmd/go", "/cmd/go", ""},
+		{"stdlib directory does redirect", "cmd/go/internal", "/cmd/go/internal", ""},
+		{"std does not redirect", "std", "", ""},
+		{"non-existent path does not redirect", "github.com/non-existent", "", ""},
+		{"trim URL scheme from query", "https://golang.org/x/tools", "/golang.org/x/tools", ""},
+		{"Go vuln redirects", "GO-1969-0720", "/vuln/GO-1969-0720?q", ""},
+		{"not a Go vuln", "somepkg/GO-1969-0720", "", ""},
+		{"search mode is vuln", "searchmodevuln", "/vuln/searchmodevuln?q", searchModeVuln},
 	} {
 		t.Run(test.name, func(t *testing.T) {
-			if got := searchRequestRedirectPath(ctx, testDB, test.query); got != test.want {
+			if got := searchRequestRedirectPath(ctx, testDB, test.query, test.mode); got != test.want {
 				t.Errorf("searchRequestRedirectPath(ctx, %q) = %q; want = %q", test.query, got, test.want)
 			}
 		})
diff --git a/internal/frontend/vulns.go b/internal/frontend/vulns.go
index 192cea7..35cdd69 100644
--- a/internal/frontend/vulns.go
+++ b/internal/frontend/vulns.go
@@ -135,6 +135,9 @@
 	default: // the path should be "/<ID>", e.g. "/GO-2021-0001".
 		id := r.URL.Path[1:]
 		if !goVulnIDRegexp.MatchString(id) {
+			if r.URL.Query().Has("q") {
+				return &serverError{status: derrors.ToStatus(derrors.NotFound)}
+			}
 			return &serverError{
 				status:       http.StatusBadRequest,
 				responseText: "invalid Go vuln ID; should be GO-YYYY-NNNN",
diff --git a/static/frontend/vuln/main/main.tmpl b/static/frontend/vuln/main/main.tmpl
index dfe177d..b771286 100644
--- a/static/frontend/vuln/main/main.tmpl
+++ b/static/frontend/vuln/main/main.tmpl
@@ -29,6 +29,7 @@
       role="search"
     >
       <input name="q" class="go-Input" placeholder="Search GO IDs" />
+      <input name="m" value="vuln" hidden />
       <button class="go-Button">Submit</button>
     </form>