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

Extra slash in URL makes git clone fail #20462

Closed
reynir opened this issue Jul 23, 2022 · 15 comments · Fixed by #21333
Closed

Extra slash in URL makes git clone fail #20462

reynir opened this issue Jul 23, 2022 · 15 comments · Fixed by #21333
Labels
good first issue Likely to be an easy fix issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented type/bug

Comments

@reynir
Copy link

reynir commented Jul 23, 2022

Description

Cloning a repository with a double slash fails:

$ git clone https://git.data.coop//halfd/new-website.git
Cloning into 'new-website'...
remote: Not found.
fatal: repository 'https://git.data.coop//halfd/new-website.git/' not found

Gitea Version

1.16.8

Can you reproduce the bug on the Gitea demo site?

Yes

Log Gist

No response

Screenshots

No response

Git Version

No response

Operating System

No response

How are you running Gitea?

Using the docker image.

Database

SQLite

@silverwind
Copy link
Member

silverwind commented Jul 23, 2022

This does work on GitHub, so I guess we can also allow one extra / at start of the path.

@silverwind silverwind added issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented good first issue Likely to be an easy fix labels Jul 23, 2022
@zeripath
Copy link
Contributor

What's the reason behind wanting to do this?

@silverwind
Copy link
Member

silverwind commented Jul 23, 2022

To follow the robustness principle in this case. Extra slashes are the result of common misconfigurations but the intend of such requests is still clear enough imho that we can accept it.

@reynir
Copy link
Author

reynir commented Jul 23, 2022

Yes, the software I'm using uses a different git implementation, and something somewhere in the tall stack adds an extra slash in the beginning. I am trying to figure where this happens so I can either report it or fix it myself.

I found this behavior of gitea a bit surprising because often web servers normalize the paths so extra slashes don't break things. It can be nice when you're building a path in a script that you can be a bit sloppy and just put a slash between each path segment not worrying if either segment contains a slash.

@silverwind
Copy link
Member

web servers normalize the paths so extra slashes don't break things

Yes, in fact, nginx has a default behaviour where it merges any // to /. I'm not saying we should do this everywhere like nginx does, but at the start of the path seems reasonable.

@Gusted
Copy link
Contributor

Gusted commented Jul 23, 2022

This does work on GitHub, so I guess we can also allow one extra / at start of the path.

FYI, Github & Gitlab allows any arbitrary number of extra leading slashes, even better, it allows any number of slashes, for every valid slash position. Well I still consider this user fault, I personally won't put up a PR for this. But feel free to use this patch and create a PR for this:

This is just to strip leading/trailing slashes and still have working behavior:

diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index 6ea1e1dfb..78e83abc6 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -16,7 +16,7 @@ import (
        "code.gitea.io/gitea/modules/web/routing"
 
        "github.com/chi-middleware/proxy"
-       "github.com/go-chi/chi/v5/middleware"
+       "github.com/go-chi/chi/v5"
 )
 
 // Middlewares returns common middlewares
@@ -48,7 +48,38 @@ func Middlewares() []func(http.Handler) http.Handler {
                handlers = append(handlers, proxy.ForwardedHeaders(opt))
        }
 
-       handlers = append(handlers, middleware.StripSlashes)
+       // Strip leading and trailing slashes.
+       handlers = append(handlers, func(next http.Handler) http.Handler {
+               return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+                       var path string
+                       rctx := chi.RouteContext(req.Context())
+
+                       if rctx != nil && rctx.RoutePath != "" {
+                               path = rctx.RoutePath
+                       } else {
+                               path = req.URL.Path
+                       }
+
+                       if len(path) > 1 {
+                               // Strip trailing slashes.
+                               if strings.HasSuffix(path, "/") {
+                                       path = strings.TrimRight(path, "/")
+                               }
+
+                               // Strip double leading slashes to a single slash.
+                               if strings.HasPrefix(path, "//") {
+                                       path = "/" + strings.TrimLeft(path, "/")
+                               }
+
+                               if rctx == nil {
+                                       req.URL.Path = path
+                               } else {
+                                       rctx.RoutePath = path
+                               }
+                       }
+                       next.ServeHTTP(resp, req)
+               })
+       })

However if someone wants to go ahead and argue that also considering every other slash position to allow for multiple slashes, feel free to use this patch and create a PR:

diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index 6ea1e1dfb..64aa3603f 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -16,7 +16,7 @@ import (
        "code.gitea.io/gitea/modules/web/routing"
 
        "github.com/chi-middleware/proxy"
-       "github.com/go-chi/chi/v5/middleware"
+       "github.com/go-chi/chi/v5"
 )
 
 // Middlewares returns common middlewares
