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 mergeandAllowed to push and mergetoNo 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 tagtosecret-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::PipelineSchedulesControlleris usingprojectas the subject forauthorize_create_pipeline_schedule!, see howmethod_missinghandlesauthorize_create_pipeline_schedule!at https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/controllers/projects/application_controller.rb#L61-70 -
ProjectPolicydoesn'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::PipelineSchedulesControllerto 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::PipelineSchedulePolicyto disable:create_pipeline_schedulewhen 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 endNOTE: current condition
:protected_refis using@subject.refto check if the ref is a protected branch, but it should use@subject.ref_for_display. BecauseProtectedBranchis created using a ref name e.g."secret-branch", butCi::PipelineScheduleis created using a full ref e.g."refs/heads/secret-branch". Not sure if this is also a bug forCi::PipelinePolicyas well sinceCi::PipelineSchedulePolicyinherits from it.
