diff --git a/src/cmd/go/internal/auth/auth.go b/src/cmd/go/internal/auth/auth.go index b008e9c2818..bd80222427a 100644 --- a/src/cmd/go/internal/auth/auth.go +++ b/src/cmd/go/internal/auth/auth.go @@ -12,7 +12,6 @@ import ( "log" "net/http" "os" - "path" "path/filepath" "slices" "strings" @@ -73,7 +72,12 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { if err != nil { base.Fatalf("go: could not parse netrc (GOAUTH=%s): %v", cfg.GOAUTH, err) } - for _, l := range lines { + // Process lines in reverse so that if the same machine is listed + // multiple times, we end up saving the earlier one + // (overwriting later ones). This matches the way the go command + // worked before GOAUTH. + for i := len(lines) - 1; i >= 0; i-- { + l := lines[i] r := http.Request{Header: make(http.Header)} r.SetBasicAuth(l.login, l.password) storeCredential(l.machine, r.Header) @@ -137,11 +141,13 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { func loadCredential(req *http.Request, url string) bool { currentPrefix := strings.TrimPrefix(url, "https://") // Iteratively try prefixes, moving up the path hierarchy. - for currentPrefix != "/" && currentPrefix != "." && currentPrefix != "" { + for { headers, ok := credentialCache.Load(currentPrefix) if !ok { - // Move to the parent directory. - currentPrefix = path.Dir(currentPrefix) + currentPrefix, _, ok = strings.Cut(currentPrefix, "/") + if !ok { + return false + } continue } for key, values := range headers.(http.Header) { @@ -151,7 +157,6 @@ func loadCredential(req *http.Request, url string) bool { } return true } - return false } // storeCredential caches or removes credentials (represented by HTTP headers) diff --git a/src/cmd/go/internal/auth/auth_test.go b/src/cmd/go/internal/auth/auth_test.go index c7b4851e288..c1bbf4b1a91 100644 --- a/src/cmd/go/internal/auth/auth_test.go +++ b/src/cmd/go/internal/auth/auth_test.go @@ -25,7 +25,29 @@ func TestCredentialCache(t *testing.T) { got := &http.Request{Header: make(http.Header)} ok := loadCredential(got, tc.machine) if !ok || !reflect.DeepEqual(got.Header, want.Header) { - t.Errorf("loadCredential:\nhave %q\nwant %q", got.Header, want.Header) + t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header) + } + } + + // Having stored those credentials, we should be able to look up longer URLs too. + extraCases := []netrcLine{ + {"https://api.github.com/foo", "user", "pwd"}, + {"https://api.github.com/foo/bar/baz", "user", "pwd"}, + {"https://example.com/abc", "", ""}, + {"https://example.com/?/../api.github.com/", "", ""}, + {"https://example.com/?/../api.github.com", "", ""}, + {"https://example.com/../api.github.com/", "", ""}, + {"https://example.com/../api.github.com", "", ""}, + } + for _, tc := range extraCases { + want := http.Request{Header: make(http.Header)} + if tc.login != "" { + want.SetBasicAuth(tc.login, tc.password) + } + got := &http.Request{Header: make(http.Header)} + loadCredential(got, tc.machine) + if !reflect.DeepEqual(got.Header, want.Header) { + t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header) } } } diff --git a/src/cmd/go/testdata/script/goauth_netrc.txt b/src/cmd/go/testdata/script/goauth_netrc.txt index 2dda119e825..26e03f8968c 100644 --- a/src/cmd/go/testdata/script/goauth_netrc.txt +++ b/src/cmd/go/testdata/script/goauth_netrc.txt @@ -2,8 +2,6 @@ # credentials passed in HTTPS requests to VCS servers. # See golang.org/issue/26232 -[short] skip - env GOPROXY=direct env GOSUMDB=off @@ -55,7 +53,6 @@ go get vcs-test.golang.org/auth/or401 env NETRC=$WORK/missing ! go get vcs-test.golang.org/auth/or401 stderr '^\tserver response: ACCESS DENIED, buddy$' - -- go.mod -- module private.example.com -- $WORK/empty -- @@ -63,3 +60,7 @@ module private.example.com machine vcs-test.golang.org login aladdin password opensesame +# first one should override this one +machine vcs-test.golang.org + login aladdin + password ignored