Repository default branch denies force pushes
GitHub repository default branch blocks force pushes through branch protection.
Evaluates whether the default branch permits force pushes. This protection can be enforced through classic branch protection or repository rulesets.
Risk
Allowing force pushes on the default branch erodes integrity and auditability by enabling history rewrites and deletion of commits. Attackers or insiders can inject unreviewed code, bypass reviews and status checks, and corrupt PRs, risking supply-chain compromise and reduced availability.
Run this check with Prowler
Run in Prowler Cloudprowler github --checks repository_default_branch_disallows_force_pushRecommendation
Disable Allow force pushes on the default branch. Enforce PR-based changes with required reviews and status checks, require signed commits and linear history, and restrict bypass to minimal actors. Apply protections to admins too to uphold least privilege and defense in depth.
Remediation
Terraform
resource "github_branch_protection_v3" "<example_resource_name>" {
repository_id = "<example_resource_id>"
pattern = "<default_branch_name>"
allows_force_pushes = false # Critical: disallows force pushes on the default branch
}Other
Check Code
from typing import List
from prowler.lib.check.models import Check, CheckReportGithub
from prowler.providers.github.services.repository.repository_client import (
repository_client,
)
class repository_default_branch_disallows_force_push(Check):
"""Check if a repository denies force push
This class verifies whether each repository denies force push.
"""
def execute(self) -> List[CheckReportGithub]:
"""Execute the Github Repository Denies Force Push check
Iterates over all repositories and checks if they deny force push.
Returns:
List[CheckReportGithub]: A list of reports for each repository
"""
findings = []
for repo in repository_client.repositories.values():
if repo.default_branch.allow_force_pushes is not None:
report = CheckReportGithub(metadata=self.metadata(), resource=repo)
report.status = "FAIL"
report.status_extended = f"Repository {repo.name} does allow force pushes on default branch ({repo.default_branch.name})."
if (
repo.default_branch.allow_force_pushes_source
== "ruleset_not_active"
):
report.status_extended = f"Repository {repo.name} has force pushes disallowed in a ruleset on default branch ({repo.default_branch.name}), but the ruleset is not active."
if not repo.default_branch.allow_force_pushes:
report.status = "PASS"
report.status_extended = f"Repository {repo.name} does deny force pushes on default branch ({repo.default_branch.name})."
findings.append(report)
return findingsReferences
- https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule
- https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#allow-force-pushes
- https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets