diff options
author | Jason Song <i@wolfogre.com> | 2023-02-24 15:58:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-24 15:58:49 +0800 |
commit | edf98a2dc30956c8e04b778bb7f1ce55c14ba963 (patch) | |
tree | 2d8b7708f76a36c27700ab128082bdb976a0753e /routers | |
parent | a6175b01d92a75dcbadbbfc6782a486636fd62a2 (diff) | |
download | gitea-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.go | 49 | ||||
-rw-r--r-- | routers/web/web.go | 1 |
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) |