Vulnerability: Insecure Permissions in Gogs routes/api/v1/api.go in Gogs 0.11.86 lacks permission checks for routes: deploy keys, collaborators, and hooks.
Vulnerability ID: GHSA-5r2v-6gm6-vpvh
CWE: CWE-200
Severity score: 9.8
Project: https://github.com/gogs/gogs
Published: 2021-05-18
File: routes/api/v1/api.go
Commit message: routes/api: fix permission checks for routes

Reported by @ManassehZhou #5764
Commit author: unknwon

Vulnerable version (before fix):
 func reqRepoWriter() macaron.Handler { 	} }  func orgAssignment(args ...bool) macaron.Handler { 	var ( 		assignOrg  bool func RegisterRoutes(m *macaron.Macaron) { 						Post(bind(api.CreateHookOption{}), repo.CreateHook) 					m.Combo("/:id").Patch(bind(api.EditHookOption{}), repo.EditHook). 						Delete(repo.DeleteHook) 				}, reqAdmin()) 				m.Group("/collaborators", func() { 					m.Get("", repo.ListCollaborators) 					m.Combo("/:collaborator").Get(repo.IsCollaborator).Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator). 						Delete(repo.DeleteCollaborator) 				}, reqAdmin()) 				m.Get("/raw/*", context.RepoRef(), repo.GetRawFile) 				m.Get("/archive/*", repo.GetArchive) 				m.Get("/forks", repo.ListForks) func RegisterRoutes(m *macaron.Macaron) { 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) 					m.Combo("/:id").Get(repo.GetDeployKey). 						Delete(repo.DeleteDeploykey) 				}, reqAdmin()) 				m.Group("/issues", func() { 					m.Combo("").Get(repo.ListIssues).Post(bind(api.CreateIssueOption{}), repo.CreateIssue) 					m.Group("/comments", func() { func RegisterRoutes(m *macaron.Macaron) { 						Delete(reqRepoWriter(), repo.DeleteMilestone) 				})  				m.Patch("/issue-tracker", bind(api.EditIssueTrackerOption{}), repo.IssueTracker) 				m.Post("/mirror-sync", repo.MirrorSync) 				m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) 			}, repoAssignment()) 		}, reqToken())

Fixed version (after fix):
 func reqRepoWriter() macaron.Handler { 	} }  func reqRepoAdmin() macaron.Handler { 	return func(c *context.Context) { 		if !c.Repo.IsAdmin() { 			c.Error(http.StatusForbidden) 			return 		} 	} }  func orgAssignment(args ...bool) macaron.Handler { 	var ( 		assignOrg  bool func RegisterRoutes(m *macaron.Macaron) { 						Post(bind(api.CreateHookOption{}), repo.CreateHook) 					m.Combo("/:id").Patch(bind(api.EditHookOption{}), repo.EditHook). 						Delete(repo.DeleteHook) 				}, reqRepoAdmin()) 				m.Group("/collaborators", func() { 					m.Get("", repo.ListCollaborators) 					m.Combo("/:collaborator").Get(repo.IsCollaborator).Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator). 						Delete(repo.DeleteCollaborator) 				}, reqRepoAdmin()) 				m.Get("/raw/*", context.RepoRef(), repo.GetRawFile) 				m.Get("/archive/*", repo.GetArchive) 				m.Get("/forks", repo.ListForks) func RegisterRoutes(m *macaron.Macaron) { 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) 					m.Combo("/:id").Get(repo.GetDeployKey). 						Delete(repo.DeleteDeploykey) 				}, reqRepoAdmin()) 				m.Group("/issues", func() { 					m.Combo("").Get(repo.ListIssues).Post(bind(api.CreateIssueOption{}), repo.CreateIssue) 					m.Group("/comments", func() { func RegisterRoutes(m *macaron.Macaron) { 						Delete(reqRepoWriter(), repo.DeleteMilestone) 				})  				m.Patch("/issue-tracker", reqRepoWriter(), bind(api.EditIssueTrackerOption{}), repo.IssueTracker) 				m.Post("/mirror-sync", reqRepoWriter(), repo.MirrorSync) 				m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) 			}, repoAssignment()) 		}, reqToken())

Patch:
patch: @@ -112,6 +112,15 @@ func reqRepoWriter() macaron.Handler {
 	}
 }
 
+func reqRepoAdmin() macaron.Handler {
+	return func(c *context.Context) {
+		if !c.Repo.IsAdmin() {
+			c.Error(http.StatusForbidden)
+			return
+		}
+	}
+}
+
 func orgAssignment(args ...bool) macaron.Handler {
 	var (
 		assignOrg  bool
@@ -236,12 +245,12 @@ func RegisterRoutes(m *macaron.Macaron) {
 						Post(bind(api.CreateHookOption{}), repo.CreateHook)
 					m.Combo("/:id").Patch(bind(api.EditHookOption{}), repo.EditHook).
 						Delete(repo.DeleteHook)
-				}, reqAdmin())
+				}, reqRepoAdmin())
 				m.Group("/collaborators", func() {
 					m.Get("", repo.ListCollaborators)
 					m.Combo("/:collaborator").Get(repo.IsCollaborator).Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
 						Delete(repo.DeleteCollaborator)
-				}, reqAdmin())
+				}, reqRepoAdmin())
 				m.Get("/raw/*", context.RepoRef(), repo.GetRawFile)
 				m.Get("/archive/*", repo.GetArchive)
 				m.Get("/forks", repo.ListForks)
@@ -260,7 +269,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
 					m.Combo("/:id").Get(repo.GetDeployKey).
 						Delete(repo.DeleteDeploykey)
-				}, reqAdmin())
+				}, reqRepoAdmin())
 				m.Group("/issues", func() {
 					m.Combo("").Get(repo.ListIssues).Post(bind(api.CreateIssueOption{}), repo.CreateIssue)
 					m.Group("/comments", func() {
@@ -300,8 +309,8 @@ func RegisterRoutes(m *macaron.Macaron) {
 						Delete(reqRepoWriter(), repo.DeleteMilestone)
 				})
 
-				m.Patch("/issue-tracker", bind(api.EditIssueTrackerOption{}), repo.IssueTracker)
-				m.Post("/mirror-sync", repo.MirrorSync)
+				m.Patch("/issue-tracker", reqRepoWriter(), bind(api.EditIssueTrackerOption{}), repo.IssueTracker)
+				m.Post("/mirror-sync", reqRepoWriter(), repo.MirrorSync)
 				m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig)
 			}, repoAssignment())
 		}, reqToken())
path: routes/api/v1/api.go