mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net/url: disallow raw IPv6 addresses in host
RFC 3986 requires square brackets around IPv6 addresses. Parse's acceptance of raw IPv6 addresses is non compliant, and complicates splitting out a port. This is a resubmission of CL 710176 after the revert in CL 711800, this time with a new urlstrictipv6 godebug to control the behavior. Fixes #31024 Fixes #75223 Change-Id: I4cbe5bb84266b3efe9c98cf4300421ddf1df7291 Reviewed-on: https://go-review.googlesource.com/c/go/+/712840 Reviewed-by: Junyang Shao <shaojunyang@google.com> Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
4e761b9a18
commit
0c28789bd7
6 changed files with 49 additions and 21 deletions
|
|
@ -163,6 +163,11 @@ will fail early. The default value is `httpcookiemaxnum=3000`. Setting
|
||||||
number of cookies. To avoid denial of service attacks, this setting and default
|
number of cookies. To avoid denial of service attacks, this setting and default
|
||||||
was backported to Go 1.25.2 and Go 1.24.8.
|
was backported to Go 1.25.2 and Go 1.24.8.
|
||||||
|
|
||||||
|
Go 1.26 added a new `urlstrictcolons` setting that controls whether `net/url.Parse`
|
||||||
|
allows malformed hostnames containing colons outside of a bracketed IPv6 address.
|
||||||
|
The default `urlstrictcolons=1` rejects URLs such as `http://localhost:1:2` or `http://::1/`.
|
||||||
|
Colons are permitted as part of a bracketed IPv6 address, such as `http://[::1]/`.
|
||||||
|
|
||||||
### Go 1.25
|
### Go 1.25
|
||||||
|
|
||||||
Go 1.25 added a new `decoratemappings` setting that controls whether the Go
|
Go 1.25 added a new `decoratemappings` setting that controls whether the Go
|
||||||
|
|
|
||||||
4
doc/next/6-stdlib/99-minor/net/url/31024.md
Normal file
4
doc/next/6-stdlib/99-minor/net/url/31024.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Parse] now rejects malformed URLs containing colons in the host subcomponent,
|
||||||
|
such as `http://::1/` or `http://localhost:80:80/`.
|
||||||
|
URLs containing bracketed IPv6 addresses, such as `http://[::1]/` are still accepted.
|
||||||
|
The new GODEBUG=urlstrictcolons=0 setting restores the old behavior.
|
||||||
|
|
@ -67,6 +67,7 @@ var All = []Info{
|
||||||
{Name: "tlssha1", Package: "crypto/tls", Changed: 25, Old: "1"},
|
{Name: "tlssha1", Package: "crypto/tls", Changed: 25, Old: "1"},
|
||||||
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
|
||||||
{Name: "updatemaxprocs", Package: "runtime", Changed: 25, Old: "0"},
|
{Name: "updatemaxprocs", Package: "runtime", Changed: 25, Old: "0"},
|
||||||
|
{Name: "urlstrictcolons", Package: "net/url", Changed: 26, Old: "0"},
|
||||||
{Name: "winreadlinkvolume", Package: "os", Changed: 23, Old: "0"},
|
{Name: "winreadlinkvolume", Package: "os", Changed: 23, Old: "0"},
|
||||||
{Name: "winsymlink", Package: "os", Changed: 23, Old: "0"},
|
{Name: "winsymlink", Package: "os", Changed: 23, Old: "0"},
|
||||||
{Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"},
|
{Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"},
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package url
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/godebug"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"path"
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
@ -26,6 +27,8 @@ import (
|
||||||
_ "unsafe" // for linkname
|
_ "unsafe" // for linkname
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var urlstrictcolons = godebug.New("urlstrictcolons")
|
||||||
|
|
||||||
// Error reports an error and the operation and URL that caused it.
|
// Error reports an error and the operation and URL that caused it.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Op string
|
Op string
|
||||||
|
|
@ -599,7 +602,11 @@ func parseHost(host string) (string, error) {
|
||||||
return "", errors.New("invalid IP-literal")
|
return "", errors.New("invalid IP-literal")
|
||||||
}
|
}
|
||||||
return "[" + unescapedHostname + "]" + unescapedColonPort, nil
|
return "[" + unescapedHostname + "]" + unescapedColonPort, nil
|
||||||
} else if i := strings.LastIndex(host, ":"); i != -1 {
|
} else if i := strings.Index(host, ":"); i != -1 {
|
||||||
|
if j := strings.LastIndex(host, ":"); urlstrictcolons.Value() == "0" && j != i {
|
||||||
|
urlstrictcolons.IncNonDefault()
|
||||||
|
i = j
|
||||||
|
}
|
||||||
colonPort := host[i:]
|
colonPort := host[i:]
|
||||||
if !validOptionalPort(colonPort) {
|
if !validOptionalPort(colonPort) {
|
||||||
return "", fmt.Errorf("invalid port %q after host", colonPort)
|
return "", fmt.Errorf("invalid port %q after host", colonPort)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -506,26 +507,6 @@ var urltests = []URLTest{
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// Malformed IPv6 but still accepted.
|
|
||||||
"http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080/foo",
|
|
||||||
&URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080",
|
|
||||||
Path: "/foo",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Malformed IPv6 but still accepted.
|
|
||||||
"http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:/foo",
|
|
||||||
&URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:",
|
|
||||||
Path: "/foo",
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080/foo",
|
"http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080/foo",
|
||||||
&URL{
|
&URL{
|
||||||
|
|
@ -735,6 +716,9 @@ var parseRequestURLTests = []struct {
|
||||||
{"https://[0:0::test.com]:80", false},
|
{"https://[0:0::test.com]:80", false},
|
||||||
{"https://[2001:db8::test.com]", false},
|
{"https://[2001:db8::test.com]", false},
|
||||||
{"https://[test.com]", false},
|
{"https://[test.com]", false},
|
||||||
|
{"https://1:2:3:4:5:6:7:8", false},
|
||||||
|
{"https://1:2:3:4:5:6:7:8:80", false},
|
||||||
|
{"https://example.com:80:", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRequestURI(t *testing.T) {
|
func TestParseRequestURI(t *testing.T) {
|
||||||
|
|
@ -2280,3 +2264,25 @@ func TestJoinPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseStrictIpv6(t *testing.T) {
|
||||||
|
t.Setenv("GODEBUG", "urlstrictcolons=0")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
url string
|
||||||
|
}{
|
||||||
|
// Malformed URLs that used to parse.
|
||||||
|
{"https://1:2:3:4:5:6:7:8"},
|
||||||
|
{"https://1:2:3:4:5:6:7:8:80"},
|
||||||
|
{"https://example.com:80:"},
|
||||||
|
}
|
||||||
|
for i, tc := range tests {
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
_, err := Parse(tc.url)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Parse(%q) error = %v, want nil", tc.url, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,11 @@ Below is the full list of supported metrics, ordered lexicographically.
|
||||||
The number of non-default behaviors executed by the runtime
|
The number of non-default behaviors executed by the runtime
|
||||||
package due to a non-default GODEBUG=updatemaxprocs=... setting.
|
package due to a non-default GODEBUG=updatemaxprocs=... setting.
|
||||||
|
|
||||||
|
/godebug/non-default-behavior/urlstrictcolons:events
|
||||||
|
The number of non-default behaviors executed by the net/url
|
||||||
|
package due to a non-default GODEBUG=urlstrictcolons=...
|
||||||
|
setting.
|
||||||
|
|
||||||
/godebug/non-default-behavior/winreadlinkvolume:events
|
/godebug/non-default-behavior/winreadlinkvolume:events
|
||||||
The number of non-default behaviors executed by the os package
|
The number of non-default behaviors executed by the os package
|
||||||
due to a non-default GODEBUG=winreadlinkvolume=... setting.
|
due to a non-default GODEBUG=winreadlinkvolume=... setting.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue