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
This is a settings change in the GitHub web UI, not a code or .plumber.yaml change. The .plumber.yaml config only tells Plumber which branches to check. Protect the branch through either mechanism: a Repository Ruleset (recommended for new setups) or classic Branch Protection. Plumber reads both and merges them, so a rule defined in one is enough to satisfy the policy.
- 1
Open Settings > Rules > Rulesets and click New branch ruleset.
Rulesets are the modern mechanism and are recommended here. Prefer the classic UI instead? Use Settings > Branches > Add branch ruleset, where the settings below map one-to-one.
- 2
Name the ruleset and set Enforcement status to Active.
Disabled and evaluate-mode rulesets are ignored by Plumber; only an active ruleset counts as protection.
- 3
Under Target branches, add every branch this policy covers.
Match the branches in your
namePatterns(for examplemainandrelease/*). Use Include by pattern for wildcards likerelease/*. - 4
Enable Restrict deletions and Block force pushes.
This stops the branch from being deleted or having its history rewritten.
- 5
Enable Require a pull request before merging.
Set Required approvals to at least 1, tick Dismiss stale pull request approvals when new commits are pushed, and, if you use a
CODEOWNERSfile, Require review from Code Owners. - 6
Optionally enable Require status checks to pass and add your checks.
For example
build,test, andcodeql, so failing pipelines can't be merged. - 7
Click Create to save the ruleset.
Re-run Plumber to confirm the finding clears. The branch is now protected and the policy is satisfied.
๐ก 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 violations (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.