Skip to content

Commit 75dcdb8

Browse files
Fix username:password@ validation in urls (#2080)
1 parent ede09fa commit 75dcdb8

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

uri.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,11 @@ func (u *URI) parse(host, uri []byte, isTLS bool) error {
298298
u.SetSchemeBytes(strHTTPS)
299299
}
300300

301-
if n := bytes.IndexByte(host, '@'); n >= 0 {
301+
if n := bytes.LastIndexByte(host, '@'); n >= 0 {
302302
auth := host[:n]
303+
if !validUserinfo(auth) {
304+
return ErrorInvalidURI
305+
}
303306
host = host[n+1:]
304307

305308
if n := bytes.IndexByte(auth, ':'); n >= 0 {
@@ -356,6 +359,26 @@ func (u *URI) parse(host, uri []byte, isTLS bool) error {
356359
return nil
357360
}
358361

362+
func validUserinfo(userinfo []byte) bool {
363+
for _, c := range userinfo {
364+
switch {
365+
case 'A' <= c && c <= 'Z':
366+
continue
367+
case 'a' <= c && c <= 'z':
368+
continue
369+
case '0' <= c && c <= '9':
370+
continue
371+
}
372+
switch c {
373+
case '-', '.', '_', ':', '~', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', '%', '@':
374+
continue
375+
default:
376+
return false
377+
}
378+
}
379+
return true
380+
}
381+
359382
// parseHost parses host as an authority without user
360383
// information. That is, as host[:port].
361384
//

uri_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,44 @@ func testURIUpdate(t *testing.T, base, update, result string) {
151151
}
152152
}
153153

154+
func TestURIRejectInvalidUserinfo(t *testing.T) {
155+
t.Parallel()
156+
157+
bad := []string{
158+
"http://[normal.com@]vulndetector.com/",
159+
"http://normal.com[user@vulndetector].com/",
160+
"http://normal.com[@]vulndetector.com/",
161+
}
162+
163+
for _, raw := range bad {
164+
var u URI
165+
if err := u.Parse(nil, []byte(raw)); err == nil {
166+
t.Fatalf("expected error parsing %q", raw)
167+
}
168+
}
169+
}
170+
171+
func TestURIAllowAtInUserinfo(t *testing.T) {
172+
t.Parallel()
173+
174+
var u URI
175+
if err := u.Parse(nil, []byte("http://user:p@[email protected]/")); err != nil {
176+
t.Fatalf("unexpected error: %v", err)
177+
}
178+
179+
if got := string(u.Host()); got != "example.com" {
180+
t.Fatalf("unexpected host %q", got)
181+
}
182+
183+
if got := string(u.Username()); got != "user" {
184+
t.Fatalf("unexpected username %q", got)
185+
}
186+
187+
if got := string(u.Password()); got != "p@ss" {
188+
t.Fatalf("unexpected password %q", got)
189+
}
190+
}
191+
154192
func TestURIPathNormalize(t *testing.T) {
155193
if runtime.GOOS == "windows" {
156194
t.SkipNow()

0 commit comments

Comments
 (0)