Compliance violations report flags MRs auto-merged via another MR as unapproved, misattributing the violation

Summary

When an open merge request's commits are already present in the target branch because a different MR was merged first, GitLab automatically transitions the first MR to merged (see Merged as part of a different merge request, #436061 (closed), #456426).

The compliance-violation pipeline does not know about this state transition. ComplianceManagement::MergeRequests::CreateComplianceViolationsService runs MergeRequests::ComplianceViolation.process_merge_request for every merge request where merged? is true — regardless of how it became merged — and each check in MergeRequests::ComplianceViolation::VIOLATIONS (ApprovedByMergeRequestAuthor, ApprovedByCommitter, ApprovedByInsufficientUsers) evaluates only the MR's own approved_by_users:

# ee/lib/gitlab/compliance_management/violations/approved_by_insufficient_users.rb
def violation?
  @merge_request.approved_by_users.count < MINIMUM_NUMBER_OF_APPROVERS
end

Because the auto-merged MR was never itself reviewed (the equivalent code was reviewed and approved on the other MR), it has 0 approvals of its own, and the compliance pipeline creates a "Fewer than two approvals" violation (severity: High) exactly as it would for a genuine policy bypass. The violation is also misattributed:

violating_user: @merge_request.metrics&.merged_by || @merge_request.merge_user,

merge_user on the auto-merged MR is set to the user who merged the other MR (per UpdateMergeRequestsWorker), so a user who took no merge action on this MR is recorded as the violator.

This produces a false-positive compliance/audit finding that is functionally indistinguishable from a genuine approval-policy bypass, even though the code was reviewed, approved, and scanned before entering the protected branch — just via a different MR.

Steps to reproduce

  1. Create MR A and MR B against the same protected target branch, where MR B's branch contains a subset of commits also present in MR A (e.g., B was branched from A, or both contain an identical cherry-picked commit).
  2. Get MR A fully approved (satisfying minimum_approvals_required_2 / 2 approvals) and merge it. Do not directly approve or merge MR B.
  3. Observe MR B automatically transition to merged shortly after, because its commit(s) are now contained in the target branch.
  4. Check Secure > Compliance center > Violations (or the compliance_violations data) for the project/group.

Actual behavior

MR B appears in the compliance violations report as a "Fewer than two approvals" (and potentially "Committers approved merge request") violation, with the violating user set to whoever merged MR A — even though MR A had full, valid approvals and a passing pipeline before merging.

Expected behavior

When a merge request is auto-transitioned to merged as a consequence of another MR merging first, the compliance-violation pipeline should either:

  • Not create a violation record for the auto-merged MR when the MR that actually introduced the commits satisfied the applicable approval rules, and instead associate the audit evidence with that originating MR, or
  • Clearly distinguish/flag the violation as arising from an automatic state transition rather than an actual merge action, and not attribute it to a user who took no action on the affected MR.

Why this is a distinct defect from #456426 and #512551

  • #456426 ("Merged system notes displayed as manually merged when it is not") is scoped to the system note / activity feed text shown for the auto-merged MR. Fixing it does not touch MergeRequests::ComplianceViolation or any compliance/audit data — the violation record described above would still be created and misattributed even with a perfectly worded system note.
  • #512551 ("Improve reporting for when a merge request is merged via another MR") proposes exposing source_commit_id / source_merge_request_id on the Resource State Events API. That's a necessary building block for attribution, but by itself doesn't change whether/how ComplianceManagement::MergeRequests::CreateComplianceViolationsService creates or attributes a violation.

This issue tracks fixing the compliance/audit-attribution behavior specifically. It's related to both.

Business impact

Customers relying on the Compliance Violations report / MR approval policies as audit evidence (SOC 2, internal governance, etc.) see this as a false positive suggesting protected code was merged without approval, when in fact it went through a fully approved and scanned MR. This requires manual forensic investigation per occurrence and erodes confidence in the compliance tooling. Multiple Support tickets from Ultimate customers have raised this (see #456426 for linked internal references).

Edited by 🤖 GitLab Bot 🤖