"errors"
"fmt"
"internal/goroot"
+ "io/fs"
"os"
"path/filepath"
"strings"
return
}
- if info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
+ info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed)
+ var noVersionErr *NoMatchingVersionError
+ if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
+ // Ignore "not found" and "no matching version" errors. This usually means
+ // the user is offline or the proxy doesn't have a matching version.
+ //
+ // We should report other errors though. An attacker that controls the
+ // network shouldn't be able to hide versions by interfering with
+ // the HTTPS connection. An attacker that controls the proxy may still
+ // hide versions, since the "list" and "latest" endpoints are not
+ // authenticated.
+ return
+ } else if err != nil {
+ if m.Error == nil {
+ m.Error = &modinfo.ModuleError{Err: err.Error()}
+ }
+ return
+ }
+
+ if semver.Compare(info.Version, m.Version) > 0 {
m.Update = &modinfo.ModulePublic{
Path: m.Path,
Version: info.Version,
}
err := CheckRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
- var rerr *ModuleRetractedError
- if errors.As(err, &rerr) {
- if len(rerr.Rationale) == 0 {
+ var noVersionErr *NoMatchingVersionError
+ var retractErr *ModuleRetractedError
+ if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
+ // Ignore "not found" and "no matching version" errors. This usually means
+ // the user is offline or the proxy doesn't have a go.mod file that could
+ // contain retractions.
+ //
+ // We should report other errors though. An attacker that controls the
+ // network shouldn't be able to hide versions by interfering with
+ // the HTTPS connection. An attacker that controls the proxy may still
+ // hide versions, since the "list" and "latest" endpoints are not
+ // authenticated.
+ return
+ } else if errors.As(err, &retractErr) {
+ if len(retractErr.Rationale) == 0 {
m.Retracted = []string{"retracted by module author"}
} else {
- m.Retracted = rerr.Rationale
+ m.Retracted = retractErr.Rationale
}
- } else if err != nil && m.Error == nil {
+ } else if m.Error == nil {
m.Error = &modinfo.ModuleError{Err: err.Error()}
}
}
go list -m -f '{{with .Retracted}}retracted{{end}}' example.com/retract@v1.0.0-unused
! stdout .
-# 'go list -m -retracted mod@version' shows an error if the go.mod that should
-# contain the retractions is not available.
-! go list -m -retracted example.com/retract/missingmod@v1.0.0
-stderr '^go list -m: loading module retractions for example.com/retract/missingmod@v1.0.0: .*404 Not Found$'
-go list -e -m -retracted -f '{{.Error.Err}}' example.com/retract/missingmod@v1.0.0
-stdout '^loading module retractions for example.com/retract/missingmod@v1.0.0: .*404 Not Found$'
+# 'go list -m -retracted mod@version' does not show an error if the module
+# that would contain the retraction is unavailable. See #45305.
+go list -m -retracted -f '{{.Path}} {{.Version}} {{.Error}}' example.com/retract/missingmod@v1.0.0
+stdout '^example.com/retract/missingmod v1.0.0 <nil>$'
+exists $GOPATH/pkg/mod/cache/download/example.com/retract/missingmod/@v/v1.9.0.info
+! exists $GOPATH/pkg/mod/cache/download/example.com/retract/missingmod/@v/v1.9.0.mod
# 'go list -m -retracted mod@version' shows retractions.
go list -m -retracted example.com/retract@v1.0.0-unused
--- /dev/null
+# Check that if a proxy does not have a version of a module that could be
+# an upgrade, 'go list -m -u' still succeeds.
+# We use a local file proxy, since our test proxy doesn't have the behavior
+# we want to test, and we don't want it to be too clever.
+# Verifies #45305, where proxy.golang.org serves an empty /@v/list (200)
+# but has no /@latest (410) because the go.mod at the tip of the default
+# branch has a different major version suffix.
+env testproxy=$GOPROXY
+env GOPROXY=file://$WORK/proxy
+env GOSUMDB=off
+
+# If the proxy does not return a list of versions (404/410)
+# or a latest version (404/410), we should see no error.
+go list -m example.com/noversion
+stdout '^example.com/noversion v0.0.0$'
+go list -m -u example.com/noversion
+stdout '^example.com/noversion v0.0.0$'
+
+# If the proxy returns an empty list of versions (200, not 404/410)
+# but does not have a latest version (404/410), we should see no error.
+go list -m example.com/nolatest
+stdout '^example.com/nolatest v0.0.0$'
+go list -m -u example.com/nolatest
+stdout '^example.com/nolatest v0.0.0$'
+
+# If proxy returns an invalid response, we should see an error.
+env GOPROXY=$testproxy/invalid
+! go list -m -u example.com/nolatest
+stderr '^go list -m: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$'
+
+-- go.mod --
+module m
+
+go 1.17
+
+require (
+ example.com/nolatest v0.0.0
+ example.com/noversion v0.0.0
+)
+-- go.sum --
+example.com/nolatest v0.0.0/go.mod h1:HnLrCt6SJga5tCtJ7IzG9dOOCniY3G5C0VT7jfMdS0M=
+example.com/noversion v0.0.0/go.mod h1:2RUfWiCYsygSXPM2Igxx0FD3Kq33OnVdxm34eDDhXbQ=
+-- $WORK/proxy/example.com/nolatest/@v/list --
+-- $WORK/proxy/example.com/nolatest/@v/v0.0.0.info --
+{"Version":"v0.0.0"}
+-- $WORK/proxy/example.com/nolatest/@v/v0.0.0.mod --
+module example.com/nolatest
+
+go 1.17
+-- $WORK/proxy/example.com/noversion/@v/v0.0.0.info --
+{"Version":"v0.0.0"}
+-- $WORK/proxy/example.com/noversion/@v/v0.0.0.mod --
+module example.com/noversion
+
+go 1.17
go get -d
# The latest version, v1.9.0, is not available on the proxy.
-! go list -m -retracted example.com/retract/missingmod
-stderr '^go list -m: loading module retractions for example.com/retract/missingmod@v1.0.0: .*404 Not Found$'
+go list -m -retracted example.com/retract/missingmod
+stdout '^example.com/retract/missingmod v1.0.0$'
+exists $GOPATH/pkg/mod/cache/download/example.com/retract/missingmod/@v/v1.9.0.info
+! exists $GOPATH/pkg/mod/cache/download/example.com/retract/missingmod/@v/v1.9.0.mod
# If we replace that version, we should see retractions.
go mod edit -replace=example.com/retract/missingmod@v1.9.0=./missingmod-v1.9.0