Skip to content

Commit dcb0b5a

Browse files
authored
fix: fix path-traversal bug (#229)
1 parent 690c346 commit dcb0b5a

File tree

3 files changed

+51
-19
lines changed

3 files changed

+51
-19
lines changed

internal/bytestr/bytes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var (
2828
)
2929

3030
var (
31+
StrBackSlash = []byte("\\")
3132
StrSlash = []byte("/")
3233
StrSlashSlash = []byte("//")
3334
StrSlashDotDot = []byte("/..")

pkg/protocol/uri.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ package protocol
4343

4444
import (
4545
"bytes"
46+
"path/filepath"
4647
"sync"
4748

4849
"github.com/cloudwego/hertz/internal/bytesconv"
@@ -318,9 +319,9 @@ func (u *URI) SetHostBytes(host []byte) {
318319
//
319320
// Examples:
320321
//
321-
// * For /foo/bar/baz.html path returns baz.html.
322-
// * For /foo/bar/ returns empty byte slice.
323-
// * For /foobar.js returns foobar.js.
322+
// - For /foo/bar/baz.html path returns baz.html.
323+
// - For /foo/bar/ returns empty byte slice.
324+
// - For /foobar.js returns foobar.js.
324325
func (u *URI) LastPathSegment() []byte {
325326
path := u.Path()
326327
n := bytes.LastIndexByte(path, '/')
@@ -334,14 +335,14 @@ func (u *URI) LastPathSegment() []byte {
334335
//
335336
// The following newURI types are accepted:
336337
//
337-
// * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
338-
// uri is replaced by newURI.
339-
// * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
340-
// the original scheme is preserved.
341-
// * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
342-
// of the original uri is replaced.
343-
// * Relative path, i.e. xx?yy=abc . In this case the original RequestURI
344-
// is updated according to the new relative path.
338+
// - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
339+
// uri is replaced by newURI.
340+
// - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
341+
// the original scheme is preserved.
342+
// - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
343+
// of the original uri is replaced.
344+
// - Relative path, i.e. xx?yy=abc . In this case the original RequestURI
345+
// is updated according to the new relative path.
345346
func (u *URI) Update(newURI string) {
346347
u.UpdateBytes(bytesconv.S2b(newURI))
347348
}
@@ -350,14 +351,14 @@ func (u *URI) Update(newURI string) {
350351
//
351352
// The following newURI types are accepted:
352353
//
353-
// * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
354-
// uri is replaced by newURI.
355-
// * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
356-
// the original scheme is preserved.
357-
// * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
358-
// of the original uri is replaced.
359-
// * Relative path, i.e. xx?yy=abc . In this case the original RequestURI
360-
// is updated according to the new relative path.
354+
// - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
355+
// uri is replaced by newURI.
356+
// - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
357+
// the original scheme is preserved.
358+
// - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
359+
// of the original uri is replaced.
360+
// - Relative path, i.e. xx?yy=abc . In this case the original RequestURI
361+
// is updated according to the new relative path.
361362
func (u *URI) UpdateBytes(newURI []byte) {
362363
u.requestURI = u.updateBytes(newURI, u.requestURI)
363364
}
@@ -484,6 +485,18 @@ func normalizePath(dst, src []byte) []byte {
484485
dst = addLeadingSlash(dst, src)
485486
dst = decodeArgAppendNoPlus(dst, src)
486487

488+
// Windows server need to replace all backslashes with
489+
// forward slashes to avoid path traversal attacks.
490+
if filepath.Separator == '\\' {
491+
for {
492+
n := bytes.IndexByte(dst, '\\')
493+
if n < 0 {
494+
break
495+
}
496+
dst[n] = '/'
497+
}
498+
}
499+
487500
// remove duplicate slashes
488501
b := dst
489502
bSize := len(b)

pkg/protocol/uri_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ package protocol
4343

4444
import (
4545
"fmt"
46+
"path/filepath"
4647
"reflect"
4748
"testing"
4849

@@ -228,3 +229,20 @@ func testURIFullURI(t *testing.T, scheme, host, path, hash string, args *Args, e
228229
t.Fatalf("Unexpected URI: %q. Expected %q", uri, expectedURI)
229230
}
230231
}
232+
233+
func TestParsePathWindows(t *testing.T) {
234+
t.Parallel()
235+
236+
testParsePathWindows(t, "/../../../../../foo", "/foo")
237+
testParsePathWindows(t, "/..\\..\\..\\..\\..\\foo", "/foo")
238+
testParsePathWindows(t, "/..%5c..%5cfoo", "/foo")
239+
}
240+
241+
func testParsePathWindows(t *testing.T, path, expectedPath string) {
242+
var u URI
243+
u.Parse(nil, []byte(path))
244+
parsedPath := u.Path()
245+
if filepath.Separator == '\\' && string(parsedPath) != expectedPath {
246+
t.Fatalf("Unexpected Path: %q. Expected %q", parsedPath, expectedPath)
247+
}
248+
}

0 commit comments

Comments
 (0)