Protected branch and approval gaps under audit
Six engineers maintain payments-api. main is protected, but Maintainers may push directly; two Maintainers do so for hotfixes. Merge requests require one approval from anyone. The project does not reset approvals when new commits are added. A flaky test causes pipelines to fail intermittently. One MR merged with a red pipeline because a Maintainer used Merge immediately after the pipeline was canceled. A release branch release/2024.10 was cut from main; three direct commits then landed on main and were cherry-picked to the release branch, producing different commit SHAs. A CODEOWNERS file exists but is not enforced. An auditor asks for evidence that no change reached main without review and a green pipeline in the last 30 days. In that period there were 42 MRs, 5 direct pushes to main, and 2 MRs approved only by their author. The team has no time to rewrite history and wants a practical remediation plan.
1. Identify the control gaps and the exact GitLab settings you would change.
Read the worked answer
Gaps: Maintainers can push to main; one approval from anyone allows self-approval; approvals are not reset on new commits; failed or canceled pipelines can still be merged; CODEOWNERS is not enforced; the release branch is not protected. Settings: In Settings > Repository > Protected branches, set main allowed to push to No one and allowed to merge to Maintainers; enable Require approval from code owners if using CODEOWNERS. In Settings > Merge requests, require at least one approval, enable Prevent approval by author and Prevent approvals by users who add commits, enable Remove all approvals when commits are added, and enable Pipelines must succeed with Skip pipelines disabled. Do not allow merge with failed pipelines. Protect release/* the same way, with merge access via MRs only. If Maintainers need hotfixes, give them a short-lived protected hotfix branch and a fast-track MR, not direct push. Keep the audit trail via MR approvals and pipeline status.
2. What can you do about the 5 direct pushes and 42 MRs for the auditor? What evidence can and cannot be produced?
Read the worked answer
You cannot retroactively prove review for the 5 direct pushes. You can produce the commit SHAs, authors, timestamps, and diffs from the repository graph and audit events if available; then create retrospective MRs that document the changes and have a second person review them, or revert and reapply through an MR if the change is risky. For the 42 MRs, export or screenshot each MR approval status, approver identity, pipeline result, and merge commit. Two MRs approved only by the author are exceptions; document them and re-review the changes. Evidence that is possible: MR list with approvals and pipeline status, protected branch settings, and audit events. Evidence that is not possible: a claim that every direct push had prior review. Be honest: state the gap, the remediation date, and the control now in place. The auditor usually cares more about a credible remediation than a perfect past.
3. How should CODEOWNERS and the release branch be handled going forward?
Read the worked answer
Put CODEOWNERS in the default branch and map critical paths, for example /payments/ @payments-core and /.gitlab-ci.yml @platform-security. Enforce it in the protected branch rule for main and release/*; otherwise it is only a suggestion. Require code owner approval in addition to the one general approval for those paths. For releases, stop cherry-picking direct commits from main into a long-lived release branch without MRs. Cut release/2024.10 from main, protect it, and require MRs for every change; cherry-pick via MRs so the release branch has its own reviewed SHAs. If the team needs a hotfix, branch from the release branch, open an MR to the release branch, then merge back to main through a second MR. Tag the release after the pipeline succeeds. This produces a clear audit chain: issue -> MR -> approval -> pipeline -> merge -> tag.