From edf98a2dc30956c8e04b778bb7f1ce55c14ba963 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Fri, 24 Feb 2023 15:58:49 +0800 Subject: 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 `///settings/actions`, and we can support that later. image --------- Co-authored-by: Lunny Xiao --- models/actions/run.go | 12 +++++------- models/actions/run_list.go | 8 ++++++++ models/actions/status.go | 4 ++++ models/migrations/migrations.go | 4 ++++ models/migrations/v1_20/v244.go | 22 ++++++++++++++++++++++ 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 models/migrations/v1_20/v244.go (limited to 'models') diff --git a/models/actions/run.go b/models/actions/run.go index 14d191c814..a8d991471e 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -32,11 +32,13 @@ type ActionRun struct { OwnerID int64 `xorm:"index"` WorkflowID string `xorm:"index"` // the name of workflow file Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository - TriggerUserID int64 - TriggerUser *user_model.User `xorm:"-"` + TriggerUserID int64 `xorm:"index"` + TriggerUser *user_model.User `xorm:"-"` Ref string CommitSHA string IsForkPullRequest bool + NeedApproval bool // may need approval if it's a fork pull request + ApprovedBy int64 `xorm:"index"` // who approved Event webhook_module.HookEventType EventPayload string `xorm:"LONGTEXT"` Status Status `xorm:"index"` @@ -164,10 +166,6 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork } run.Index = index - if run.Status.IsUnknown() { - run.Status = StatusWaiting - } - if err := db.Insert(ctx, run); err != nil { return err } @@ -191,7 +189,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork job.EraseNeeds() payload, _ := v.Marshal() status := StatusWaiting - if len(needs) > 0 { + if len(needs) > 0 || run.NeedApproval { status = StatusBlocked } runJobs = append(runJobs, &ActionRunJob{ diff --git a/models/actions/run_list.go b/models/actions/run_list.go index f9d8417227..bc69c65840 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -68,6 +68,8 @@ type FindRunOptions struct { OwnerID int64 IsClosed util.OptionalBool WorkflowFileName string + TriggerUserID int64 + Approved bool // not util.OptionalBool, it works only when it's true } func (opts FindRunOptions) toConds() builder.Cond { @@ -89,6 +91,12 @@ func (opts FindRunOptions) toConds() builder.Cond { if opts.WorkflowFileName != "" { cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowFileName}) } + if opts.TriggerUserID > 0 { + cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID}) + } + if opts.Approved { + cond = cond.And(builder.Gt{"approved_by": 0}) + } return cond } diff --git a/models/actions/status.go b/models/actions/status.go index 059cf9bc09..c97578f2ac 100644 --- a/models/actions/status.go +++ b/models/actions/status.go @@ -82,6 +82,10 @@ func (s Status) IsRunning() bool { return s == StatusRunning } +func (s Status) IsBlocked() bool { + return s == StatusBlocked +} + // In returns whether s is one of the given statuses func (s Status) In(statuses ...Status) bool { for _, v := range statuses { diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 989a1d6ae1..585457e474 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/migrations/v1_17" "code.gitea.io/gitea/models/migrations/v1_18" "code.gitea.io/gitea/models/migrations/v1_19" + "code.gitea.io/gitea/models/migrations/v1_20" "code.gitea.io/gitea/models/migrations/v1_6" "code.gitea.io/gitea/models/migrations/v1_7" "code.gitea.io/gitea/models/migrations/v1_8" @@ -463,6 +464,9 @@ var migrations = []Migration{ NewMigration("Add exclusive label", v1_19.AddExclusiveLabel), // Gitea 1.19.0 ends at v244 + + // v244 -> v245 + NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go new file mode 100644 index 0000000000..977566ad7d --- /dev/null +++ b/models/migrations/v1_20/v244.go @@ -0,0 +1,22 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import ( + "xorm.io/xorm" +) + +func AddNeedApprovalToActionRun(x *xorm.Engine) error { + /* + New index: TriggerUserID + New fields: NeedApproval, ApprovedBy + */ + type ActionRun struct { + TriggerUserID int64 `xorm:"index"` + NeedApproval bool // may need approval if it's a fork pull request + ApprovedBy int64 `xorm:"index"` // who approved + } + + return x.Sync(new(ActionRun)) +} -- cgit v1.2.3