aboutsummaryrefslogtreecommitdiffstats
path: root/routers
diff options
context:
space:
mode:
authorJason Song <i@wolfogre.com>2023-02-24 15:58:49 +0800
committerGitHub <noreply@github.com>2023-02-24 15:58:49 +0800
commitedf98a2dc30956c8e04b778bb7f1ce55c14ba963 (patch)
tree2d8b7708f76a36c27700ab128082bdb976a0753e /routers
parenta6175b01d92a75dcbadbbfc6782a486636fd62a2 (diff)
downloadgitea-edf98a2dc30956c8e04b778bb7f1ce55c14ba963.tar.gz
gitea-edf98a2dc30956c8e04b778bb7f1ce55c14ba963.zip
Require approval to run actions for fork pull request (#22803)
Currently, Gitea will run actions automatically which are triggered by fork pull request. It's a security risk, people can create a PR and modify the workflow yamls to execute a malicious script. So we should require approval for first-time contributors, which is the default strategy of a public repo on GitHub, see [Approving workflow runs from public forks](https://docs.github.com/en/actions/managing-workflow-runs/approving-workflow-runs-from-public-forks). Current strategy: - don't need approval if it's not a fork PR; - always need approval if the user is restricted; - don't need approval if the user can write; - don't need approval if the user has been approved before; - otherwise, need approval. https://user-images.githubusercontent.com/9418365/217207121-badf50a8-826c-4425-bef1-d82d1979bc81.mov GitHub has an option for that, you can see that at `/<owner>/<repo>/settings/actions`, and we can support that later. <img width="835" alt="image" src="https://user-images.githubusercontent.com/9418365/217199990-2967e68b-e693-4e59-8186-ab33a1314a16.png"> --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'routers')
-rw-r--r--routers/web/repo/actions/view.go49
-rw-r--r--routers/web/web.go1
2 files changed, 45 insertions, 5 deletions
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 5370310e8d..dd2750f905 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -49,11 +49,12 @@ type ViewRequest struct {
type ViewResponse struct {
State struct {
Run struct {
- Link string `json:"link"`
- Title string `json:"title"`
- CanCancel bool `json:"canCancel"`
- Done bool `json:"done"`
- Jobs []*ViewJob `json:"jobs"`
+ Link string `json:"link"`
+ Title string `json:"title"`
+ CanCancel bool `json:"canCancel"`
+ CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve
+ Done bool `json:"done"`
+ Jobs []*ViewJob `json:"jobs"`
} `json:"run"`
CurrentJob struct {
Title string `json:"title"`
@@ -107,6 +108,7 @@ func ViewPost(ctx *context_module.Context) {
resp.State.Run.Title = run.Title
resp.State.Run.Link = run.Link()
resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions)
+ resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.Done = run.Status.IsDone()
resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json
for _, v := range jobs {
@@ -135,6 +137,9 @@ func ViewPost(ctx *context_module.Context) {
resp.State.CurrentJob.Title = current.Name
resp.State.CurrentJob.Detail = current.Status.LocaleString(ctx.Locale)
+ if run.NeedApproval {
+ resp.State.CurrentJob.Detail = ctx.Locale.Tr("actions.need_approval_desc")
+ }
resp.State.CurrentJob.Steps = make([]*ViewJobStep, 0) // marshal to '[]' instead fo 'null' in json
resp.Logs.StepsLog = make([]*ViewStepLog, 0) // marshal to '[]' instead fo 'null' in json
if task != nil {
@@ -261,6 +266,40 @@ func Cancel(ctx *context_module.Context) {
ctx.JSON(http.StatusOK, struct{}{})
}
+func Approve(ctx *context_module.Context) {
+ runIndex := ctx.ParamsInt64("run")
+
+ current, jobs := getRunJobs(ctx, runIndex, -1)
+ if ctx.Written() {
+ return
+ }
+ run := current.Run
+ doer := ctx.Doer
+
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ run.NeedApproval = false
+ run.ApprovedBy = doer.ID
+ if err := actions_model.UpdateRun(ctx, run, "need_approval", "approved_by"); err != nil {
+ return err
+ }
+ for _, job := range jobs {
+ if len(job.Needs) == 0 && job.Status.IsBlocked() {
+ job.Status = actions_model.StatusWaiting
+ _, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ }); err != nil {
+ ctx.Error(http.StatusInternalServerError, err.Error())
+ return
+ }
+
+ ctx.JSON(http.StatusOK, struct{}{})
+}
+
// getRunJobs gets the jobs of runIndex, and returns jobs[jobIndex], jobs.
// Any error will be written to the ctx.
// It never returns a nil job of an empty jobs, if the jobIndex is out of range, it will be treated as 0.
diff --git a/routers/web/web.go b/routers/web/web.go
index 88e27ad678..ff312992dd 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1286,6 +1286,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
})
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
+ m.Post("/approve", reqRepoActionsWriter, actions.Approve)
})
}, reqRepoActionsReader, actions.MustEnableActions)