Skip to content

Commit 2a193b5

Browse files
authored
Merge pull request #82 from projectdiscovery/issue-81-url-char-format
use standard url encoding format
2 parents 1cba687 + bd8b9dc commit 2a193b5

2 files changed

Lines changed: 35 additions & 9 deletions

File tree

url/rawparam.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,15 @@ func ParamEncode(data string) string {
163163
func URLEncodeWithEscapes(data string, charset ...rune) string {
164164
mustescape := getrunemap(charset)
165165
var buff bytes.Buffer
166-
totallen := len(data)
167166
// In any case
168-
buff.Grow(totallen)
167+
buff.Grow(len(data))
169168

170169
for _, r := range data {
171-
switch true {
170+
switch {
172171
case r < rune(20):
173172
// control character
174173
buff.WriteRune('%')
175-
buff.WriteString(strconv.FormatInt(int64(r), 16)) // 2 digit hex
174+
buff.WriteString(getasciihex(r)) // 2 digit hex
176175
case r == ' ':
177176
// prefer using + when space
178177
buff.WriteRune('+')
@@ -181,15 +180,15 @@ func URLEncodeWithEscapes(data string, charset ...rune) string {
181180
if _, ok := mustescape[r]; ok {
182181
// reserved char must escape
183182
buff.WriteRune('%')
184-
buff.WriteString(strconv.FormatInt(int64(r), 16))
183+
buff.WriteString(getasciihex(r))
185184
} else {
186185
// do not percent encode
187186
buff.WriteRune(r)
188187
}
189188
case r == rune(127):
190189
// [DEL] char should be encoded
191190
buff.WriteRune('%')
192-
buff.WriteString(strconv.FormatInt(int64(r), 16))
191+
buff.WriteString(getasciihex(r))
193192
case r > rune(128):
194193
// non-ascii characters i.e chinese chars or any other utf-8
195194
buff.WriteRune('%')
@@ -209,7 +208,7 @@ func PercentEncoding(data string) string {
209208
buff.WriteRune('%')
210209
if r <= rune(127) {
211210
// these are all ascii characters
212-
buff.WriteString(strconv.FormatInt(int64(r), 16))
211+
buff.WriteString(getasciihex(r))
213212
} else {
214213
// unicode characters
215214
buff.WriteString(getutf8hex(r))
@@ -238,6 +237,7 @@ func getrunemap(runes []rune) map[rune]struct{} {
238237
return x
239238
}
240239

240+
// returns hex value of utf-8 non-ascii char
241241
func getutf8hex(r rune) string {
242242
// Percent Encoding is only done in hexadecimal values and in ASCII Range only
243243
// other UTF-8 chars (chinese etc) can be used by utf-8 encoding and byte conversion
@@ -253,3 +253,13 @@ func getutf8hex(r rune) string {
253253
}
254254
return buff.String()
255255
}
256+
257+
// returns hex value of ascii char
258+
func getasciihex(r rune) string {
259+
val := strconv.FormatInt(int64(r), 16)
260+
if len(val) == 1 {
261+
// append 0 formatInt skips it by default
262+
val = "0" + val
263+
}
264+
return strings.ToUpper(val)
265+
}

url/rawparam_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,13 @@ func TestParamIntegration(t *testing.T) {
6969

7070
func TestPercentEncoding(t *testing.T) {
7171
// From Burpsuite
72-
expected := "%74%65%73%74%20%26%23%20%28%29%20%70%65%72%63%65%6e%74%20%5b%5d%7c%2a%20%65%6e%63%6f%64%69%6e%67"
72+
expected := "%74%65%73%74%20%26%23%20%28%29%20%70%65%72%63%65%6E%74%20%5B%5D%7C%2A%20%65%6E%63%6F%64%69%6E%67"
7373
payload := "test &# () percent []|* encoding"
7474
value := PercentEncoding(payload)
75-
require.Equalf(t, value, expected, "expected percentencoding to be %v but got %v", expected, payload)
75+
require.Equalf(t, value, expected, "expected percentencoding to be %v but got %v", expected, value)
76+
decoded, err := url.QueryUnescape(value)
77+
require.Nil(t, err)
78+
require.Equal(t, payload, decoded)
7679
}
7780

7881
func TestGetParams(t *testing.T) {
@@ -84,3 +87,16 @@ func TestGetParams(t *testing.T) {
8487
require.Equalf(t, p.Get("sqli"), values.Get("sqli"), "malformed or missing value for param sqli expected %v but got %v", values.Get("sqli"), p.Get("sqli"))
8588
require.Equalf(t, p.Get("xss"), values.Get("xss"), "malformed or missing value for param xss expected %v but got %v", values.Get("xss"), p.Get("xss"))
8689
}
90+
91+
func TestURLEncode(t *testing.T) {
92+
example := "\r\n"
93+
got := URLEncodeWithEscapes(example)
94+
require.Equalf(t, "%0D%0A", got, "failed to url encode characters")
95+
96+
// verify with stdlib
97+
for r := 0; r < 20; r++ {
98+
expected := url.QueryEscape(string(rune(r)))
99+
got := URLEncodeWithEscapes(string(rune(r)))
100+
require.Equalf(t, expected, got, "url encoding mismatch for non-printable char with ascii val:%v", r)
101+
}
102+
}

0 commit comments

Comments
 (0)