Forbid branch protection force-push updates with policy in place
What does this MR do and why?
MR approval policies support the prevent_pushing_and_force_pushing property which prevents force-pushing to any branch affected by a policy. This is achieved through an access check.
We also disable the UI controls for protected branches matched by such a policy. However we don't prevent changes via API to a protected branch's allow_force_push column or its push access levels. Since our access check works independently of protected branches, this is not a policy bypass but merely confusing.
This MR rejects changes to a protected branch's allow_force_push column or its push access levels when a policy applies that sets prevent_pushing_and_force_pushing.
Database
New query:
SELECT
*
FROM
"scan_result_policies"
WHERE
"scan_result_policies"."project_id" = 57505981
AND (project_approval_settings ->> 'prevent_pushing_and_force_pushing' = 'true')
LIMIT 1;
https://console.postgres.ai/gitlab/gitlab-production-main/sessions/28603/commands/89175
We process up to 20 policies so there are no more than 20 rows to filter per project_id.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
How to set up and validate locally
-
Create a new project and note its ID
-
Navigate to
Code > Branchesand create a new branch, sayfoobar -
Navigate to
Secure > Policiesand create the following MR approval policy:
type: approval_policy
name: Prevent force-pushing
enabled: true
rules:
- type: any_merge_request
branches: [foobar]
commits: any
actions:
- type: require_approval
approvals_required: 1
role_approvers:
- owner
approval_settings:
prevent_pushing_and_force_pushing: true
-
Navigate to
Settings > Repositoryand create a protected branch forfoobar. -
Verify you cannot update
allows_force_pushvia API:
curl -X PATCH -H "PRIVATE-TOKEN: glpat-REDACTED" -H "Content-Type: application/json" -d '{"allow_force_push": true}' "http://gdk.test:3000/api/v4/projects/$PROJECT_ID/protected_branches/foobar"
- Verify you cannot update its push access levels via API:
curl -X PATCH -H "PRIVATE-TOKEN: glpat-REDACTED" -H "Content-Type: application/json" -d '{"allowed_to_push": [{"access_level": 40}]}' "http://gdk.test:3000/api/v4/projects/$PROJECT_ID/protected_branches/foobar"
Related to #436539 (closed)