Skip to content

Commit 1040626

Browse files
octobradfitz
authored andcommitted
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 1d1a027 commit 1040626

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/net/url/url.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,13 @@ func parse(rawurl string, viaRequest bool) (*URL, error) {
515515
url.ForceQuery = true
516516
rest = rest[:len(rest)-1]
517517
} else {
518-
rest, url.RawQuery = split(rest, "?", true)
518+
var q string
519+
rest, q = split(rest, "?", true)
520+
if validQuery(q) {
521+
url.RawQuery = q
522+
} else {
523+
url.RawQuery = QueryEscape(q)
524+
}
519525
}
520526

521527
if !strings.HasPrefix(rest, "/") {
@@ -1114,3 +1120,46 @@ func validUserinfo(s string) bool {
11141120
}
11151121
return true
11161122
}
1123+
1124+
// validQuery reports whether s is a valid query string per RFC 3986
1125+
// Section 3.4:
1126+
// query = *( pchar / "/" / "?" )
1127+
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1128+
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1129+
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1130+
// / "*" / "+" / "," / ";" / "="
1131+
func validQuery(s string) bool {
1132+
pctEnc := 0
1133+
1134+
for _, r := range s {
1135+
if pctEnc > 0 {
1136+
if uint32(r) > 255 || !ishex(byte(r)) {
1137+
return false
1138+
}
1139+
pctEnc--
1140+
continue
1141+
} else if r == '%' {
1142+
pctEnc = 2
1143+
continue
1144+
}
1145+
1146+
if 'A' <= r && r <= 'Z' {
1147+
continue
1148+
}
1149+
if 'a' <= r && r <= 'z' {
1150+
continue
1151+
}
1152+
if '0' <= r && r <= '9' {
1153+
continue
1154+
}
1155+
switch r {
1156+
case '-', '.', '_', '~', '!', '$', '&', '\'', '(', ')',
1157+
'*', '+', ',', ';', '=', ':', '@', '/', '?':
1158+
continue
1159+
default:
1160+
return false
1161+
}
1162+
}
1163+
1164+
return true
1165+
}

src/net/url/url_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,16 @@ var urltests = []URLTest{
590590
},
591591
"mailto:?subject=hi",
592592
},
593+
{
594+
"https://example.com/search?q=Фотки собак&source=lnms",
595+
&URL{
596+
Scheme: "https",
597+
Host: "example.com",
598+
Path: "/search",
599+
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",
600+
},
601+
"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",
602+
},
593603
}
594604

595605
// more useful string for debugging than fmt's struct printer
@@ -1439,6 +1449,7 @@ func TestParseErrors(t *testing.T) {
14391449
{"cache_object:foo", true},
14401450
{"cache_object:foo/bar", true},
14411451
{"cache_object/:foo/bar", false},
1452+
{"https://example.com/search?q=Фотки собак&source=lnms", false},
14421453
}
14431454
for _, tt := range tests {
14441455
u, err := Parse(tt.in)

0 commit comments

Comments
 (0)