Branch/ref protection is not enforced or is bypassable

ID

ref_protection_bypass

Severity

high

Remediation Complexity

medium

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, supply-chain

Description

Is the branch/ref protection not only present, but effective?

A protection rule that looks configured but can be trivially skipped provides a false sense of security. This check reports two weaknesses in existing protection, on the platforms whose API exposes the primitive:

  • not enforced — the protection exists but is in an advisory/disabled state, so it blocks nothing.

  • bypassable — the protection is active but privileged or broad actors can bypass it (or remove it) unconditionally.

This is the cross-platform counterpart of github_rulesets (which covers GitHub). Presence and strength of protection (does the branch have protection at all, force-push/delete disabled, reviews required) remain owned by unprotected_branch — this detector is deliberately scoped to the enforcement/bypass quality to avoid duplicate findings. A "not enforced" finding is reported one severity below a "bypassable" one, so it never outranks the underlying unprotected_branch finding.

Insufficient data (the scan token lacks the scope to read the protection detail, or the access lists are empty) yields no finding — the detector never guesses.

Platform support

Platform What is evaluated Status

GitLab / GitLab Enterprise

Protected branches: allow_force_push (history can be rewritten); push/merge access that is broad (developer role, or explicit user/group grants); who may unprotect the branch (strip protection entirely)

Supported

Azure DevOps

Branch policies: a policy that is disabled or advisory (isBlocking=false) does not block = not enforced; a minimum-reviewers policy with creatorVoteCounts lets authors approve their own PR = bypass

Supported

Bitbucket Cloud

Branch restrictions that list exempt users/groups — those actors are exempted from the restriction and can bypass it. Bitbucket has no on/off enforcement state, so "not enforced" is N/A. (Bitbucket Server / Data Center uses a different API and is out of scope.)

Supported

GitHub / GitHub Enterprise

Covered by the github_rulesets detector (ruleset enforcement + unconditional bypass)

Covered elsewhere

Impact context

Branch/ref protection is the control that stops unreviewed or malicious changes from reaching a protected branch (CICD-SEC-1, SPVS V3.3.18). If the rule is not enforced, or a broad set of actors can push around it or remove it, the control is theatre: an attacker with ordinary developer access — or a compromised token — can rewrite history, merge without review, or simply unprotect the branch and then do as they please.

Example — FAIL (GitLab)
Protected branch "main"
  allow_force_push: true
  push_access_levels: [ { access_level: 30 } ]     # developers
  unprotect_access_levels: [ { access_level: 40 } ] # maintainers (non-admin)
  -> force-push allowed (history rewritable)
  -> developers can push directly to main
  -> non-admins can unprotect main and strip all protection
Example — PASS (GitLab)
Protected branch "main"
  allow_force_push: false
  push_access_levels: [ { access_level: 0 } ]      # no direct push (MR only)
  merge_access_levels: [ { access_level: 40 } ]    # maintainers
  unprotect_access_levels: [ { access_level: 60 } ] # admins only
  -> no finding

Security

Enforce the protection and keep its bypass surface minimal: disallow force-push on protected branches, require merge requests / pull requests rather than direct pushes by broad roles, and restrict who can unprotect a branch (or bypass policies) to administrators. A protection rule is only as strong as the smallest, most-trusted set of actors that can go around it.

Mitigation / Fix

GitLab — in Settings → Repository → Protected branches:

  • Turn off "Allowed to force push".

  • Set "Allowed to push and merge" / "Allowed to merge" to Maintainers (avoid Developers or explicit broad user/group grants).

  • Restrict "Allowed to unprotect" to administrators/owners.

Azure DevOps — in Project settings → Repositories → Policies (branch policies):

  • Set protection policies to Required (blocking) and enabled rather than optional/disabled.

  • On "Minimum number of reviewers", turn off "Allow requestors to approve their own changes".

Bitbucket Cloud — in Repository settings → Branch restrictions:

  • Avoid adding exempt users/groups to a restriction; if an exemption is unavoidable, keep the list minimal and periodically reviewed.

Configuration

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

properties:
  # GitLab: report protected branches that allow force-push.
  flagForcePush: true
  # GitLab: report protected branches with broad push/merge access (developer role or explicit user/group).
  flagWeakPushAccess: true
  # GitLab: report protected branches that non-admins (or explicit users/groups) can unprotect.
  flagUnprotectBypass: true
  # Azure DevOps: report branch policies that are disabled or advisory (non-blocking).
  flagPolicyNotEnforced: true
  # Azure DevOps: report minimum-reviewers policies that allow authors to approve their own PR.
  flagSelfApproval: true
  # Bitbucket: report branch restrictions that have exempt users/groups.
  flagExemptActors: true