rc: require authentication to list the remotes with --rc-serve GHSA-mfvx-7rcj-9m5g

With --rc-serve set the root listing enumerated the names of all configured
remotes without any authentication.

Make the root listing obey the same fail-closed rule as the rest of
the rc endpoints: it now requires authentication to be configured or
an explicit opt out with --rc-no-auth.

Addresses GHSA-mfvx-7rcj-9m5g finding 2.
This commit is contained in:
Nick Craig-Wood
2026-07-31 13:21:59 +01:00
parent faaf716e9b
commit 4bb6a1edf6
2 changed files with 54 additions and 0 deletions
+6
View File
@@ -329,6 +329,12 @@ func (s *Server) handleOptions(w http.ResponseWriter, r *http.Request, path stri
}
func (s *Server) serveRoot(w http.ResponseWriter, r *http.Request) {
// Listing the configured remotes discloses their names so
// require auth like the rest of the rc endpoints
if !s.noAuth && !s.server.UsingAuth() {
writeError(r.URL.Path, nil, w, errors.New("listing the remotes requires authentication to be set up on the rc server or the --rc-no-auth flag"), http.StatusForbidden)
return
}
remoteNames := config.GetRemoteNames()
sort.Strings(remoteNames)
directory := serve.NewDirectory("", s.server.HTMLTemplate())
+48
View File
@@ -787,6 +787,7 @@ func TestServingRoot(t *testing.T) {
opt := newTestOpt()
opt.Serve = true
opt.Files = testFs
opt.NoAuth = true
testServer(t, tests, &opt)
}
@@ -800,6 +801,53 @@ func TestServingRootNoFiles(t *testing.T) {
opt := newTestOpt()
opt.Serve = true
opt.Files = ""
opt.NoAuth = true
testServer(t, tests, &opt)
}
// On a server with no authentication configured and without --rc-no-auth the
// remote listing must not disclose the configured remote names.
func TestServingRootFailClosed(t *testing.T) {
forbidden := regexp.MustCompile(`"status": 403`)
tests := []testRun{{
Name: "rootlist-star",
URL: "*",
Status: http.StatusForbidden,
Contains: forbidden,
}, {
Name: "rootlist-empty",
URL: "",
Status: http.StatusForbidden,
Contains: forbidden,
}}
opt := newTestOpt()
opt.Serve = true
opt.Files = ""
testServer(t, tests, &opt)
}
// With authentication configured the remote listing is available to
// authenticated requests only.
func TestServingRootWithAuth(t *testing.T) {
const user, pass = "user", "pass"
tests := []testRun{{
Name: "rootlist-authed",
URL: "",
User: user,
Pass: pass,
Status: http.StatusOK,
Contains: matchRemoteDirListing,
}, {
Name: "rootlist-unauthed",
URL: "",
Status: http.StatusUnauthorized,
Contains: regexp.MustCompile(`Unauthorized`),
}}
opt := newTestOpt()
opt.Serve = true
opt.Files = ""
opt.Auth.BasicUser = user
opt.Auth.BasicPass = pass
testServer(t, tests, &opt)
}