[release-branch.go1.25] cmd/go: reject sumdb response lacking module hash

Report an error when a sumdb /lookup/ request does not
include a hash for the requested module, rather than
silently proceeding.

Previously, we would verify that a returned sum matched
the expected module hash, but did not verify that the
response contained a sum. This permits a malicous
proxy to serve a corrupted module along with a
valid-but-irrelevant sumdb response for some other
module. We now ensure that the sumdb response contains
a valid hash for the module we are validating.

Thanks to Mundur (https://github.com/M0nd0R) for reporting this issue.

Fixes CVE-2026-42501
Updates #79070
Fixes #79072

Change-Id: I7d9a367deb237aa70cade2434495998f6a6a6964
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/4340
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/4421
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/775161
TryBot-Bypass: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go
index 5d310cc..41e02e7 100644
--- a/src/cmd/go/internal/modfetch/fetch.go
+++ b/src/cmd/go/internal/modfetch/fetch.go
@@ -851,7 +851,7 @@
 			return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+sumdbMismatch, noun, h, db, line[len(prefix)-len("h1:"):]))
 		}
 	}
-	return nil
+	return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: checksum missing from sumdb response"+sumdbAbsent, noun))
 }
 
 // Sum returns the checksum for the downloaded copy of the given module,
@@ -1062,6 +1062,19 @@
 For more information, see 'go help module-auth'.
 `
 
+const sumdbAbsent = `
+
+SECURITY ERROR
+This download does NOT match one reported by the checksum server.
+The checksum server has provided checksums, but the checksums do
+not contain an entry for the download.
+The checksum server may be malfunctioning, or an attacker may have
+intercepted the checksum request.
+The download cannot be verified.
+
+For more information, see 'go help module-auth'.
+`
+
 const hashVersionMismatch = `
 
 SECURITY WARNING
diff --git a/src/cmd/go/proxy_test.go b/src/cmd/go/proxy_test.go
index 5ff8136..2a8bace 100644
--- a/src/cmd/go/proxy_test.go
+++ b/src/cmd/go/proxy_test.go
@@ -172,6 +172,23 @@
 		return
 	}
 
+	// Request for $GOPROXY/sumdb-redirect/module@version:/lookup/...
+	// performs a lookup for module@version rather than the requested module.
+	if strings.HasPrefix(path, "sumdb-redirect/") {
+		redirect, rest, ok := strings.Cut(path[len("sumdb-redirect"):], ":")
+		if !ok {
+			w.WriteHeader(500)
+			return
+		}
+		if strings.HasPrefix(rest, "/lookup/") {
+			r.URL.Path = "/lookup" + redirect
+		} else {
+			r.URL.Path = rest
+		}
+		sumdbServer.ServeHTTP(w, r)
+		return
+	}
+
 	// Request for $GOPROXY/redirect/<count>/... goes to redirects.
 	if strings.HasPrefix(path, "redirect/") {
 		path = path[len("redirect/"):]
diff --git a/src/cmd/go/testdata/script/mod_sum_absent.txt b/src/cmd/go/testdata/script/mod_sum_absent.txt
new file mode 100644
index 0000000..c2dd814
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_sum_absent.txt
@@ -0,0 +1,17 @@
+# When the sumdb returns a response which does not
+# include a sum for the requested module,
+# we should report an error.
+# Verifies CVE-2026-42501.
+env sumdb=$GOSUMDB
+env proxy=$GOPROXY
+env GOPROXY GONOPROXY GOSUMDB GONOSUMDB
+
+# /sumdb-redirect/ causes the sumdb to return /lookup/ responses
+# for rsc.io/quote@v1.0.0, not for the requested module.
+env GOSUMDB=$sumdb' '$proxy/sumdb-redirect/rsc.io/quote@v1.0.0:
+
+! go get rsc.io/fortune@v1.0.0
+stderr 'SECURITY ERROR'
+! grep rsc.io go.sum
+-- go.mod --
+module m