A GitHub secret scanning alert means a working credential may already be in someone else's hands, yet many alerts are closed by deleting the line of code, which removes nothing. GitHub's own position is that once a secret has been committed you should consider it compromised, and that secret scanning does not close an alert when the secret is removed from the repository. This guide is the runbook for one alert: read what GitHub tells you about it, decide urgency, contain the credential with its provider, check what it was used for, and close the alert with the right resolution and evidence. It is for repository administrators, security engineers and the developers who own the leaked credential. The wider triage process across many alerts and tools is in Developer Secret Scanning Triage.
This is an independent guide from NHI Mgmt Group, based on GitHub Docs as of November 2026. It is not sponsored or reviewed by GitHub. Several features named here (on-demand metadata, report leak) are in public preview and may change.
Key takeaways
- Removing the commit is not remediation. The secret stays in history, forks and clones, and the alert stays open; only revoking or rotating at the provider ends the exposure.
- Read the alert's labels first. Validity
active, apublic leaklabel or amulti-repolabel each raise urgency and change who you need to involve. - Rotate before you report or rewrite. GitHub advises rotating first because reporting a leak may get the secret revoked, and rewriting history is a hygiene step that comes last.
- Check usage from first commit to revocation. An
inactivesecret can still have been used while it was live; review the provider's logs either way. - Close with the true reason and a comment. The resolution and comment become the audit record, and the API does not let you edit the comment afterwards.
When you need this
| Alert type | Where it appears | Your action |
|---|---|---|
| User alert | Repository Security and quality > Secret scanning (generic and AI-detected patterns are listed separately) | Follow this runbook |
| Push protection alert | Same list, created when a contributor bypasses repository-level push protection; filter with bypassed: true | Follow this runbook and review the bypass reason with the contributor |
| Partner alert | Sent to the secret's provider for public repositories and npm packages, not to repository admins | GitHub says no action is needed for this type, but expect the provider to revoke the secret; check for breakage |
Bypassing user-level push protection does not create an alert, so it is not covered by this list. Setting up push protection itself is covered in How to enable GitHub secret scanning push protection.
Before you start
- Access: viewing and closing alerts needs repository admin, organisation owner or security manager rights. For the REST API, a fine-grained token needs the "Secret scanning alerts" repository permission (read to list, write to update); a classic token needs
repoorsecurity_events. - Owner of the credential: identify who can revoke it at the provider. For a cloud key that is the account owner, not the developer who committed it.
- Do not copy the secret around: work from the alert view and the provider console. Pasting the value into tickets or chat creates new leaks; the API's
hide_secretparameter keeps the value out of list output. - Clock: note the alert creation time and the commit date of the first location; usage review covers that whole window.
Step-by-step
Steps 1 to 3 are read-only. Step 4 changes the credential at its provider. Step 6 changes the alert. Step 7 is optional and disruptive.
- Open the alert and read its signals. In the repository go to Security and quality > Secret scanning and open the alert, or fetch it:
Record the secret type, every location (file, commit, and whether it also appears in issues, pull requests or wikis), the validity, and labels. The table below sets urgency.GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations - Decide urgency.
For GitHub tokens that are active, GitHub can show token metadata (public preview): secret owner, creation and expiry dates, last used date and whether it has organisation access. For a small set of third-party types (currently OpenAI, Google OAuth, Slack and Replicate) extended metadata such as owner and issue date may be shown. Use these to find the owner fast.
Signal Meaning (GitHub Docs) Response target Validity activeGitHub checked with the provider and the secret works Contain within hours Validity unknownPossibly active: validation not supported for the type or could not be verified Treat as active until the owner confirms Validity inactiveSecret no longer works Still review usage for the period it was live public leaklabelAlso found in GitHub's public scans of code, discussions, gists, issues, pull requests or wikis Highest urgency; GitHub notes this may need more urgent or different remediation multi-repolabelSame secret found in several repositories in the organisation or enterprise One rotation, many alerts: coordinate and close them together Bypassed push protection A contributor pushed despite the block Confirm the bypass reason was true - Confirm it is a real secret. A string that is genuinely a test fixture, a revoked sample or a false match should be closed with the matching reason in step 6, with evidence. If in doubt, it is real.
- Contain at the provider: revoke or rotate. Create the replacement, update the services that use it, then revoke the leaked one. OWASP's Secrets Management Cheat Sheet orders the response as revoke, rotate, delete and log, with rapid containment as the goal. For credentials that production depends on, a short overlap avoids an outage; for an active secret with a public leak, revoke first and accept the outage. Provider runbooks: How to revoke a leaked GitHub personal access token and check what it touched, How to rotate an AWS IAM access key without downtime, How to rotate Entra app secrets without downtime and How to rotate database credentials without downtime. For a GitHub personal access token in a private repository that you cannot reach the owner of, the alert offers Report leak (public preview), after which GitHub treats it like a publicly leaked secret and revokes it.
- Review what the secret did. Check the provider's security or audit logs from the first commit date to the revocation time for calls from unexpected addresses, user agents or regions. GitHub's guidance is to check logs even when the secret is inactive. For GitHub tokens, the organisation audit log is the source; see the GitHub Audit Log Detection Guide. If you find misuse, this is now an incident: hand over to the Leaked Credential Response Playbook. The Home Depot Token Exposure 2025 shows what a long window between exposure and revocation looks like.
- Close the alert with the right resolution. In the alert, open Close as, choose the reason, add a comment and select Close alert. Through the API:
The comment is stored on the alert timeline and cannot be updated or deleted afterwards, so write it once, completely.PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} {"state": "resolved", "resolution": "revoked", "resolution_comment": "Revoked at provider 2026-11-11 10:42 UTC; logs reviewed, no misuse; ticket SEC-1234"} - Clean the history only if it is still needed. GitHub's guidance is that revoking or rotating may solve the problem without rewriting history. If you must remove the value (for example a secret you cannot rotate), use
git-filter-repo2.47 or later with--sensitive-data-removal, and expect side effects: changed commit hashes, removed signatures, broken diffs on closed pull requests, and recontamination from old clones. Cached views and pull request references need GitHub Support, and forks need their owners. See How to Remove Secrets From Git History.
Containment by secret type
| Secret type in the alert | Who revokes | Containment notes | Runbook |
|---|---|---|---|
| GitHub personal access token | The token owner, or GitHub through Report leak for private repositories | Token metadata shows owner and organisation access while the token is active | Leaked GitHub token |
| Cloud access key | Cloud account administrator | Deactivate first if production depends on it, then delete; review the cloud audit trail by key ID | Cloud Access Key Leak Playbook |
| Entra or other app client secret | App registration owner | Remove the credential by its key ID; check service principal sign-ins | Rotate Entra app secrets |
| Database connection string | DBA | Change the password and end existing sessions; a password change alone does not disconnect them | Rotate database credentials |
| SaaS API token | SaaS tenant administrator | Many SaaS tokens cannot be scoped down after issue; replace with a narrower token | Work SaaS API tokens |
| Private key (TLS, SSH) | Certificate or host owner | Revoke or remove the public key everywhere it is trusted | Leaked TLS Private Key Response |
Choosing the resolution
| API resolution | Use when | Evidence to put in the comment |
|---|---|---|
revoked | The credential has been revoked or rotated at the provider | Revocation time, who did it, log review result, ticket |
false_positive | The match is not a credential | Why it is not a secret; consider a custom pattern or exclusion fix |
used_in_tests | A deliberate test credential with no access to anything real | Where it is used, proof it grants no access |
wont_fix | A real secret you have accepted the risk for | Risk owner, reason, expiry of the acceptance; should be rare |
The API also reports pattern_edited and pattern_deleted for custom pattern alerts, but you cannot set those yourself. Closing a real, live secret as false_positive or used_in_tests to clear a dashboard is the most damaging shortcut in this process.
Evidence to preserve
If the log review in step 5 finds anything unexpected, the alert becomes an incident and the evidence you kept decides how well you can scope it. Capture these before anyone rewrites history or deletes branches:
| Evidence | Where it comes from | Why it matters |
|---|---|---|
| Alert record and locations | Alert view or the alert and locations endpoints, saved as JSON | First commit, author, file path and every place the secret appeared |
| Commit and push details | Commit SHA, pusher, push time, branch and whether it reached a default branch | Exposure start time and who could have seen it; SHAs change if history is rewritten later |
| Repository visibility history | Organisation audit log | A repository made public, even briefly, widens the audience |
| Fork and clone exposure | Forks list and repository traffic data | Copies you cannot clean yourself |
| Provider activity | Cloud trail, SaaS audit log or token usage log for the credential | Shows whether the secret was used and from where |
| Revocation proof | Provider console or API response, with timestamp | Exposure end time for any notification decision |
Store this in the incident ticket, not in the repository. Never attach the secret value itself. If personal data could have been reached with the credential, involve your privacy team early, because breach notification rules such as UK GDPR and EU GDPR Article 33 set a 72-hour clock from awareness for reportable personal data breaches.
Small team or large organisation
- Small team: the repository admin runs this runbook end to end. Enable notifications for the repository's security alerts so an alert is not found weeks later.
- Large organisation: work from the organisation endpoint
GET /orgs/{org}/secret-scanning/alerts?state=open&validity=active,unknown, work the subsets returned withis_publicly_leaked=trueandis_multi_repo=truefirst, and assign each alert with the API'sassigneefield to the credential owner. Organisation-wide listing needs an organisation owner or security manager. - Regulated environments: agree in advance which alerts count as a security incident (for example an active cloud key with a public leak) so notification clocks are not missed.
Detection and measurement
- Alert activity: stream secret scanning alert events to your SIEM through the
secret_scanning_alertwebhook or the audit log, so new alerts open a ticket automatically. - Bypasses: review every push protection bypass weekly; repeat bypassers need coaching or a review requirement.
- Use after revocation: failed authentications with the revoked credential at the provider show which systems you missed.
Metrics:
- Median time from alert creation to provider revocation for
activealerts. - Open alerts with validity
activeorunknown, by age band. - Share of closures with resolution
revokedagainstwont_fixandused_in_tests. - Alerts closed without a comment (target zero).
- Push protection bypasses per month.
Common failure patterns
- Delete the line, close the alert: the secret still works and is still in history; the alert was the only record.
- Rewriting history before revoking: it takes hours, breaks clones and leaves the credential live the whole time.
- Trusting
inactive: an expired or revoked secret may have been used before it stopped working. - Fixing one of a
multi-reposet: the same secret in other repositories, CI variables or wikis is forgotten. - Developers rotating keys they do not own: they create a new key but cannot revoke the old one at the provider, so both stay live.
Where identity fits
Most secret scanning alerts are non-human identity credentials: API keys, tokens, service account keys and connection strings. The lasting fix is to stop issuing long-lived secrets to code: use workload identity federation for CI, as in the GitHub Actions OIDC Federation Hardening Guide, and short-lived tokens elsewhere. Each alert should end with a question: can this credential be removed rather than replaced?
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | IR-4 Incident Handling | Containment, eradication and recovery for a leaked credential |
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management | Changing authenticators on compromise |
| NIST CSF 2.0 | RS.MI-01 | Incidents are contained |
| NIST CSF 2.0 | PR.AA-01 | Credentials for services are managed by the organisation |
| MITRE ATT&CK | T1552.001 Credentials In Files; T1593.003 Code Repositories; T1213.003 Code Repositories | Attackers search public and internal repositories for secrets |
Practitioner checklist
- Open the alert and record type, locations, validity and labels.
- Set urgency: active or public leak means contain within hours.
- Find the credential owner at the provider.
- Rotate, update dependent services, then revoke the leaked credential.
- Review provider logs from first commit to revocation, even if inactive.
- Close the alert with the true resolution and a complete comment.
- Rewrite history only if revocation is not possible or policy requires it.
- Record whether the credential can be replaced by federation or short-lived tokens.
Standards and references
- GitHub Docs: Resolving alerts from secret scanning
- GitHub Docs: Evaluating alerts from secret scanning
- GitHub Docs: About secret scanning alerts
- GitHub Docs: REST API endpoints for secret scanning
- GitHub Docs: Removing sensitive data from a repository
- OWASP: Secrets Management Cheat Sheet
Related NHI Mgmt Group resources: Git Secret Scanning Guide · Developer Secret Scanning Triage · How to revoke a leaked GitHub personal access token and check what it touched · Leaked Credential Response Playbook