Skip to content

Commit 12139f5

Browse files
committed
Prevent re-review and dismiss review actions on closed or merged PRs
1 parent 475b6e8 commit 12139f5

File tree

5 files changed

+78
-5
lines changed

5 files changed

+78
-5
lines changed

models/issues/review.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ func (err ErrNotValidReviewRequest) Unwrap() error {
6666
return util.ErrInvalidArgument
6767
}
6868

69+
// ErrReviewRequestOnClosedPR represents an error when an user tries to request a re-review on a closed or merged PR.
70+
type ErrReviewRequestOnClosedPR struct{}
71+
72+
// IsErrReviewRequestOnClosedPR checks if an error is an ErrReviewRequestOnClosedPR.
73+
func IsErrReviewRequestOnClosedPR(err error) bool {
74+
_, ok := err.(ErrReviewRequestOnClosedPR)
75+
return ok
76+
}
77+
78+
func (err ErrReviewRequestOnClosedPR) Error() string {
79+
return "cannot request a re-review on a closed or merged PR"
80+
}
81+
82+
func (err ErrReviewRequestOnClosedPR) Unwrap() error {
83+
return util.ErrPermissionDenied
84+
}
85+
6986
// ReviewType defines the sort of feedback a review gives
7087
type ReviewType int
7188

@@ -618,9 +635,24 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
618635
return nil, err
619636
}
620637

621-
// skip it when reviewer hase been request to review
622-
if review != nil && review.Type == ReviewTypeRequest {
623-
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
638+
if review != nil {
639+
// skip it when reviewer hase been request to review
640+
if review.Type == ReviewTypeRequest {
641+
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
642+
}
643+
644+
if issue.IsClosed {
645+
return nil, ErrReviewRequestOnClosedPR{}
646+
}
647+
648+
if issue.IsPull {
649+
if err := issue.LoadPullRequest(ctx); err != nil {
650+
return nil, err
651+
}
652+
if issue.PullRequest.HasMerged {
653+
return nil, ErrReviewRequestOnClosedPR{}
654+
}
655+
}
624656
}
625657

626658
// if the reviewer is an official reviewer,

routers/web/repo/issue.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,10 @@ func UpdatePullReviewRequest(ctx *context.Context) {
24982498

24992499
_, err = issue_service.ReviewRequest(ctx, issue, ctx.Doer, reviewer, action == "attach")
25002500
if err != nil {
2501+
if issues_model.IsErrReviewRequestOnClosedPR(err) {
2502+
ctx.Status(http.StatusForbidden)
2503+
return
2504+
}
25012505
ctx.ServerError("ReviewRequest", err)
25022506
return
25032507
}

routers/web/repo/pull_review.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ func DismissReview(ctx *context.Context) {
277277
form := web.GetForm(ctx).(*forms.DismissReviewForm)
278278
comm, err := pull_service.DismissReview(ctx, form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.Doer, true, true)
279279
if err != nil {
280+
if pull_service.IsErrDismissRequestOnClosedPR(err) {
281+
ctx.Status(http.StatusForbidden)
282+
return
283+
}
280284
ctx.ServerError("pull_service.DismissReview", err)
281285
return
282286
}

services/pull/review.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,29 @@ import (
2020
"code.gitea.io/gitea/modules/log"
2121
"code.gitea.io/gitea/modules/optional"
2222
"code.gitea.io/gitea/modules/setting"
23+
"code.gitea.io/gitea/modules/util"
2324
notify_service "code.gitea.io/gitea/services/notify"
2425
)
2526

2627
var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`)
2728

29+
// ErrDismissRequestOnClosedPR represents an error when an user tries to dismiss a review associated to a closed or merged PR.
30+
type ErrDismissRequestOnClosedPR struct{}
31+
32+
// IsErrDismissRequestOnClosedPR checks if an error is an ErrDismissRequestOnClosedPR.
33+
func IsErrDismissRequestOnClosedPR(err error) bool {
34+
_, ok := err.(ErrDismissRequestOnClosedPR)
35+
return ok
36+
}
37+
38+
func (err ErrDismissRequestOnClosedPR) Error() string {
39+
return "can't dismiss a review associated to a closed or merged PR"
40+
}
41+
42+
func (err ErrDismissRequestOnClosedPR) Unwrap() error {
43+
return util.ErrPermissionDenied
44+
}
45+
2846
// checkInvalidation checks if the line of code comment got changed by another commit.
2947
// If the line got changed the comment is going to be invalidated.
3048
func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error {
@@ -382,6 +400,21 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
382400
return nil, fmt.Errorf("reviews's repository is not the same as the one we expect")
383401
}
384402

403+
issue := review.Issue
404+
405+
if issue.IsClosed {
406+
return nil, ErrDismissRequestOnClosedPR{}
407+
}
408+
409+
if issue.IsPull {
410+
if err := issue.LoadPullRequest(ctx); err != nil {
411+
return nil, err
412+
}
413+
if issue.PullRequest.HasMerged {
414+
return nil, ErrDismissRequestOnClosedPR{}
415+
}
416+
}
417+
385418
if err := issues_model.DismissReview(ctx, review, isDismiss); err != nil {
386419
return nil, err
387420
}

templates/repo/issue/view_content/sidebar.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
{{end}}
6060
</div>
6161
<div class="tw-flex tw-items-center tw-gap-2">
62-
{{if (and $.Permission.IsAdmin (or (eq .Review.Type 1) (eq .Review.Type 3)) (not $.Issue.IsClosed))}}
62+
{{if (and $.Permission.IsAdmin (or (eq .Review.Type 1) (eq .Review.Type 3)) (not $.Issue.IsClosed) (not $.Issue.PullRequest.HasMerged))}}
6363
<a href="#" class="ui muted icon tw-flex tw-items-center show-modal" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dismiss_review"}}" data-modal="#dismiss-review-modal-{{.Review.ID}}">
6464
{{svg "octicon-x" 20}}
6565
</a>
@@ -91,7 +91,7 @@
9191
{{svg "octicon-hourglass" 16}}
9292
</span>
9393
{{end}}
94-
{{if .CanChange}}
94+
{{if and .CanChange (or .Checked (and (not $.Issue.IsClosed) (not $.Issue.PullRequest.HasMerged)))}}
9595
<a href="#" class="ui muted icon re-request-review{{if .Checked}} checked{{end}}" data-tooltip-content="{{if .Checked}}{{ctx.Locale.Tr "repo.issues.remove_request_review"}}{{else}}{{ctx.Locale.Tr "repo.issues.re_request_review"}}{{end}}" data-issue-id="{{$.Issue.ID}}" data-id="{{.ItemID}}" data-update-url="{{$.RepoLink}}/issues/request_review">{{if .Checked}}{{svg "octicon-trash"}}{{else}}{{svg "octicon-sync"}}{{end}}</a>
9696
{{end}}
9797
{{svg (printf "octicon-%s" .Review.Type.Icon) 16 (printf "text %s" (.Review.HTMLTypeColorName))}}

0 commit comments

Comments
 (0)