mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-13 13:12:17 +08:00
worked on review comments
This commit is contained in:
parent
67c0ef7f91
commit
cf959d79ff
53
tree.go
53
tree.go
@ -85,16 +85,37 @@ func (n *node) addChild(child *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func countParams(path string) uint16 {
|
func countParams(path string) uint16 {
|
||||||
var n uint16
|
|
||||||
s := bytesconv.StringToBytes(path)
|
s := bytesconv.StringToBytes(path)
|
||||||
n += uint16(bytes.Count(s, strColon))
|
colons := bytes.Count(s, strColon)
|
||||||
n += uint16(bytes.Count(s, strStar))
|
stars := bytes.Count(s, strStar)
|
||||||
return n
|
total := colons + stars
|
||||||
|
// Cap at max uint16 to prevent overflow
|
||||||
|
if total > 0xFFFF {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
return uint16(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countSections(path string) uint16 {
|
func countSections(path string) uint16 {
|
||||||
s := bytesconv.StringToBytes(path)
|
s := bytesconv.StringToBytes(path)
|
||||||
return uint16(bytes.Count(s, strSlash))
|
count := bytes.Count(s, strSlash)
|
||||||
|
// Cap at max uint16 to prevent overflow
|
||||||
|
if count > 0xFFFF {
|
||||||
|
return 0xFFFF
|
||||||
|
}
|
||||||
|
return uint16(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unescapePathValue unescapes a path parameter value if unescape is enabled.
|
||||||
|
// Only unescapes if the value contains percent-encoded characters or plus signs.
|
||||||
|
// This prevents double unescaping and potential path traversal vulnerabilities.
|
||||||
|
func unescapePathValue(val string, unescape bool) string {
|
||||||
|
if unescape && strings.ContainsAny(val, "%+") {
|
||||||
|
if v, err := url.QueryUnescape(val); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodeType uint8
|
type nodeType uint8
|
||||||
@ -519,16 +540,7 @@ walk: // Outer loop for walking the tree
|
|||||||
// Expand slice within preallocated capacity
|
// Expand slice within preallocated capacity
|
||||||
i := len(*value.params)
|
i := len(*value.params)
|
||||||
*value.params = (*value.params)[:i+1]
|
*value.params = (*value.params)[:i+1]
|
||||||
val := path[:end]
|
val := unescapePathValue(path[:end], unescape)
|
||||||
if unescape {
|
|
||||||
// Only unescape if the value contains percent-encoded characters or plus signs
|
|
||||||
// This prevents double unescaping and potential path traversal
|
|
||||||
if strings.ContainsAny(val, "%+") {
|
|
||||||
if v, err := url.QueryUnescape(val); err == nil {
|
|
||||||
val = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(*value.params)[i] = Param{
|
(*value.params)[i] = Param{
|
||||||
Key: n.path[1:],
|
Key: n.path[1:],
|
||||||
Value: val,
|
Value: val,
|
||||||
@ -576,16 +588,7 @@ walk: // Outer loop for walking the tree
|
|||||||
// Expand slice within preallocated capacity
|
// Expand slice within preallocated capacity
|
||||||
i := len(*value.params)
|
i := len(*value.params)
|
||||||
*value.params = (*value.params)[:i+1]
|
*value.params = (*value.params)[:i+1]
|
||||||
val := path
|
val := unescapePathValue(path, unescape)
|
||||||
if unescape {
|
|
||||||
// Only unescape if the value contains percent-encoded characters or plus signs
|
|
||||||
// This prevents double unescaping and potential path traversal
|
|
||||||
if strings.ContainsAny(path, "%+") {
|
|
||||||
if v, err := url.QueryUnescape(path); err == nil {
|
|
||||||
val = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(*value.params)[i] = Param{
|
(*value.params)[i] = Param{
|
||||||
Key: n.path[2:],
|
Key: n.path[2:],
|
||||||
Value: val,
|
Value: val,
|
||||||
|
|||||||
16
tree_test.go
16
tree_test.go
@ -377,16 +377,16 @@ func TestSecureParameterHandling(t *testing.T) {
|
|||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
// Normal case - single encoding works as expected
|
// Normal case - single encoding works as expected
|
||||||
{"/info/user%2Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user/profile"}}},
|
{"/info/user%2Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user/profile"}}},
|
||||||
|
|
||||||
// Double encoding - should only decode once
|
// Double encoding - should only decode once
|
||||||
{"/info/user%252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user%2Fprofile"}}},
|
{"/info/user%252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user%2Fprofile"}}},
|
||||||
|
|
||||||
// Triple encoding - should only decode once
|
// Triple encoding - should only decode once
|
||||||
{"/info/user%25252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user%252Fprofile"}}},
|
{"/info/user%25252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "user%252Fprofile"}}},
|
||||||
|
|
||||||
// Mixed encoding - should only decode once
|
// Mixed encoding - should only decode once
|
||||||
{"/info/%2Fuser%252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "/user%2Fprofile"}}},
|
{"/info/%2Fuser%252Fprofile", false, "/info/:user", Params{Param{Key: "user", Value: "/user%2Fprofile"}}},
|
||||||
|
|
||||||
// No encoding - should pass through unchanged
|
// No encoding - should pass through unchanged
|
||||||
{"/info/user", false, "/info/:user", Params{Param{Key: "user", Value: "user"}}},
|
{"/info/user", false, "/info/:user", Params{Param{Key: "user", Value: "user"}}},
|
||||||
}, unescape)
|
}, unescape)
|
||||||
@ -396,16 +396,16 @@ func TestSecureParameterHandling(t *testing.T) {
|
|||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
// Normal case - single encoding works as expected
|
// Normal case - single encoding works as expected
|
||||||
{"/files/path%2Fto%2Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path/to/file.txt"}}},
|
{"/files/path%2Fto%2Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path/to/file.txt"}}},
|
||||||
|
|
||||||
// Double encoding - should only decode once
|
// Double encoding - should only decode once
|
||||||
{"/files/path%252Fto%252Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path%2Fto%2Ffile.txt"}}},
|
{"/files/path%252Fto%252Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path%2Fto%2Ffile.txt"}}},
|
||||||
|
|
||||||
// Triple encoding - should only decode once
|
// Triple encoding - should only decode once
|
||||||
{"/files/path%25252Fto%25252Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path%252Fto%252Ffile.txt"}}},
|
{"/files/path%25252Fto%25252Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/path%252Fto%252Ffile.txt"}}},
|
||||||
|
|
||||||
// Mixed encoding - should only decode once
|
// Mixed encoding - should only decode once
|
||||||
{"/files/%2Fpath%252Fto%2Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "//path%2Fto/file.txt"}}},
|
{"/files/%2Fpath%252Fto%2Ffile.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "//path%2Fto/file.txt"}}},
|
||||||
|
|
||||||
// No encoding - should pass through unchanged
|
// No encoding - should pass through unchanged
|
||||||
{"/files/normal/file.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/normal/file.txt"}}},
|
{"/files/normal/file.txt", false, "/files/*filepath", Params{Param{Key: "filepath", Value: "/normal/file.txt"}}},
|
||||||
}, unescape)
|
}, unescape)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user