From: Florian Forster Date: Wed, 7 Mar 2018 09:45:00 +0000 (+0100) Subject: net/url: escape URL.RawQuery on Parse if it contains invalid characters X-Git-Tag: go1.12beta1~1372 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1040626c0ce4a1bc2b312aa0866ffeb2ff53c1ab;p=gostls13.git net/url: escape URL.RawQuery on Parse if it contains invalid characters Fixes #22907 Change-Id: I7abcf53ab92768514e13ce2554a6c25dcde8218e Reviewed-on: https://go-review.googlesource.com/99135 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/net/url/url.go b/src/net/url/url.go index 80eb7a86c8..4943ea6d67 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -515,7 +515,13 @@ func parse(rawurl string, viaRequest bool) (*URL, error) { url.ForceQuery = true rest = rest[:len(rest)-1] } else { - rest, url.RawQuery = split(rest, "?", true) + var q string + rest, q = split(rest, "?", true) + if validQuery(q) { + url.RawQuery = q + } else { + url.RawQuery = QueryEscape(q) + } } if !strings.HasPrefix(rest, "/") { @@ -1114,3 +1120,46 @@ func validUserinfo(s string) bool { } return true } + +// validQuery reports whether s is a valid query string per RFC 3986 +// Section 3.4: +// query = *( pchar / "/" / "?" ) +// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" +// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" +// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" +// / "*" / "+" / "," / ";" / "=" +func validQuery(s string) bool { + pctEnc := 0 + + for _, r := range s { + if pctEnc > 0 { + if uint32(r) > 255 || !ishex(byte(r)) { + return false + } + pctEnc-- + continue + } else if r == '%' { + pctEnc = 2 + continue + } + + if 'A' <= r && r <= 'Z' { + continue + } + if 'a' <= r && r <= 'z' { + continue + } + if '0' <= r && r <= '9' { + continue + } + switch r { + case '-', '.', '_', '~', '!', '$', '&', '\'', '(', ')', + '*', '+', ',', ';', '=', ':', '@', '/', '?': + continue + default: + return false + } + } + + return true +} diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 9043a844e8..19d4d636d6 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -590,6 +590,16 @@ var urltests = []URLTest{ }, "mailto:?subject=hi", }, + { + "https://example.com/search?q=Фотки собак&source=lnms", + &URL{ + Scheme: "https", + Host: "example.com", + Path: "/search", + RawQuery: "q%3D%D0%A4%D0%BE%D1%82%D0%BA%D0%B8+%D1%81%D0%BE%D0%B1%D0%B0%D0%BA%26source%3Dlnms", + }, + "https://example.com/search?q%3D%D0%A4%D0%BE%D1%82%D0%BA%D0%B8+%D1%81%D0%BE%D0%B1%D0%B0%D0%BA%26source%3Dlnms", + }, } // more useful string for debugging than fmt's struct printer @@ -1439,6 +1449,7 @@ func TestParseErrors(t *testing.T) { {"cache_object:foo", true}, {"cache_object:foo/bar", true}, {"cache_object/:foo/bar", false}, + {"https://example.com/search?q=Фотки собак&source=lnms", false}, } for _, tt := range tests { u, err := Parse(tt.in)