From 46bc33819ac86a9596b8059235842f0e0c7469bd Mon Sep 17 00:00:00 2001
From: Bryan C. Mills <bcmills@google.com>
Date: Thu, 2 Nov 2023 15:06:35 -0400
Subject: [PATCH] cmd/go/internal/vcs: error out if the requested repo does not
 support a secure protocol

Updates #63845.
Fixes #63972.

Change-Id: If86d6b13d3b55877b35c087112bd76388c9404b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/539321
Reviewed-by: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
(cherry picked from commit be26ae18caf7ddffca4073333f80d0d9e76483c3)
Reviewed-on: https://go-review.googlesource.com/c/go/+/540335
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>

CVE: CVE-2023-45285

Upstream-Status: Backport [https://github.com/golang/go/commit/46bc33819ac86a9596b8059235842f0e0c7469bd]

Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
---
 src/cmd/go/internal/vcs/vcs.go                | 25 +++++++++++++----
 .../script/mod_insecure_issue63845.txt        | 28 +++++++++++++++++++
 2 files changed, 47 insertions(+), 6 deletions(-)
 create mode 100644 src/cmd/go/testdata/script/mod_insecure_issue63845.txt

diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go
index ab42424..0e2882d 100644
--- a/src/cmd/go/internal/vcs/vcs.go
+++ b/src/cmd/go/internal/vcs/vcs.go
@@ -891,19 +891,32 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
		if !srv.schemelessRepo {
			repoURL = match["repo"]
		} else {
-			scheme := vcs.Scheme[0] // default to first scheme
			repo := match["repo"]
-			if vcs.PingCmd != "" {
-				// If we know how to test schemes, scan to find one.
+			scheme, err := func() (string, error) {
				for _, s := range vcs.Scheme {
					if security == web.SecureOnly && !vcs.isSecureScheme(s) {
						continue
					}
-					if vcs.Ping(s, repo) == nil {
-						scheme = s
-						break
+
+					// If we know how to ping URL schemes for this VCS,
+					// check that this repo works.
+					// Otherwise, default to the first scheme
+					// that meets the requested security level.
+					if vcs.PingCmd == "" {
+						return s, nil
+					}
+					if err := vcs.Ping(s, repo); err == nil {
+						return s, nil
					}
				}
+				securityFrag := ""
+				if security == web.SecureOnly {
+					securityFrag = "secure "
+				}
+				return "", fmt.Errorf("no %sprotocol found for repository", securityFrag)
+			}()
+			if err != nil {
+				return nil, err
			}
			repoURL = scheme + "://" + repo
		}
diff --git a/src/cmd/go/testdata/script/mod_insecure_issue63845.txt b/src/cmd/go/testdata/script/mod_insecure_issue63845.txt
new file mode 100644
index 0000000..5fa6a4f
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_insecure_issue63845.txt
@@ -0,0 +1,28 @@
+# Regression test for https://go.dev/issue/63845:
+# If 'git ls-remote' fails for all secure protocols,
+# we should fail instead of falling back to an arbitrary protocol.
+#
+# Note that this test does not use the local vcweb test server
+# (vcs-test.golang.org), because the hook for redirecting to that
+# server bypasses the "ping to determine protocol" logic
+# in cmd/go/internal/vcs.
+
+[!net] skip
+[!git] skip
+[short] skip 'tries to access a nonexistent external Git repo'
+
+env GOPRIVATE=golang.org
+env CURLOPT_TIMEOUT_MS=100
+env GIT_SSH_COMMAND=false
+
+! go get -x golang.org/nonexist.git@latest
+stderr '^git ls-remote https://golang.org/nonexist$'
+stderr '^git ls-remote git\+ssh://golang.org/nonexist'
+stderr '^git ls-remote ssh://golang.org/nonexist$'
+! stderr 'git://'
+stderr '^go: golang.org/nonexist.git@latest: no secure protocol found for repository$'
+
+-- go.mod --
+module example
+
+go 1.19
--
2.40.0
