Attack Paths
1. Secret Exfiltration via Workflow Creation
A user with write access (GH_WriteRepoContents) to a repository can:- Create a new branch in the repository
- Push a workflow file (
.github/workflows/*.yml) to that branch withon: pushtrigger - The workflow executes automatically on push
- The workflow can access all repo-level and org-level secrets (GH_HasSecret) available to the repository
- The workflow exfiltrates the secrets (e.g., via HTTP request to an attacker-controlled server)
(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(repo:GH_Repository)-[:GH_HasSecret]->(:GH_RepoSecret|:GH_OrgSecret)
PR reviews do not prevent this attack because they only gate merging, not pushing to new branches. The attacker never needs to merge anything.
2. Supply Chain Attack via Direct Push
A user with write access to a repository can push directly to the default branch (e.g.,main, master), injecting a backdoor into released software.
Graph path: (:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(repo:GH_Repository)
Branch Protection Settings
Setting Dependencies
blocks_creationsrequirespush_restrictionsto betrue. Ifpush_restrictionsisfalse, the GitHub API accepts the mutation but silently revertsblocks_creationstofalse.allows_force_pushesonly controls whether history rewrites are permitted. It does not bypass any access controls.
Merge-Gate vs. Push-Gate Controls
Branch protection settings fall into two distinct categories based on what they control and how they can be bypassed. This distinction is critical because each category has a completely different set of bypass mechanisms, andenforce_admins only affects one category.
Merge-Gate Controls
Govern whether changes can be merged or committed to a protected branch. They enforce code review and read-only policies.
Merge-gate controls are bypassed by
bypass_branch_protection and bypassPullRequestAllowances. They are enforced by enforce_admins.
Push-Gate Controls
Govern who is authorized to push to matching branches. They are an access control layer that restricts push operations to an explicit allowlist.
Push-gate controls are bypassed by
push_protected_branch, admin access, and pushAllowances. They are NOT enforced by enforce_admins.
Bypass Mechanisms
There are seven mechanisms that can bypass branch protection rules. They fall into two categories based on which type of control they bypass.Merge-Gate Bypasses
bypassPullRequestAllowances is narrower than bypass_branch_protection. It only bypasses PR review requirements, not lock branch.
Push-Gate Bypasses
Other Bypasses
Complete Test Results
Test Series 1: New Branch Creation (Secret Exfiltration Path)
Can a user with write access create a new branch and push a workflow?
Conclusion: The only branch protection configuration that blocks the secret exfiltration attack is
push_restrictions + blocks_creations on a * pattern rule.
Test Series 2: Push to Existing Protected Branch (Supply Chain Path)
Can a user with write access push directly tomaster?
Conclusion: Any one of PR reviews, push restrictions (without allowance), or lock branch is sufficient to block direct pushes to an existing protected branch.
Test Series 3: bypass_branch_protection Permission
Conclusion:
bypass_branch_protection bypasses merge-gate controls (PR reviews, lock branch) but NOT push-gate controls (push_restrictions).
Test Series 4: push_protected_branch Permission
Conclusion:
push_protected_branch bypasses push-gate controls (push_restrictions, blocks_creations) but NOT merge-gate controls (PR reviews, lock branch). It is the exact complement of bypass_branch_protection.
Test Series 5: enforce_admins Interaction
Conclusion:
enforce_admins only enforces merge-gate controls. It suppresses bypass_branch_protection but has no effect on push_protected_branch or admin push access.
Test Series 6: allows_force_pushes
Conclusion:
allows_force_pushes is not a bypass mechanism. It only controls whether force pushes (history rewrites) are permitted for users who already have push access.
Test Series 7: Both Permissions Combined
User with bothbypass_branch_protection and push_protected_branch:
Conclusion: Both permissions combined provide full bypass capability, equivalent to admin access.
Test Series 8: bypassPullRequestAllowances (Per-Rule Edge)
User in bypassPullRequestAllowances list (regular write access, no custom role):
Conclusion:
bypassPullRequestAllowances is narrower than the bypass_branch_protection permission. It only bypasses PR review requirements, not lock branch.
Summary Matrix
N/T = Not tested (not applicable to that control type)
Effective Mitigating Controls
For Secret Exfiltration (Write → New Branch → Workflow → Secrets)
The attack requires creating a new branch. This is only blocked when all of the following are true:- A GH_BranchProtectionRule exists with
pattern=* push_restrictions=trueblocks_creations=true
- Users with
push_protected_branchpermission (GH_PushProtectedBranch) - Users with admin access (GH_AdminTo) — cannot be mitigated by
enforce_admins - Users in
pushAllowancesfor the*rule (GH_RestrictionsCanPush) - Users with
edit_repo_protectionspermission (GH_EditRepoProtections) — can remove the rule - Users with both
bypass_branch_protectionandpush_protected_branch
For Supply Chain Attack (Write → Push to Default Branch)
Any one of the following protections is sufficient to block direct pushes to an existing branch:required_pull_request_reviews=truepush_restrictions=true(and attacker not inpushAllowances)lock_branch=true