Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected c.Param() results when UseRawPath and UnescapePathValues are true #2633

Open
nozhT opened this issue Feb 8, 2021 · 3 comments
Open

Comments

@nozhT
Copy link

nozhT commented Feb 8, 2021

Description

'+' plus signs in path parameters are unescaped to ' ' (the space character) when UnescapePathValues and UseRawPath are set to true.

However this problem does not appear if the URL path segment does not contain %2F (URLencoded '/').

Changing

gin/tree.go

Line 446 in a573ec6

if v, err := url.QueryUnescape(val); err == nil {
to url.PathUnescape might fix the problem.

How to reproduce

package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.New()
	r.UseRawPath = true
	r.GET("/:name/hi", func(c *gin.Context) {
		c.String(200, "Hello %s", c.Param("name"))
	})
	r.Run(":9000")
}

Expectations

$ curl 'http://localhost:9000/a+file.txt/hi' 
Hello a+file.txt
$ curl 'http://localhost:9000/a+%2Ffile.txt/hi'
Hello a+/file.txt

Actual result

$ curl 'http://localhost:9000/a+file.txt/hi' 
Hello a+file.txt
$ curl 'http://localhost:9000/a+%2Ffile.txt/hi'
Hello a /file.txt

Environment

  • go version: go1.15.2
  • gin version (or commit ref): gin v1.6.3
  • operating system: linux/amd64
@dbeckwith
Copy link

Here's a workaround that uses a middleware to manually unescape the path params using url.PathUnescape:

app.UseRawPath = true
app.UnescapePathValues = false
app.Use(func(c *gin.Context) {
    for idx, param := range c.Params {
        unescaped, err := url.PathUnescape(param.Value)
        if err == nil {
            param.Value = unescaped
            c.Params[idx] = param
        }
    }
    c.Next()
})

@nozhT
Copy link
Author

nozhT commented May 4, 2021

Here's a workaround that uses a middleware to manually unescape the path params using url.PathUnescape:

app.UseRawPath = true
app.UnescapePathValues = false
app.Use(func(c *gin.Context) {
    for idx, param := range c.Params {
        unescaped, err := url.PathUnescape(param.Value)
        if err == nil {
            param.Value = unescaped
            c.Params[idx] = param
        }
    }
    c.Next()
})

Just found out this incorrectly decodes a%20%252Ffile.txt to a /file.txt instead of a %2Ffile.txt.

I think it's because

gin/gin.go

Line 396 in a573ec6

if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {

uses URL.RawPath only if it exists instead of use URL.EscapedPath.

@UndeadBaneGitHub
Copy link

I would like to bring attention to this issue, since it is still present and essentially doesn't allow for raw path use.

The root is in the fact that Go's url library only populates the RawPath when it considers the default decoding incorrect:

func (u *URL) setPath(p string) error {
	path, err := unescape(p, encodePath)
	if err != nil {
		return err
	}
	u.Path = path
	if escp := escape(path, encodePath); p == escp {
		// Default encoding is fine.
		u.RawPath = ""
	} else {
		u.RawPath = p
	}
	return nil
}

I am not sure, whether it is a good idea on its own, but that's what it is. However, I would love to see Gin working around this (annoying) issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants