Skip to content

Commit 9956fdd

Browse files
committed
Fix GitOps Command Issue on Pushed Commit by Unautorized User
Issue: when an unautorized user sends GitOps comment on a pushed commit, PAC is triggering CI since access check is done only for pull_request event in verifyRepoAndUser func of controller. Solution: added a check for push event and Ops comment event type in verifyRepoAndUser func. https://issues.redhat.com/browse/SRVKP-7110 Signed-off-by: Zaki Shaikh <[email protected]>
1 parent 799386b commit 9956fdd

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

pkg/pipelineascode/match.go

+30-10
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,31 @@ is that what you want? make sure you use -n when generating the secret, eg: echo
132132
return repo, err
133133
}
134134

135+
// Verify whether the sender of the GitOps command (e.g., /test) has the appropriate permissions to
136+
// trigger CI on the repository, as any user is able to comment on a pushed commit in open-source repositories.
137+
if p.event.TriggerTarget == triggertype.Push && opscomments.IsAnyOpsEventType(p.event.EventType) {
138+
status := provider.StatusOpts{
139+
Status: CompletedStatus,
140+
Title: "Permission denied",
141+
Conclusion: failureConclusion,
142+
DetailsURL: p.event.URL,
143+
}
144+
if allowed, err := p.checkAccessOrErrror(ctx, repo, status, "by GitOps comment"); !allowed {
145+
return nil, err
146+
}
147+
}
148+
135149
// Check if the submitter is allowed to run this.
136150
// on push we don't need to check the policy since the user has pushed to the repo so it has access to it.
137151
// on comment we skip it for now, we are going to check later on
138152
if p.event.TriggerTarget != triggertype.Push && p.event.EventType != opscomments.NoOpsCommentEventType.String() {
139-
if allowed, err := p.checkAccessOrErrror(ctx, repo, "via "+p.event.TriggerTarget.String()); !allowed {
153+
status := provider.StatusOpts{
154+
Status: queuedStatus,
155+
Title: "Pending approval, needs /ok-to-test",
156+
Conclusion: pendingConclusion,
157+
DetailsURL: p.event.URL,
158+
}
159+
if allowed, err := p.checkAccessOrErrror(ctx, repo, status, "via "+p.event.TriggerTarget.String()); !allowed {
140160
return nil, err
141161
}
142162
}
@@ -245,7 +265,13 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
245265
// if the event is a comment event, but we don't have any match from the keys.OnComment then do the ACL checks again
246266
// we skipped previously so we can get the match from the event to the pipelineruns
247267
if p.event.EventType == opscomments.NoOpsCommentEventType.String() || p.event.EventType == opscomments.OnCommentEventType.String() {
248-
if allowed, err := p.checkAccessOrErrror(ctx, repo, "by gitops comment"); !allowed {
268+
status := provider.StatusOpts{
269+
Status: queuedStatus,
270+
Title: "Pending approval, needs /ok-to-test",
271+
Conclusion: pendingConclusion,
272+
DetailsURL: p.event.URL,
273+
}
274+
if allowed, err := p.checkAccessOrErrror(ctx, repo, status, "by GitOps comment"); !allowed {
249275
return nil, err
250276
}
251277
}
@@ -370,7 +396,7 @@ func (p *PacRun) checkNeedUpdate(_ string) (string, bool) {
370396
return "", false
371397
}
372398

373-
func (p *PacRun) checkAccessOrErrror(ctx context.Context, repo *v1alpha1.Repository, viamsg string) (bool, error) {
399+
func (p *PacRun) checkAccessOrErrror(ctx context.Context, repo *v1alpha1.Repository, status provider.StatusOpts, viamsg string) (bool, error) {
374400
allowed, err := p.vcx.IsAllowed(ctx, p.event)
375401
if err != nil {
376402
return false, err
@@ -383,13 +409,7 @@ func (p *PacRun) checkAccessOrErrror(ctx context.Context, repo *v1alpha1.Reposit
383409
msg = fmt.Sprintf("User: %s AccountID: %s is not allowed to trigger CI %s on this repo.", p.event.Sender, p.event.AccountID, viamsg)
384410
}
385411
p.eventEmitter.EmitMessage(repo, zap.InfoLevel, "RepositoryPermissionDenied", msg)
386-
status := provider.StatusOpts{
387-
Status: queuedStatus,
388-
Title: "Pending approval, needs /ok-to-test",
389-
Conclusion: pendingConclusion,
390-
Text: msg,
391-
DetailsURL: p.event.URL,
392-
}
412+
status.Text = msg
393413
if err := p.vcx.CreateStatus(ctx, p.event, status); err != nil {
394414
return false, fmt.Errorf("failed to run create status, user is not allowed to run the CI:: %w", err)
395415
}

pkg/pipelineascode/pipelineascode_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,24 @@ func TestRun(t *testing.T) {
465465
finalStatusText: "PipelineRun has no taskruns",
466466
expectedNumberofCleanups: 10,
467467
},
468+
{
469+
name: "Do not allow unauthorized user to run CI on pushed commit",
470+
runevent: info.Event{
471+
SHA: "principale",
472+
Organization: "organizationes",
473+
Repository: "lagaffe",
474+
URL: "https://service/documentation",
475+
HeadBranch: "main",
476+
BaseBranch: "main",
477+
Sender: "fantasio",
478+
EventType: "test-all-comment",
479+
TriggerTarget: "push",
480+
},
481+
tektondir: "testdata/push_branch",
482+
finalStatus: "failure",
483+
finalStatusText: "User fantasio is not allowed to trigger CI by GitOps comment on this repo.",
484+
skipReplyingOrgPublicMembers: true,
485+
},
468486
}
469487
for _, tt := range tests {
470488
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)