internal/postgres: exclusions should be case insensitive

Update exclusion logic to be case insensitive. When there is no go.mod
file, GitHub module paths may be spelled with any casing.

As a result of this change, we will no longer be able to exclude only
certain capitalizations of a module.

Change-Id: I67342cf3543e795e7795aa74081541d7e6fed18f
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/576595
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
kokoro-CI: kokoro <noreply+kokoro@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
diff --git a/internal/postgres/excluded.go b/internal/postgres/excluded.go
index fea9b17..176b9c7 100644
--- a/internal/postgres/excluded.go
+++ b/internal/postgres/excluded.go
@@ -30,19 +30,23 @@
 }
 
 func excludes(pattern, path, version string) bool {
-	// Patterns with "@" must match exactly.
+	// Certain hosts (such as GitHub) are case insensitive.
+	// Therefore, we err on the side of insensitive exclusions.
+
+	// Patterns with "@" must match the full path (case insensitively).
 	mod, ver, found := strings.Cut(pattern, "@")
 	if found {
-		return mod == path && ver == version
+		return strings.EqualFold(mod, path) && ver == version
 	}
-	// Patterns without "@" can match exactly or be a componentwise prefix.
-	if pattern == path {
+	// Patterns without "@" can match the full path or be a componentwise prefix
+	// (case insensitively).
+	if strings.EqualFold(pattern, path) {
 		return true
 	}
 	if !strings.HasSuffix(pattern, "/") {
 		pattern += "/"
 	}
-	return strings.HasPrefix(path, pattern)
+	return strings.HasPrefix(strings.ToLower(path), strings.ToLower(pattern))
 }
 
 // InsertExcludedPattern inserts pattern into the excluded_prefixes table.
diff --git a/internal/postgres/excluded_test.go b/internal/postgres/excluded_test.go
index ac696d4..13206a8 100644
--- a/internal/postgres/excluded_test.go
+++ b/internal/postgres/excluded_test.go
@@ -15,7 +15,7 @@
 	defer release()
 	ctx := context.Background()
 
-	for _, pat := range []string{"bad", "badslash/", "baddy@v1.2.3"} {
+	for _, pat := range []string{"bad", "badslash/", "baddy@v1.2.3", "github.com/bad"} {
 		if err := testDB.InsertExcludedPattern(ctx, pat, "someone", "because"); err != nil {
 			t.Fatal(err)
 		}
@@ -38,6 +38,16 @@
 		{"baddy", "v1.2.4", false},
 		{"baddy", "", false},
 		{"baddy", "v1.2.3", true},
+
+		// tests for case insensitivity
+		{"Bad", "", true},
+		{"Bad/repo", "", true},
+		{"baDDy", "v1.2.3", true},
+		{"baDDy", "v1.2.4", false},
+		{"github.com/Bad", "", true},
+		{"github.com/bad/repo", "", true},
+		{"github.com/bad/Repo", "", true},
+		{"github.com/Bad/repo", "", true},
 	} {
 		got := testDB.IsExcluded(ctx, test.path, test.version)
 		if got != test.want {