Developers can create pipeline schedules on protected branches even if they don't have access to merge
HackerOne report #1936572 by js_noob
on 2023-04-06, assigned to @ottilia_westerlund:
Report | Attachments | How To Reproduce
Report
Summary
Hello team, as a part of the branch protection rules, developers should not be able to run or schedule pipelines on branches they don't have merge permissions to. From the docs
The schedule owner must have the Developer role. For pipelines on protected branches, the schedule owner must be allowed to merge to the branch.
However, a developer can bypass this condition.
Steps to reproduce
As an owner:
- Create a project
- Create a new branch called
secret-branch
- Navigate to https://gitlab.com/OWNER/PROJECT/-/settings/repository#js-protected-branches-settings and set
Allowed to merge
andAllowed to push and merge
toNo one
- Add a developer to the project
As the developer
- Navigate to https://gitlab.com/OWNER/PROJECT/-/pipeline_schedules/new
- Fill out all info and set
Target branch or tag
tosecret-branch
- Verify that no errors are thrown and the schedule is created successfully
Video/POC
bandicam_2023-04-06_18-26-22-117.mp4
Impact
Developers with no merge access can create pipeline schedules on protected branches.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section:
Possible solution:
The issue is that:
-
Projects::PipelineSchedulesController
is usingproject
as the subject forauthorize_create_pipeline_schedule!
, see howmethod_missing
handlesauthorize_create_pipeline_schedule!
at https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/controllers/projects/application_controller.rb#L61-70 -
ProjectPolicy
doesn't check the ref for permission:create_pipeline_schedule
, see https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/policies/project_policy.rb#L490-494
Therefore, here is a possible solution:
-
change the
Projects::PipelineSchedulesController
to use custom authorize action for pipeline schedule instead:def new_schedule # We need the `ref` here for authorization below project.pipeline_schedules.new(ref: params.dig(:schedule, :ref)) end def authorize_create_pipeline_schedule! return access_denied! unless can?(current_user, :create_pipeline_schedule, new_schedule) end
-
change the
Ci::PipelineSchedulePolicy
to disable:create_pipeline_schedule
when it's a protected branch:condition(:protected_ref) do ref_protected?(@user, @subject.project, @subject.project.repository.tag_exists?(@subject.ref), @subject.ref_for_display) end rule { custom_protected_ref }.policy do prevent :play_pipeline_schedule prevent :create_pipeline_schedule end
NOTE: current condition
:protected_ref
is using@subject.ref
to check if the ref is a protected branch, but it should use@subject.ref_for_display
. BecauseProtectedBranch
is created using a ref name e.g."secret-branch"
, butCi::PipelineSchedule
is created using a full ref e.g."refs/heads/secret-branch"
. Not sure if this is also a bug forCi::PipelinePolicy
as well sinceCi::PipelineSchedulePolicy
inherits from it.