]> Cypherpunks repositories - gostls13.git/commitdiff
net/url: escape URL.RawQuery on Parse if it contains invalid characters
authorFlorian Forster <octo@google.com>
Wed, 7 Mar 2018 09:45:00 +0000 (10:45 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 21 Aug 2018 14:05:03 +0000 (14:05 +0000)
Fixes #22907

Change-Id: I7abcf53ab92768514e13ce2554a6c25dcde8218e
Reviewed-on: https://go-review.googlesource.com/99135
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/url/url.go
src/net/url/url_test.go

index 80eb7a86c8de26f3e3110c1f0da58a4281d8b2ef..4943ea6d67ceb54b0147c169551f4730efa4d6ef 100644 (file)
@@ -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
+}
index 9043a844e88f800e8d5b206fba665ccbc2187efe..19d4d636d62217da1751f06ea5e249ea0bbdc72f 100644 (file)
@@ -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)