Browse Source

admin: use POST to run operations (#5997)

* admin: use POST to run operations

Fixed CSRF reported by Wenxu Wu of Tencent's Xuanwu Lab.

* Update CHANGELOG
ᴜɴᴋɴᴡᴏɴ 4 năm trước cách đây
mục cha
commit
958d8b6bb4

+ 1 - 0
CHANGELOG.md

@@ -40,6 +40,7 @@ All notable changes to Gogs are documented in this file.
 - [Security] Potential ability to delete files outside a repository.
 - [Security] Potential ability to set primary email on others' behalf from their verified emails.
 - [Security] Potential XSS attack via `.ipynb`. [#5170](https://github.com/gogs/gogs/issues/5170)
+- [Security] Potential CSRF attack in admin panel. [#5367](https://github.com/gogs/gogs/issues/5367)
 - [Security] Potential RCE on mirror repositories. [#5767](https://github.com/gogs/gogs/issues/5767)
 - [Security] Potential XSS attack with raw markdown API. [#5907](https://github.com/gogs/gogs/pull/5907)
 - Open/close milestone redirects to a 404 page. [#5677](https://github.com/gogs/gogs/issues/5677)

+ 1 - 0
conf/locale/locale_en-US.ini

@@ -1017,6 +1017,7 @@ dashboard.system_status = System Monitor Status
 dashboard.statistic_info = Gogs database has <b>%d</b> users, <b>%d</b> organizations, <b>%d</b> public keys, <b>%d</b> repositories, <b>%d</b> watches, <b>%d</b> stars, <b>%d</b> actions, <b>%d</b> accesses, <b>%d</b> issues, <b>%d</b> comments, <b>%d</b> social accounts, <b>%d</b> follows, <b>%d</b> mirrors, <b>%d</b> releases, <b>%d</b> login sources, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> labels, <b>%d</b> hook tasks, <b>%d</b> teams, <b>%d</b> update tasks, <b>%d</b> attachments.
 dashboard.operation_name = Operation Name
 dashboard.operation_switch = Switch
+dashboard.select_operation_to_run = Please select operation to run
 dashboard.operation_run = Run
 dashboard.clean_unbind_oauth = Clean unbound OAuthes
 dashboard.clean_unbind_oauth_success = All unbind OAuthes have been deleted successfully.

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
internal/assets/conf/conf_gen.go


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
internal/assets/templates/templates_gen.go


+ 1 - 1
internal/cmd/web.go

@@ -271,7 +271,7 @@ func runWeb(c *cli.Context) error {
 
 	// ***** START: Admin *****
 	m.Group("/admin", func() {
-		m.Get("", admin.Dashboard)
+		m.Combo("").Get(admin.Dashboard).Post(admin.Operation) // "/admin"
 		m.Get("/config", admin.Config)
 		m.Post("/config/test_mail", admin.SendTestMail)
 		m.Get("/monitor", admin.Monitor)

+ 49 - 53
internal/route/admin/admin.go

@@ -11,7 +11,6 @@ import (
 	"time"
 
 	"github.com/json-iterator/go"
-	"github.com/unknwon/com"
 
 	"gogs.io/gogs/internal/conf"
 	"gogs.io/gogs/internal/context"
@@ -110,63 +109,11 @@ func updateSystemStatus() {
 	sysStatus.NumGC = m.NumGC
 }
 
-// Operation types.
-type AdminOperation int
-
-const (
-	CLEAN_INACTIVATE_USER AdminOperation = iota + 1
-	CLEAN_REPO_ARCHIVES
-	CLEAN_MISSING_REPOS
-	GIT_GC_REPOS
-	SYNC_SSH_AUTHORIZED_KEY
-	SYNC_REPOSITORY_HOOKS
-	REINIT_MISSING_REPOSITORY
-)
-
 func Dashboard(c *context.Context) {
 	c.Title("admin.dashboard")
 	c.PageIs("Admin")
 	c.PageIs("AdminDashboard")
 
-	// Run operation.
-	op, _ := com.StrTo(c.Query("op")).Int()
-	if op > 0 {
-		var err error
-		var success string
-
-		switch AdminOperation(op) {
-		case CLEAN_INACTIVATE_USER:
-			success = c.Tr("admin.dashboard.delete_inactivate_accounts_success")
-			err = db.DeleteInactivateUsers()
-		case CLEAN_REPO_ARCHIVES:
-			success = c.Tr("admin.dashboard.delete_repo_archives_success")
-			err = db.DeleteRepositoryArchives()
-		case CLEAN_MISSING_REPOS:
-			success = c.Tr("admin.dashboard.delete_missing_repos_success")
-			err = db.DeleteMissingRepositories()
-		case GIT_GC_REPOS:
-			success = c.Tr("admin.dashboard.git_gc_repos_success")
-			err = db.GitGcRepos()
-		case SYNC_SSH_AUTHORIZED_KEY:
-			success = c.Tr("admin.dashboard.resync_all_sshkeys_success")
-			err = db.RewriteAuthorizedKeys()
-		case SYNC_REPOSITORY_HOOKS:
-			success = c.Tr("admin.dashboard.resync_all_hooks_success")
-			err = db.SyncRepositoryHooks()
-		case REINIT_MISSING_REPOSITORY:
-			success = c.Tr("admin.dashboard.reinit_missing_repos_success")
-			err = db.ReinitMissingRepositories()
-		}
-
-		if err != nil {
-			c.Flash.Error(err.Error())
-		} else {
-			c.Flash.Success(success)
-		}
-		c.RedirectSubpath("/admin")
-		return
-	}
-
 	c.Data["GitVersion"] = conf.Git.Version
 	c.Data["GoVersion"] = runtime.Version()
 	c.Data["BuildTime"] = conf.BuildTime
@@ -179,6 +126,55 @@ func Dashboard(c *context.Context) {
 	c.Success(DASHBOARD)
 }
 
+// Operation types.
+type AdminOperation int
+
+const (
+	CleanInactivateUser AdminOperation = iota + 1
+	CleanRepoArchives
+	CleanMissingRepos
+	GitGCRepos
+	SyncSSHAuthorizedKey
+	SyncRepositoryHooks
+	ReinitMissingRepository
+)
+
+func Operation(c *context.Context) {
+	var err error
+	var success string
+	switch AdminOperation(c.QueryInt("op")) {
+	case CleanInactivateUser:
+		success = c.Tr("admin.dashboard.delete_inactivate_accounts_success")
+		err = db.DeleteInactivateUsers()
+	case CleanRepoArchives:
+		success = c.Tr("admin.dashboard.delete_repo_archives_success")
+		err = db.DeleteRepositoryArchives()
+	case CleanMissingRepos:
+		success = c.Tr("admin.dashboard.delete_missing_repos_success")
+		err = db.DeleteMissingRepositories()
+	case GitGCRepos:
+		success = c.Tr("admin.dashboard.git_gc_repos_success")
+		err = db.GitGcRepos()
+	case SyncSSHAuthorizedKey:
+		success = c.Tr("admin.dashboard.resync_all_sshkeys_success")
+		err = db.RewriteAuthorizedKeys()
+	case SyncRepositoryHooks:
+		success = c.Tr("admin.dashboard.resync_all_hooks_success")
+		err = db.SyncRepositoryHooks()
+	case ReinitMissingRepository:
+		success = c.Tr("admin.dashboard.reinit_missing_repos_success")
+		err = db.ReinitMissingRepositories()
+	}
+
+	if err != nil {
+		c.Flash.Error(err.Error())
+	} else {
+		c.Flash.Success(success)
+	}
+	c.RedirectSubpath("/admin")
+	return
+}
+
 func SendTestMail(c *context.Context) {
 	emailAddr := c.Query("email")
 	// Send a test email to the user's email address and redirect back to Config

+ 40 - 32
templates/admin/dashboard.tmpl

@@ -42,38 +42,46 @@
 					{{.i18n.Tr "admin.dashboard.operations"}}
 				</h4>
 				<div class="ui unstackable attached table segment">
-					<table class="ui unstackable very basic table">
-						<tbody>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.delete_inactivate_accounts"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=1">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.delete_repo_archives"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=2">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.delete_missing_repos"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=3">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.git_gc_repos"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=4">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.resync_all_sshkeys"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=5">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.resync_all_hooks"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=6">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-							<tr>
-								<td>{{.i18n.Tr "admin.dashboard.reinit_missing_repos"}}</td>
-								<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubURL}}/admin?op=7">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
-							</tr>
-						</tbody>
-					</table>
+					<form action="{{AppSubURL}}/admin" method="post">
+						<table class="ui unstackable very basic table">
+							<tbody>
+								<tr>
+									<td>
+										{{.CSRFTokenHTML}}
+										<div class="ui fluid selection dropdown">
+											<input type="hidden" name="op">
+											<i class="dropdown icon"></i>
+											<div class="default text">{{.i18n.Tr "admin.dashboard.select_operation_to_run"}}</div>
+											<div class="menu">
+												<div class="item" data-value="1">
+													{{.i18n.Tr "admin.dashboard.delete_inactivate_accounts"}}
+												</div>
+												<div class="item" data-value="2">
+													{{.i18n.Tr "admin.dashboard.delete_repo_archives"}}
+												</div>
+												<div class="item" data-value="3">
+													{{.i18n.Tr "admin.dashboard.delete_missing_repos"}}
+												</div>
+												<div class="item" data-value="4">
+													{{.i18n.Tr "admin.dashboard.git_gc_repos"}}
+												</div>
+												<div class="item" data-value="5">
+													{{.i18n.Tr "admin.dashboard.resync_all_sshkeys"}}
+												</div>
+												<div class="item" data-value="6">
+													{{.i18n.Tr "admin.dashboard.resync_all_hooks"}}
+												</div>
+												<div class="item" data-value="7">
+													{{.i18n.Tr "admin.dashboard.reinit_missing_repos"}}
+												</div>
+											</div>
+										</div>
+									</td>
+									<td><button class="ui button" type="submit">{{.i18n.Tr "admin.dashboard.operation_run"}}</button></td>
+								</tr>
+							</tbody>
+						</table>
+					</form>
 				</div>
 
 				<h4 class="ui top attached header">

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác