CVE-2022-1993: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: Path Traversal in GitHub repository gogs/gogs prior to 0.12.9.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 8.1
Published: 2022-06-09T17:15Z
Repository: https://github.com/gogs/gogs
Language: Go
Files changed: CHANGELOG.md, internal/pathutil/pathutil_test.go, internal/route/repo/http.go
Commit date: 2022-06-07 21:11:36 +0800
Commit message: http: clean request path from Git endpoints (#7022)


Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 765d69351d..cb3e5cfe3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,10 +24,11 @@ All notable changes to Gogs are documented in this file.
 ### Fixed
 
 - _Security:_ OS Command Injection in file editor. [#7000](https://github.com/gogs/gogs/issues/7000)
 - _Security:_ Sanitize `DisplayName` in repository issue list. [#7009](https://github.com/gogs/gogs/pull/7009)
 - _Security:_ Path Traversal in file editor on Windows. [#7001](https://github.com/gogs/gogs/issues/7001)
+- _Security:_ Path Traversal in Git HTTP endpoints. [#7002](https://github.com/gogs/gogs/issues/7002)
 - Unable to use LDAP authentication on ARM machines. [#6761](https://github.com/gogs/gogs/issues/6761)
 - Unable to init repository during creation on Windows. [#6967](https://github.com/gogs/gogs/issues/6967)
 - Mysterious panic on `Value not found for type *repo.HTTPContext`. [#6963](https://github.com/gogs/gogs/issues/6963)
 
 ### Removed
diff --git a/internal/pathutil/pathutil_test.go b/internal/pathutil/pathutil_test.go
index d20e537a4f..7444c82a1f 100644
--- a/internal/pathutil/pathutil_test.go
+++ b/internal/pathutil/pathutil_test.go
@@ -25,10 +25,14 @@ func TestClean(t *testing.T) {
 		},
 		{
 			path:    "/../a/b/../c/../readme.txt",
 			wantVal: "a/readme.txt",
 		},
+		{
+			path:    "../../objects/info/..",
+			wantVal: "objects",
+		},
 		{
 			path:    "/a/readme.txt",
 			wantVal: "a/readme.txt",
 		},
 		{
diff --git a/internal/route/repo/http.go b/internal/route/repo/http.go
index 575719f1f7..668c4cfa98 100644
--- a/internal/route/repo/http.go
+++ b/internal/route/repo/http.go
@@ -22,10 +22,11 @@ import (
 
 	"gogs.io/gogs/internal/auth"
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/db"
 	"gogs.io/gogs/internal/lazyregexp"
+	"gogs.io/gogs/internal/pathutil"
 	"gogs.io/gogs/internal/tool"
 )
 
 type HTTPContext struct {
 	*macaron.Context
@@ -406,19 +407,25 @@ func HTTP(c *HTTPContext) {
 			c.Error(http.StatusForbidden, "Interacting with repositories by HTTP protocol is disabled")
 			return
 		}
 
 		if route.method != c.Req.Method {
-			c.NotFound()
+			c.Error(http.StatusNotFound)
 			return
 		}
 
-		file := strings.TrimPrefix(reqPath, m[1]+"/")
-		dir, err := getGitRepoPath(m[1])
+		cleaned := pathutil.Clean(m[1])
+		if m[1] != "/"+cleaned {
+			c.Error(http.StatusBadRequest, "Request path contains suspicious characters")
+			return
+		}
+
+		file := strings.TrimPrefix(reqPath, cleaned)
+		dir, err := getGitRepoPath(cleaned)
 		if err != nil {
 			log.Warn("HTTP.getGitRepoPath: %v", err)
-			c.NotFound()
+			c.Error(http.StatusNotFound)
 			return
 		}
 
 		route.handler(serviceHandler{
 			w:    c.Resp,
@@ -433,7 +440,7 @@ func HTTP(c *HTTPContext) {
 			repoName:  c.RepoName,
 		})
 		return
 	}
 
-	c.NotFound()
+	c.Error(http.StatusNotFound)
 }

Vulnerable code: 			c.NotFound()
		file := strings.TrimPrefix(reqPath, m[1]+"/")
		dir, err := getGitRepoPath(m[1])
			c.NotFound()
	c.NotFound()
Fixed code: - _Security:_ Path Traversal in Git HTTP endpoints. [#7002](https://github.com/gogs/gogs/issues/7002)
		{
			path:    "../../objects/info/..",
			wantVal: "objects",
		},
	"gogs.io/gogs/internal/pathutil"
			c.Error(http.StatusNotFound)
		cleaned := pathutil.Clean(m[1])
		if m[1] != "/"+cleaned {
			c.Error(http.StatusBadRequest, "Request path contains suspicious characters")
			return
		}

		file := strings.TrimPrefix(reqPath, cleaned)
		dir, err := getGitRepoPath(cleaned)
			c.Error(http.StatusNotFound)
	c.Error(http.StatusNotFound)