Owning GitHub Secret Protection does not mean pushes are being blocked: repository push protection must be switched on, and once on, every developer with write access can bypass it unless you restrict that. Repository push protection is off by default, so a licence on its own blocks nothing. This runbook shows how to switch it on properly: at repository level for a pilot, through organisation or enterprise security configurations for scale, and through the REST API for automation. It then covers how to restrict bypass with delegated bypass, how to prove coverage and monitor bypasses through the audit log and alert API, and how to roll back without losing evidence. It is for GitHub organisation owners, security managers and platform engineers. For how push protection compares across GitHub, GitLab and Azure DevOps, see the Push Protection Guide.
This is an independent guide from NHI Mgmt Group, based on GitHub Docs pages read on 11 November 2026. GitHub changes menu labels often, so check the current wording if a path below has moved.
Key takeaways
- Licence is not enforcement. Repository push protection needs GitHub Secret Protection and is off by default; it must be enabled per repository or, better, through a security configuration.
- Configurations beat clicking. An organisation security configuration, attached to all repositories and set as the default for new ones, keeps coverage from drifting as repositories are created.
- Restrict bypass before you announce it. By default anyone with write access can bypass by choosing a reason; delegated bypass turns that into a reviewed request.
- Exemptions are a hole, not a convenience. An exempt actor is never blocked, so keep the exempt list to named automation accounts and review it monthly.
- Measure with the API, not the settings page. The repository and alert APIs show which repositories have
secret_scanning_push_protectionenabled and which alerts came from bypasses (is_bypassed=true).
When you need this
Run this procedure when any of the following is true:
- New licence: the organisation has just bought GitHub Secret Protection (or GitHub Advanced Security) and needs coverage switched on.
- Audit finding: an assessment found credentials in repository history and asked for a preventive control. NIST SP 800-53 IA-5(7) (No Embedded Unencrypted Static Authenticators) is the control most auditors cite.
- Leak response: after a leaked key, as part of lessons learned. The Toyota T-Connect Key Exposure is a reminder that a key pushed to a public repository can sit there for years before anyone notices.
- Coverage drift: push protection is on for some repositories but new repositories are created without it, or teams have switched it off.
Push protection prevents new exposure. It does not find secrets already in history: run history scanning alongside it, as described in the Git Secret Scanning Guide.
Before you start
| Prerequisite | Detail | Why it matters |
|---|---|---|
| Licence | GitHub Secret Protection available for the repositories in scope | Repository push protection is a Secret Protection feature; without it the setting is not offered for private repositories |
| Role | Repository admin, organisation owner, security manager, or enterprise owner | These are the roles GitHub Docs lists as able to enable push protection |
| API token (automation) | Fine-grained token with Administration (write) on the repositories or organisation; classic token with write:org for configuration endpoints | The Update a repository and security configuration endpoints require these permissions |
| Baseline | Free secret leak report (GitHub Team and Enterprise organisations) or current alert counts | Gives a before figure for how many pushes would have been blocked |
| Bypass reviewers | A named team (for example a security champions team) that will review bypass requests | Delegated bypass needs reviewers who answer quickly, or developers will be blocked |
| Automation inventory | List of bots and service accounts that push commits (release tools, migration accounts, code generators) | Decides which actors, if any, need exemption |
Treat the token you use for this work as a privileged non-human credential: short expiry, scoped to the organisation, revoked when the rollout ends. See the GitHub Personal Access Tokens Security Guide.
Step-by-step
Phase 1: pilot on a few repositories
- Pick three to five pilot repositories with active developers, including one with heavy automation and one with test fixtures that look like keys.
- Enable in the UI: open the repository, click Settings, then in the Security and quality section of the sidebar click Advanced Security. If Secret Protection is not enabled, click Enable next to Secret Protection, then click Enable next to Push protection.
- Or enable through the API: send
PATCH /repos/{owner}/{repo}with a body that setssecurity_and_analysis.secret_scanning.statusandsecurity_and_analysis.secret_scanning_push_protection.statustoenabled. Record the previous values fromGET /repos/{owner}/{repo}first, so rollback restores the exact state. - Test with a known pattern: push a commit containing a revoked or provider-documented test token from a scratch branch and confirm the push is rejected. The
remote:output names the secret type, the commit and the file path and line, and gives a URL for bypass. - Collect developer feedback for one to two weeks: how often were pushes blocked, how long did removal take, and were any blocks false positives.
Phase 2: restrict bypass
- Decide the bypass model using the decision table below. For most organisations: delegated bypass with a reviewer team, and no exemptions except named automation.
- Repository level (pilot): in Settings > Advanced Security, under Push protection, set Who can bypass push protection for secret scanning to Specific roles or teams, then under Bypass list click Add role or team and Add selected. Secret teams cannot be added.
- Give reviewers a narrow role: create a custom organisation role with the Review and manage secret scanning bypass requests permission and assign it to the reviewer team, rather than making reviewers organisation owners.
- Exempt only named automation: click Exempt next to an entry only for an account that legitimately writes key-like material, such as a repository migration bot. GitHub's own documentation warns that exemptions may lead to leaked secrets.
Phase 3: scale with a security configuration
- Create a custom configuration: organisation Settings, then in the Security section open Advanced Security and click Configurations. Create a custom security configuration with secret scanning enabled and push protection Enabled. Under Push protection, set Bypass privileges to Specific actors and add the reviewer team. Click Save configuration.
- The API equivalent is
POST /orgs/{org}/code-security/configurationswithsecret_protection,secret_scanningandsecret_scanning_push_protectionset toenabled,secret_scanning_delegated_bypassset toenabled, andsecret_scanning_delegated_bypass_options.reviewerslisting each reviewer withreviewer_id,reviewer_type(TEAMorROLE) andmode(ALWAYS, orEXEMPTfor exemption). Setenforcementtoenforcedso repository admins cannot change the settings the configuration controls. - Attach in rings:
POST /orgs/{org}/code-security/configurations/{configuration_id}/attachwithscopeset toselectedand a list ofselected_repository_idsfor the first ring, thenpublic, thenall_without_configurations(repositories with no configuration yet), and finallyallonce you are sure no team relies on a different configuration. - Cover new repositories:
PUT /orgs/{org}/code-security/configurations/{configuration_id}/defaultswithdefault_for_new_reposset toall, orprivate_and_internalif public repositories use a separate configuration. - Enterprise: repeat the pattern from the enterprise Settings > Advanced Security > Configurations, or
POST /enterprises/{enterprise}/code-security/configurations/{configuration_id}/attach. A configuration set at organisation or enterprise level disables the repository-level bypass settings, which is the point: teams cannot loosen it locally.
Phase 4: extend what is blocked
- Custom patterns: for internal token formats, define a custom pattern at organisation or enterprise level, test it against existing history, then opt it into push protection. Patterns with a fixed, unique prefix are far easier to block without false positives.
- Non-provider (generic) patterns: enable
secret_scanning_non_provider_patternsin the configuration to detect structured secrets such as private keys and connection strings, measure false positives from the alerts for a few weeks, then decide whether to enforce them at push. - Developer accounts: remind developers that push protection for users (profile Settings > Code security > Push protection for yourself) is on by default for public repositories and should stay on, because it covers personal and open source pushes the organisation does not control.
Choosing the bypass model
| Model | How it is set | Developer experience | Evidence | Use when |
|---|---|---|---|---|
| Anyone with write access bypasses with a reason | Default | No waiting; reason chosen on the bypass URL, push retried within three hours | Alert (closed for "used in tests" and "false positive", open for "I'll fix it later"), audit log entry, email to owners and watchers | Short first pilot only, or small teams with a security engineer reading every alert |
| Delegated bypass | Specific roles or teams on the bypass list; others submit requests | Developer submits a request with a comment and waits for an email decision | Request, approval or denial and completion events in the audit log | Most organisations, and any regulated code base |
| Delegated bypass plus exemptions | Reviewer mode EXEMPT for named actors | Exempt actors are never blocked | None at push time for exempt actors | Only for automation that legitimately commits key-like files, with compensating review |
Small organisations (one security person, a few dozen repositories) can start with the default model and read every bypass alert, then move to delegated bypass once request volume is known. Large organisations should not enable at scale without delegated bypass and a reviewer rota with a response target, because unanswered requests block releases and push teams to ask for exemptions. Approved bypass requests also allow future commits with the same secret, so reviewers should approve only when the value is genuinely harmless.
Verify it worked
- Coverage: for each repository,
GET /repos/{owner}/{repo}and readsecurity_and_analysis.secret_scanning_push_protection.status. Any repository notenabledis a gap; check whether it is archived, unlicensed or attached to a different configuration. - Blocking: repeat the test push in one repository per ring and confirm the rejection, including through the web editor and a file upload, since both are covered entry points.
- Bypass alerts:
GET /orgs/{org}/secret-scanning/alerts?is_bypassed=truereturns alerts created by a bypass, withpush_protection_bypassed_by,push_protection_bypassed_atand, for delegated bypass,push_protection_bypass_request_reviewer. Combine withstate=openandvalidity=activeto find live credentials that someone chose to push. - Audit log: confirm the events below appear for your test actions, then build alerts on them in your SIEM. See the GitHub Audit Log Detection Guide.
| Audit log action | Meaning | Response |
|---|---|---|
secret_scanning_push_protection.bypass | A user bypassed push protection for a detected secret | Check the reason; treat "I'll fix it later" as exposure and rotate |
secret_scanning_push_protection_request.request / .approve / .deny | Delegated bypass request lifecycle | Track time to decision; sample approvals weekly |
secret_scanning_push_protection_request.complete | Commit containing a secret with an approved request was pushed | Confirm the value matches what was approved |
repository_secret_scanning_push_protection.disable | Push protection switched off for a repository | High priority: who, why, and was it re-enabled |
repository_secret_scanning_push_protection_bypass_list.disable | Repository bypass changed back to anyone with write access | Investigate; should not occur under an enforced configuration |
security_configuration.update, security_configuration_default.update | Configuration or default for new repositories changed | Change-controlled only; alert on any change outside a ticket |
Metrics that show it is working:
- Coverage: percentage of active repositories with push protection enabled (target 100% of licensed repositories).
- Blocked pushes: pushes blocked per month, which should rise first and then fall as developers adapt.
- Bypass rate: bypasses as a share of blocks, split by reason; a high "used in tests" share deserves sampling.
- Open bypass alerts: count of open alerts with
is_bypassed=trueand their age; the target is zero older than the rotation SLA. - Request turnaround: median time from bypass request to decision under delegated bypass.
- Exempt actors: number of exempt accounts, which should stay small and stable.
Rollback and common errors
Rollback: if push protection blocks a release-critical pipeline, do not disable it organisation-wide. In order of preference: approve a delegated bypass request for the specific value; detach only the affected repository from the configuration and set it back to its previous state with PATCH /repos/{owner}/{repo} using the values you recorded in Phase 1; as a last resort set secret_scanning_push_protection to disabled in the configuration. Disabling does not delete existing alerts or audit events, so evidence is preserved. Re-enable within an agreed window and record the exception.
- Setting not visible: the repository is not covered by a Secret Protection licence, or you lack the admin or security manager role.
- Repository settings greyed out: an organisation or enterprise configuration is enforced. Change the configuration, not the repository.
- Bypass URL returns 404: only the user who made the push can use it. Another engineer cannot bypass on their behalf.
- Bypass expired: the push must be retried within three hours of allowing it; after that the developer repeats the process.
- Push still blocked after removing the secret: it remains in an earlier commit in the push. Remove it from every commit, using
git commit --amendfor the latest commit or an interactive rebase from the earliest affected commit. - Integration fails with errors after rollout: REST API writes that contain a detected secret are blocked too; fix the integration rather than exempting its account.
Common failure patterns
- Enabled at repository level only: new repositories arrive unprotected because no default configuration was set. Set
default_for_new_repos. - Bypass by reason left as the permanent model: "It's used in tests" closes the alert, so real credentials mislabelled as test data disappear from the open queue.
- Exemptions for convenience: a release bot is exempted because it was blocked once; if its token is stolen, it can push anything. Recent supply chain worms that abused stolen developer tokens, such as the Shai-Hulud npm Worm, show why trusted automation identities need the same controls as people.
- No owner for bypass alerts: alerts open with "I'll fix it later" and nobody rotates. Route them to the team that owns rotation, with the Leaked Credential Response Playbook as the runbook.
- Assuming passwords are blocked: push protection blocks patterns it can identify with confidence. Passwords and random internal secrets need custom patterns with prefixes, or post-push scanning.
Framework mapping
| Framework | Reference | How this procedure supports it |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5(7) No Embedded Unencrypted Static Authenticators | Blocks static credentials from entering source code |
| NIST SP 800-53 Rev 5 | SA-11 Developer Testing and Evaluation; AU-6 Audit Record Review, Analysis, and Reporting | Automated check at commit time; review of bypass and configuration events |
| NIST CSF 2.0 | PR.PS-06; PR.AA-01 | Secure development practice monitored across the SDLC; managed credentials for services |
| CIS Controls v8.1 | Control 16 Application Software Security | Secure development process and tooling for in-house code |
| ISO/IEC 27001:2022 Annex A | A.8.28 Secure coding; A.5.17 Authentication information | Secure coding control with evidence; protection of authentication secrets |
| MITRE ATT&CK | T1552.001 Unsecured Credentials: Credentials In Files | Removes a common source of credentials attackers harvest from repositories |
Hardening afterwards
- Prefer credentials that cannot leak usefully: replace long-lived keys in workflows with OIDC federation. See the GitHub Actions OIDC Federation Hardening Guide.
- Run history scanning: push protection does nothing for secrets already committed; triage those with the Developer Secret Scanning Triage process.
- Add a client-side layer: a pre-commit hook keeps secrets out of local history, which saves developers the rebase that a server block forces.
- Review quarterly: bypass list membership, exempt actors, custom patterns enrolled in push protection, and the configuration enforcement setting.
Practitioner checklist
- Confirm Secret Protection licensing and the roles that will make changes.
- Record current
security_and_analysisvalues for every repository before changing anything. - Pilot on three to five repositories and test blocking through the CLI, web editor and file upload.
- Enable delegated bypass with a reviewer team holding a custom role, not owner rights.
- Create an enforced organisation security configuration and attach it in rings.
- Set the configuration as default for new repositories.
- Keep exemptions to named automation accounts and review them monthly.
- Alert on push protection disable, bypass and configuration change events.
- Track coverage, bypass rate, open bypass alerts and request turnaround monthly.
- Enrol prefixed internal token formats as custom patterns with push protection.
Standards and references
- GitHub Docs: Enabling push protection for your repository
- GitHub Docs: About push protection
- GitHub Docs: Enabling delegated bypass for push protection
- GitHub Docs: REST API endpoints for code security configurations
- GitHub Docs: REST API, Update a repository
- GitHub Docs: REST API endpoints for secret scanning
- GitHub Docs: Audit log events for your organization
- GitHub Docs: Working with push protection from the command line
Related NHI Mgmt Group resources: Push Protection Guide · Git Secret Scanning Guide · GitHub Organisation Security Checklist · Secret Scanning Programme Rollout Plan Template · OWASP NHI Top 10 NHI2 Secret Leakage Guide