CVE-2022-2024: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: OS Command Injection in GitHub repository gogs/gogs prior to 0.12.11.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 9.8
Published: 2023-02-25T08:15Z
Repository: https://github.com/gogs/gogs
Language: Go
Files changed: CHANGELOG.md, internal/db/repo_editor.go, internal/db/repo_editor_test.go
Commit date: 2023-02-18 22:15:13 +0800
Commit message: fix(db): correctly check Git path on case-insensitive file system (#7359)



Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 279db715f9..236adf9265 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,10 +27,11 @@ All notable changes to Gogs are documented in this file.
 - Access tokens are now stored using their SHA256 hashes instead of raw values. [#7008](https://github.com/gogs/gogs/pull/7008)
 
 ### Fixed
 
 - _Security:_ Stored XSS for issue assignees. [#7145](https://github.com/gogs/gogs/issues/7145)
+- _Security:_ OS Command Injection in repo editor on case-insensitive file systems. [#7030](https://github.com/gogs/gogs/issues/7030)
 - Unable to use LDAP authentication on ARM machines. [#6761](https://github.com/gogs/gogs/issues/6761)
 - Unable to choose "Lookup Avatar by mail" in user settings without deleting custom avatar. [#7267](https://github.com/gogs/gogs/pull/7267)
 - Mistakenly include the "data" directory under the custom directory in the Docker setup. [#7343](https://github.com/gogs/gogs/pull/7343)
 - Unable to render repository pages with implicit submodules (e.g. `get submodule "REDACTED": revision does not exist`). [#6436](https://github.com/gogs/gogs/issues/6436)
 - Unable to start after data recovery with an outdated migration version. [#7125](https://github.com/gogs/gogs/issues/7125)
diff --git a/internal/db/repo_editor.go b/internal/db/repo_editor.go
index 3edb16e2bd..0a1c949584 100644
--- a/internal/db/repo_editor.go
+++ b/internal/db/repo_editor.go
@@ -483,11 +483,14 @@ type UploadRepoFileOptions struct {
 	Files        []string // In UUID format
 }
 
 // isRepositoryGitPath returns true if given path is or resides inside ".git"
 // path of the repository.
+//
+// TODO(unknwon): Move to repoutil during refactoring for this file.
 func isRepositoryGitPath(path string) bool {
+	path = strings.ToLower(path)
 	return strings.HasSuffix(path, ".git") ||
 		strings.Contains(path, ".git/") ||
 		strings.Contains(path, `.git\`) ||
 		// Windows treats ".git." the same as ".git"
 		strings.HasSuffix(path, ".git.") ||
diff --git a/internal/db/repo_editor_test.go b/internal/db/repo_editor_test.go
index 6aeed011aa..f6178eda8b 100644
--- a/internal/db/repo_editor_test.go
+++ b/internal/db/repo_editor_test.go
@@ -8,21 +8,28 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func Test_isRepositoryGitPath(t *testing.T) {
+func TestIsRepositoryGitPath(t *testing.T) {
 	tests := []struct {
 		path    string
 		wantVal bool
 	}{
 		{path: ".git", wantVal: true},
 		{path: "./.git", wantVal: true},
 		{path: ".git/hooks/pre-commit", wantVal: true},
 		{path: ".git/hooks", wantVal: true},
 		{path: "dir/.git", wantVal: true},
 
+		// Case-insensitive file system
+		{path: ".Git", wantVal: true},
+		{path: "./.Git", wantVal: true},
+		{path: ".Git/hooks/pre-commit", wantVal: true},
+		{path: ".Git/hooks", wantVal: true},
+		{path: "dir/.Git", wantVal: true},
+
 		{path: ".gitignore", wantVal: false},
 		{path: "dir/.gitkeep", wantVal: false},
 
 		// Windows-specific
 		{path: `.git\`, wantVal: true},

Vulnerable code: func Test_isRepositoryGitPath(t *testing.T) {
Fixed code: - _Security:_ OS Command Injection in repo editor on case-insensitive file systems. [#7030](https://github.com/gogs/gogs/issues/7030)
//
// TODO(unknwon): Move to repoutil during refactoring for this file.
	path = strings.ToLower(path)
func TestIsRepositoryGitPath(t *testing.T) {
		// Case-insensitive file system
		{path: ".Git", wantVal: true},
		{path: "./.Git", wantVal: true},
		{path: ".Git/hooks/pre-commit", wantVal: true},
		{path: ".Git/hooks", wantVal: true},
		{path: "dir/.Git", wantVal: true},