Skip to content

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 > Branches and create a new branch, say foobar

  • Navigate to Secure > Policies and 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 > Repository and create a protected branch for foobar.

  • Verify you cannot update allows_force_push via 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)

Edited by Dominic Bauer

Merge request reports