CI/CD provider
Branch protection missing
Control: Branch must be protected · Config key: branchMustBeProtected
📋 What is this?
A branch is not protected on the repository.
⚠️ Impact
Unprotected branches are highly vulnerable to unauthorized modifications. For instance, any member can push malicious code directly to your production branch without any review or validation.
🔧 How to fix
Enable branch protection on the branch to restrict changes to authorized users only.
# GitLab project settings — ❌ Branch not protected# Branch "main" has no protection rules# Anyone with Developer access can:# - Push directly# - Force push# - Delete the branch
# .plumber.yaml configuration requiring protection:branchMustBeProtected: enabled: true defaultMustBeProtected: true namePatterns: - main - release/*# GitLab project settings — ✅ Branch properly protected# Settings > Repository > Protected Branches:## Branch: main# Allowed to merge: Developers + Maintainers# Allowed to push: Maintainers# Allow force push: No# Code owner approval required: Yes
# .plumber.yamlbranchMustBeProtected: enabled: true defaultMustBeProtected: true namePatterns: - main - release/* allowForcePush: false codeOwnerApprovalRequired: true minMergeAccessLevel: 30 # Developer minPushAccessLevel: 40 # Maintainer💡 Tips
- Use
namePatternswith wildcards to protect branch families (e.g.,release/*). - Set
minPushAccessLevel: 40(Maintainer) to prevent developers from pushing directly. - Enable
codeOwnerApprovalRequiredif you use a CODEOWNERS file.
⚙️ Configuration
This control is configured in .plumber.yaml under the gitlab section:
gitlab:
controls:
branchMustBeProtected:
enabled: trueSee the CLI documentation for the full configuration reference. On Plumber Platform, the same key is used in your policy configuration.
Branch protection missing
Control: Branch must be protected · Config key: branchMustBeProtected
📋 What is this?
A branch matching the configured protection patterns has no protection rule in either classic Branch Protection or any Repository or Organization Ruleset that covers it. Plumber reads both mechanisms and treats a branch as protected only when at least one of them applies.
⚠️ Impact
Without protection on the matching branches, anyone with write access can push directly, force-push, rewrite history, or delete the branch. Required reviews, code-owner approvals, and status checks are all bypassed.
🔧 How to fix
Add protection through either mechanism. Classic Branch Protection lives under Settings > Branches; Repository Rulesets live under Settings > Rules > Rulesets (rulesets inherited from the organization count too). Plumber merges all sources, stricter wins, so a rule defined in one mechanism contributes to the effective configuration even if the other has nothing for the same branch.
# GitHub repo settings — ❌ No protection on `main`# Settings > Rules > Rulesets:# (no ruleset targeting `main`)## Anyone with Write access can:# - push directly to `main`# - force-push and rewrite history# - delete the branch
# .plumber.yamlgithub: controls: branchMustBeProtected: enabled: true namePatterns: [main, release/*] allowForcePush: false requirePullRequestReviews: true# GitHub repo settings — ✅ Ruleset enforces review + no force-push# Settings > Rules > Rulesets > New branch ruleset:# Target: main# Rules:# - Restrict deletions# - Block force pushes# - Require a pull request before merging# · Required approvals: 1# · Dismiss stale approvals on new push# · Require review from Code Owners# - Require status checks to pass before merging# · build, test, codeql💡 Tips
- Plumber needs
Administration: Read(fine-grained PAT) orreposcope (classic) to read protection rules at all. Without it the rule reportspartialControls(abstain), not a pass. - Classic Branch Protection and Rulesets are unioned. A code-owner-approval rule defined only in a Ruleset (no classic rule for the same branch) is still seen.
- Disabled or evaluate-mode rulesets are ignored automatically. Only enforced rulesets contribute to the effective configuration.
-
namePatternsaccepts glob patterns. Common:[main, master, release/*, v*.*.*]. - Pair with ISSUE-505 for per-setting non-compliance (force-push allowed, code-owner approvals missing, etc.).
⚙️ Configuration
This control is configured in .plumber.yaml under the github section:
github:
controls:
branchMustBeProtected:
enabled: trueSee the CLI documentation for the full configuration reference. On Plumber Platform, the same key is used in your policy configuration.