@@ -48,7 +48,48 @@ func Middlewares() []func(http.Handler) http.Handler {
                handlers = append(handlers, proxy.ForwardedHeaders(opt))
        }
 
-       handlers = append(handlers, middleware.StripSlashes)
+       // Strip leading and trailing slashes.
+       handlers = append(handlers, func(next http.Handler) http.Handler {
+               return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+                       var path string
+                       rctx := chi.RouteContext(req.Context())
+
+                       if rctx != nil && rctx.RoutePath != "" {
+                               path = rctx.RoutePath
+                       } else {
+                               path = req.URL.Path
+                       }
+
+                       if len(path) > 1 {
+                               newPath := make([]rune, 0, len(path))
+
+                               prevWasSlash := false
+                               // Loop trough all characters and de-duplicate any multiple slashes to a single slash.
+                               for _, chr := range path {
+                                       if chr != '/' {
+                                               newPath = append(newPath, chr)
+                                               prevWasSlash = false
+                                               continue
+                                       }
+                                       if prevWasSlash {
+                                               continue
+                                       }
+                                       newPath = append(newPath, chr)
+                                       prevWasSlash = true
+                               }
+
+                               // Always remove the leading slash.
+                               modifiedPath := strings.TrimSuffix(string(newPath), "/")
+
+                               if rctx == nil {
+                                       req.URL.Path = modifiedPath
+                               } else {
+                                       rctx.RoutePath = modifiedPath
+                               }
+                       }
+                       next.ServeHTTP(resp, req)
+               })
+       })
 
        if !setting.DisableRouterLog {
                handlers = append(handlers, routing.NewLoggerHandler())

@sandyydk
Copy link
Contributor

sandyydk commented Oct 3, 2022

@Gusted Can I pick this up?

@Gusted
Copy link
Contributor

Gusted commented Oct 3, 2022

@sandyydk Feel free to use the patches I've provided. No need to ask.

@sandyydk
Copy link
Contributor

sandyydk commented Oct 3, 2022 via email

@stuzer05
Copy link
Contributor

stuzer05 commented Feb 26, 2023

@reynir this has been fixed, please close this issue

@reynir
Copy link
Author

reynir commented Feb 26, 2023

Where was this fixed?

@stuzer05
Copy link
Contributor

Where was this fixed?

Cannot point that out exactly, but on main branch that works:

stuzer05@stuzer05:~/Downloads$ git clone http://localhost:3000/stuzer05/test_rights.git/
Cloning into 'test_rights'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 9 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (9/9), done.

@reynir
Copy link
Author

reynir commented Feb 26, 2023

Thank you for testing. Could you try as well with a double leading slash in the path? I. e. git clone http://localhost:3000//stuzer05/test_rights.git as that's is what I originally reported.

@stuzer05
Copy link
Contributor

Thank you for testing. Could you try as well with a double leading slash in the path? I. e. git clone http://localhost:3000//stuzer05/test_rights.git as that's is what I originally reported.

Currently doesn't work, made a pr

@wxiaoguang
Copy link
Contributor

wxiaoguang commented Feb 27, 2023

OK, as a bystander, I think there are some misunderstanding or miscommunication during this issue's history.

First, thank you all for making Gitea better.

The current situation is:

In my mind:

  • It's better to focus in Support sanitising the URL by removing extra slashes in the URL #21333
  • Call TOC to make a final decision: go or no-go, with detailed explanations
  • ps: If it's agreed to fix the double-slash, I think all double-slashes could be fixed together like 21333 (not only just fixing this issue), to make "//org//repo//..." work, like GitHub. Actually, GitHub supports any amount of slashes: https://github.com/go-gitea/gitea////////////tree/main/////.github

@wolfogre wolfogre reopened this Feb 27, 2023
@lunny lunny closed this as completed in 547c173 Mar 4, 2023
GiteaBot pushed a commit to GiteaBot/gitea that referenced this issue Mar 4, 2023
…itea#21333)

Changes in this PR :

Strips incoming request URL of additional slashes (/). For example an
input like

`https://git.data.coop//halfd/new-website.git` is translated to
`https://git.data.coop/halfd/new-website.git`

Fixes go-gitea#20462

Fix go-gitea#23242

---------

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
techknowlogick pushed a commit that referenced this issue Mar 5, 2023
…) (#23300)

Backport #21333

Changes in this PR :

Strips incoming request URL of additional slashes (/). For example an
input like

`https://git.data.coop//halfd/new-website.git` is translated to
`https://git.data.coop/halfd/new-website.git`

Fixes #20462

Fix #23242

Co-authored-by: Sandeep Bhat <sandyethadka@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: delvh <leon@kske.dev>
@go-gitea go-gitea locked and limited conversation to collaborators May 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
good first issue Likely to be an easy fix issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented type/bug
Projects
None yet
8 participants