Race condition in remove_approvals_with_new_commit allows merge before approvals are removed
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Summary
Race condition in remove_approvals_with_new_commit allows automated merge commands to bypass approval requirements by executing before approvals are asynchronously removed.
Steps to reproduce
Prerequisites:
- GitLab version 18.0 (self-managed)
- Project with approval policy configured with
remove_approvals_with_new_commitset totrue - Using
glabCLI for automated merge operations
Steps:
- Push a commit to a merge request
- Obtain approval for the merge request
- Wait for all checks to pass and MR to show as "green/ready"
- Immediately push a new commit and attempt to merge using rapid automation:
git push && glab mr merge --auto-merge=false -y <MR ID>
- Repeat steps 3-4 multiple times
What is the current bug behavior?
When using rapid automated commands (git push && glab mr merge), the merge operation occasionally succeeds before the approval removal process completes. This allows unapproved code to be merged into protected branches, bypassing the remove_approvals_with_new_commit safeguard.
The inconsistent behavior (mostly failing with 405, occasionally succeeding) suggests the approval removal is happening asynchronously in a background process, creating a race condition window where:
- The push operation completes and returns
- The merge API call is executed immediately after
- The merge is accepted before the async approval removal job processes the new commit
- The merge completes with the new, unapproved commit included
Note that under normal human-operated conditions (with natural delays between push and merge), the feature works correctly
What is the expected correct behavior?
The merge operation should always be rejected when a new commit is pushed to a merge request that has remove_approvals_with_new_commit enabled, regardless of timing or how quickly the merge command is executed. The approval removal should be synchronous or the merge validation should check for pending approval state changes before allowing the merge to proceed.
Specifically:
- Pushing a new commit should immediately invalidate existing approvals before the push operation returns, OR
- The merge API should validate that no approval state changes are pending before accepting a merge request, OR
- The merge operation should be blocked until all async approval removal processes complete
The current implementation creates a security vulnerability where automated tooling or scripts can potentially bypass approval requirements through timing manipulation.
Results of GitLab environment info
Expand for output related to GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of: \`sudo gitlab-rake gitlab:env:info\`) (For installations from source run and paste the output of: \`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production\`)
Results of GitLab application Check
Expand for output related to the GitLab application check
(For installations with omnibus-gitlab package run and paste the output of: `sudo gitlab-rake gitlab:check SANITIZE=true`) (For installations from source run and paste the output of: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`) (we will only investigate if the tests are passing)