Ineffective GitHub ruleset

ID

github_rulesets

Severity

high

Remediation Complexity

moderate

Remediation Risk

low

Remediation Effort

low

Family

SCM

Tags

ASVS50:v15.1.1, branch-protection, cicd-sec-01, cicd-security, security, source-code, spvs10-v3.3.18, spvs15-v2.2.1, supply-chain

Description

Are the GitHub rulesets protecting this repository actually effective?

Rulesets are the modern GitHub-native way to protect branches, tags and pushes. Unlike classic branch protection, a ruleset can be created, scoped and left in a state where it provides no real protection. This check inspects the repository-level and organization-level rulesets that apply to the repository and reports two weaknesses:

  • Not enforced — a ruleset that carries protection rules but whose enforcement status is evaluate (insights only) or disabled. It looks like protection but enforces nothing.

  • Bypassable — an active ruleset that privileged actors (organization admins, teams or repository roles) can bypass unconditionally (bypass mode = always), which defeats the control.

The presence of branch protection itself is reported by the unprotected_branch detector. This detector focuses on the quality of existing rulesets, so the two do not raise duplicate findings. Automation-only bypass actors (Integration, DeployKey) are not flagged, and pull-request-scoped bypass (bypass mode = pull_request) is treated as an accepted, weaker exception rather than an unconditional bypass.

Security

A ruleset only protects a ref if it is enforced and cannot be trivially circumvented.

  • Unenforced rulesets give a false sense of security: reviewers assume required reviews, signed commits or force-push protection are in place, while the ruleset is in evaluate/disabled mode and enforces nothing.

  • Unconditional bypass by privileged actors is the class of weakness behind tag- and branch-poisoning supply-chain incidents: if an organization admin, a team or a repository role can push to a protected ref or move a release tag without going through the required checks, an attacker who compromises such an identity (or a token with that role) can tamper with the code or the release artifacts despite the ruleset.

Rulesets support layering (repository and organization level) and bypass lists. A bypass list is legitimate for automation (a signing bot, a deploy key), but granting unconditional bypass to human roles such as OrganizationAdmin re-introduces exactly the risk the ruleset was meant to remove. Prefer pull_request-scoped bypass, or no bypass at all, for human actors.

Mitigation / Fix

  • Set the enforcement status of protective rulesets to Active.

  • Remove privileged human actors (OrganizationAdmin, Team, RepositoryRole) from the bypass list, or restrict their bypass to pull requests only.

  • Keep bypass entries limited to the automation identities that genuinely need them (Integration, DeployKey).

Follow the GitHub documentation to manage rulesets for a repository and to configure bypass permissions.

Configuration

To change these options you can modify SCANNER_DIR/conf/misconfigurations/github_rulesets.yml. The following are the default configuration properties:

# Configuration for the checks on GitHub rulesets.
properties:
  # If true, report rulesets that carry protection rules but are not actively enforced
  # (enforcement is 'evaluate' or 'disabled'), because they provide no real protection.
  requireActiveEnforcement: true
  # If true, report active rulesets that privileged actors can bypass unconditionally
  # (bypass_mode = always), which defeats the control.
  disallowPrivilegedBypass: true
  # Actor types trusted to bypass rulesets (typically automation). Any OTHER actor type with an
  # 'always' bypass is flagged. Narrow this list (e.g. to []) to also flag GitHub App / DeployKey bypass.
  trustedBypassActorTypes: [ Integration, DeployKey ]