Branch Protection Rules Reference¶
Detailed configuration reference for GitHub branch protection rules.
Part of Branch Protection Enforcement Patterns
This page provides the detailed rules reference. For overview and architecture, see Branch Protection Enforcement Patterns. For pre-configured templates, see Security Tiers.
Security Foundation
These controls form the baseline security posture. All controls must be implemented for audit compliance.
GitHub enforces the rules. No bypasses. Full audit trail.
The Enforcement Problem¶
Documentation approach:
"All code must be reviewed by at least one other engineer before merging to main."
Provable? No. A developer could merge their own PR. The policy says they shouldn't, but nothing stops them.
Enforcement approach:
GitHub branch protection makes it impossible to merge without approval.
Basic Configuration¶
Pre-Configured Templates Available
Instead of manual configuration, use tier templates: Security Tiers provides Standard, Enhanced, and Maximum tier configurations.
# Enforced via GitHub branch protection rules
branch_protection:
required_pull_request_reviews:
required_approving_review_count: 1
dismiss_stale_reviews: true
require_code_owner_reviews: true
required_status_checks:
strict: true
contexts:
- "ci/tests"
- "security/scan"
enforce_admins: true
required_linear_history: true
Configuration via GitHub UI or API. Once enabled, the rules are immutable until explicitly changed.
Required Reviews¶
Minimum Approvals¶
Cannot merge until at least one other person approves.
Dismiss Stale Reviews¶
New commits invalidate previous approvals. Forces re-review after changes.
Code Owner Reviews¶
Requires approval from designated code owners. Defined in .github/CODEOWNERS:
Ownership-based review ensures domain experts see changes.
Required Status Checks¶
Strict Mode¶
Branch must be up-to-date with base branch. Prevents integration issues.
Required Checks¶
All listed checks must pass before merge. Failed check blocks the PR.
Administrator Enforcement¶
Critical: Applies rules to administrators too.
Without this, org admins can bypass reviews and status checks. Auditors will flag this as a control gap.
Additional Protections¶
Linear History¶
Prevents merge commits. Enforces rebase or squash workflows.
Force Push Protection¶
Blocks force pushes to protected branches. Preserves git history integrity.
Deletion Protection¶
Prevents accidental or malicious branch deletion.
Configuration via API¶
Infrastructure as Code
Terraform and manual UI configuration don't scale. Use GitHub API:
# Get current protection
gh api repos/org/repo/branches/main/protection
# Set protection
gh api \
--method PUT \
repos/org/repo/branches/main/protection \
--input protection-config.json
Example protection-config.json:
{
"required_status_checks": {
"strict": true,
"contexts": ["ci/tests", "security/scan"]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true,
"require_code_owner_reviews": true
},
"restrictions": null,
"required_linear_history": true,
"allow_force_pushes": false,
"allow_deletions": false
}
Audit Trail¶
GitHub provides automatic audit trail:
# Get PR review history for March 2025
gh api \
'repos/org/repo/pulls?state=closed&base=main' \
--jq '.[] | select(.merged_at | startswith("2025-03")) |
{number, title, reviews: .requested_reviewers | length,
merged: .merged_at}'
Output proves:
- PR numbers
- Review counts
- Merge timestamps
- Reviewer identities
Auditors can verify controls were active during any historical period.
Exception Handling¶
Formalized Bypass Controls
For production systems, implement formal bypass procedures: Bypass Controls and Emergency Access provide approval workflows and audit patterns.
Emergency Bypass¶
Sometimes you need to bypass protection (production outage, security hotfix).
Pattern: Temporary disable via API, re-enable immediately after merge.
# Disable protection
gh api --method DELETE repos/org/repo/branches/main/protection
# Merge emergency fix
git push origin hotfix
# Re-enable protection
gh api --method PUT repos/org/repo/branches/main/protection \
--input protection-config.json
Critical: Log every exception. Include:
- Timestamp
- Who requested bypass
- Reason (ticket reference)
- Duration
- Post-merge review confirmation
Auditors accept documented exceptions. They don't accept casual bypasses.
Multi-Repository Enforcement¶
Advanced Patterns Available
For comprehensive organization-wide enforcement, see Multi-Repo Management and GitHub App Enforcement.
Applying protection to 100+ repositories manually doesn't scale.
GitHub Actions Workflow¶
name: Enforce Branch Protection
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
jobs:
enforce:
runs-on: ubuntu-latest
steps:
- name: Get repositories
id: repos
run: |
gh api orgs/my-org/repos --paginate --jq '.[].name' > repos.txt
- name: Apply protection
run: |
while read repo; do
gh api --method PUT \
repos/my-org/$repo/branches/main/protection \
--input protection-config.json || echo "Failed: $repo"
done < repos.txt
Weekly enforcement ensures new repositories inherit protection.
Verification Script¶
Enhanced Verification
For comprehensive audit preparation and continuous monitoring, see Verification Scripts.
Audit preparation: Verify protection across all repositories.
#!/bin/bash
# verify-branch-protection.sh
REPOS=$(gh api orgs/my-org/repos --paginate --jq '.[].name')
for repo in $REPOS; do
PROTECTION=$(gh api repos/my-org/$repo/branches/main/protection 2>/dev/null)
if [ -z "$PROTECTION" ]; then
echo "❌ $repo: NO PROTECTION"
else
ENFORCE_ADMINS=$(echo "$PROTECTION" | jq -r '.enforce_admins.enabled')
REQ_REVIEWS=$(echo "$PROTECTION" | jq -r '.required_pull_request_reviews.required_approving_review_count')
if [ "$ENFORCE_ADMINS" = "true" ] && [ "$REQ_REVIEWS" -ge "1" ]; then
echo "✅ $repo: Protected"
else
echo "⚠️ $repo: Weak protection"
fi
fi
done
Output shows compliance status per repository.
Common Pitfalls¶
Pitfall 1: Forgetting enforce_admins¶
Administrators can bypass all rules. Auditors will test this.
Pitfall 2: No Status Check Requirement¶
Reviews without CI checks allow broken code to merge.
Pitfall 3: Undocumented Exceptions¶
Emergency bypasses are acceptable. Undocumented bypasses are findings.
Pitfall 4: Inconsistent Enforcement¶
Protection on main but not production. Apply to all release branches.
Integration with Status Checks¶
Branch protection works best with required status checks.
See Required Status Checks for full CI/CD integration.
Related Patterns¶
Branch Protection: Overview · Security Tiers · Multi-Repo · GitHub App Enforcement · Drift Detection · Bypass Controls · Emergency Access · Verification Scripts
Audit & Compliance: Audit Evidence · Compliance Reporting
Related Controls: Commit Signing · Required Status Checks · GitHub Apps
Policies became impossible to bypass. Auditors queried the API. Evidence was irrefutable. Controls passed.