CVE-2014-8681: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: SQL injection vulnerability in the GetIssues function in models/issue.go in Gogs (aka Go Git Service) 0.3.1-9 through 0.5.6.x before 0.5.6.1025 Beta allows remote attackers to execute arbitrary SQL commands via the label parameter to user/repos/issues.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Published: 2014-11-21T15:59Z
Repository: https://github.com/gogits/gogs
Language: Go
Files changed: gogs.go, models/issue.go, models/repo.go, models/user.go, routers/api/v1/repos.go, templates/.VERSION
Commit date: 2014-10-25 07:50:19 -0400
Commit message: Safe work

Diff: diff --git a/gogs.go b/gogs.go
index 4fb0dcce..82a3ae68 100644
--- a/gogs.go
+++ b/gogs.go
@@ -15,11 +15,11 @@ import (
 
 	"github.com/gogits/gogs/cmd"
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.5.6.1024 Beta"
+const APP_VER = "0.5.6.1025 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())
 	setting.AppVer = APP_VER
 }
diff --git a/models/issue.go b/models/issue.go
index f16c2e25..8004647c 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -209,11 +209,14 @@ func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labelIds, sort
 		sess.And("milestone_id=?", mid)
 	}
 
 	if len(labelIds) > 0 {
 		for _, label := range strings.Split(labelIds, ",") {
-			sess.And("label_ids like '%$" + label + "|%'")
+			// Prevent SQL inject.
+			if com.StrTo(label).MustInt() > 0 {
+				sess.And("label_ids like '%$" + label + "|%'")
+			}
 		}
 	}
 
 	switch sortType {
 	case "oldest":
diff --git a/models/repo.go b/models/repo.go
index dc47b2e6..888dea1e 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1129,21 +1129,25 @@ func GetCollaborators(repoName string) (us []*User, err error) {
 
 type SearchOption struct {
 	Keyword string
 	Uid     int64
 	Limit   int
+	Private bool
+}
+
+// FilterSQLInject tries to prevent SQL injection.
+func FilterSQLInject(key string) string {
+	key = strings.TrimSpace(key)
+	key = strings.Split(key, " ")[0]
+	key = strings.Replace(key, ",", "", -1)
+	return key
 }
 
 // SearchRepositoryByName returns given number of repositories whose name contains keyword.
 func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
 	// Prevent SQL inject.
-	opt.Keyword = strings.TrimSpace(opt.Keyword)
-	if len(opt.Keyword) == 0 {
-		return repos, nil
-	}
-
-	opt.Keyword = strings.Split(opt.Keyword, " ")[0]
+	opt.Keyword = FilterSQLInject(opt.Keyword)
 	if len(opt.Keyword) == 0 {
 		return repos, nil
 	}
 	opt.Keyword = strings.ToLower(opt.Keyword)
 
@@ -1152,10 +1156,13 @@ func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
 	// Append conditions.
 	sess := x.Limit(opt.Limit)
 	if opt.Uid > 0 {
 		sess.Where("owner_id=?", opt.Uid)
 	}
+	if !opt.Private {
+		sess.And("is_private=false")
+	}
 	sess.And("lower_name like '%" + opt.Keyword + "%'").Find(&repos)
 	return repos, err
 }
 
 //  __      __         __         .__
diff --git a/models/user.go b/models/user.go
index 6b0f796b..ce85008b 100644
--- a/models/user.go
+++ b/models/user.go
@@ -572,17 +572,11 @@ func GetUserByEmail(email string) (*User, error) {
 	return user, nil
 }
 
 // SearchUserByName returns given number of users whose name contains keyword.
 func SearchUserByName(opt SearchOption) (us []*User, err error) {
-	// Prevent SQL inject.
-	opt.Keyword = strings.TrimSpace(opt.Keyword)
-	if len(opt.Keyword) == 0 {
-		return us, nil
-	}
-
-	opt.Keyword = strings.Split(opt.Keyword, " ")[0]
+	opt.Keyword = FilterSQLInject(opt.Keyword)
 	if len(opt.Keyword) == 0 {
 		return us, nil
 	}
 	opt.Keyword = strings.ToLower(opt.Keyword)
 
diff --git a/routers/api/v1/repos.go b/routers/api/v1/repos.go
index 37a3e47a..2dee512f 100644
--- a/routers/api/v1/repos.go
+++ b/routers/api/v1/repos.go
@@ -29,10 +29,30 @@ func SearchRepos(ctx *middleware.Context) {
 	}
 	if opt.Limit == 0 {
 		opt.Limit = 10
 	}
 
+	// Check visibility.
+	if ctx.IsSigned && opt.Uid > 0 {
+		if ctx.User.Id == opt.Uid {
+			opt.Private = true
+		} else {
+			u, err := models.GetUserById(opt.Uid)
+			if err != nil {
+				ctx.JSON(500, map[string]interface{}{
+					"ok":    false,
+					"error": err.Error(),
+				})
+				return
+			}
+			if u.IsOrganization() && u.IsOrgOwner(ctx.User.Id) {
+				opt.Private = true
+			}
+			// FIXME: how about collaborators?
+		}
+	}
+
 	repos, err := models.SearchRepositoryByName(opt)
 	if err != nil {
 		ctx.JSON(500, map[string]interface{}{
 			"ok":    false,
 			"error": err.Error(),
diff --git a/templates/.VERSION b/templates/.VERSION
index 647ef439..3e75e45d 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.5.6.1024 Beta
\ No newline at end of file
+0.5.6.1025 Beta
\ No newline at end of file

Vulnerable code: const APP_VER = "0.5.6.1024 Beta"
			sess.And("label_ids like '%$" + label + "|%'")
	opt.Keyword = strings.TrimSpace(opt.Keyword)
	if len(opt.Keyword) == 0 {
		return repos, nil
	}

	opt.Keyword = strings.Split(opt.Keyword, " ")[0]
	// Prevent SQL inject.
	opt.Keyword = strings.TrimSpace(opt.Keyword)
	if len(opt.Keyword) == 0 {
		return us, nil
	}

	opt.Keyword = strings.Split(opt.Keyword, " ")[0]
0.5.6.1024 Beta
Fixed code: const APP_VER = "0.5.6.1025 Beta"
			// Prevent SQL inject.
			if com.StrTo(label).MustInt() > 0 {
				sess.And("label_ids like '%$" + label + "|%'")
			}
	Private bool
}

// FilterSQLInject tries to prevent SQL injection.
func FilterSQLInject(key string) string {
	key = strings.TrimSpace(key)
	key = strings.Split(key, " ")[0]
	key = strings.Replace(key, ",", "", -1)
	return key
	opt.Keyword = FilterSQLInject(opt.Keyword)
	if !opt.Private {
		sess.And("is_private=false")
	}
	opt.Keyword = FilterSQLInject(opt.Keyword)
	// Check visibility.
	if ctx.IsSigned && opt.Uid > 0 {
		if ctx.User.Id == opt.Uid {
			opt.Private = true
		} else {
			u, err := models.GetUserById(opt.Uid)
			if err != nil {
				ctx.JSON(500, map[string]interface{}{
					"ok":    false,
					"error": err.Error(),
				})
				return
			}
			if u.IsOrganization() && u.IsOrgOwner(ctx.User.Id) {
				opt.Private = true
			}
			// FIXME: how about collaborators?
		}
	}

0.5.6.1025 Beta