Secret scanners find far more alerts than security teams can investigate, and most organisations end up with thousands of open findings that nobody owns, while the few live, privileged credentials among them stay valid for months. The scanner is rarely the problem. The missing piece is a triage process that decides quickly which findings are real, which are dangerous, who must act and how a false positive gets closed without hiding a true one. This guide gives engineering and AppSec teams that process: a prioritisation model based on validity, privilege and exposure, close reasons that stand up to audit, suppression rules that do not become blind spots, routing to owners and measurable service levels. It assumes you already run scanning; deployment of scanners and push protection is covered in the Git Secret Scanning Guide.
Key takeaways
- Validity first, then privilege, then exposure. A verified, active credential with production write access in a public repository is an incident. An unverified test key in a private fixture is housekeeping. Score every alert on those three factors before anyone opens it.
- Inactive does not mean safe. GitHub's guidance is that a committed secret should be treated as compromised and that inactive secrets still need a check that no unauthorised access happened before deactivation.
- Close reasons are evidence. Use a fixed set (revoked, false positive, used in tests, won't fix) with a mandatory comment. GitHub stores the resolution comment in the alert timeline and it cannot be edited later, which makes it useful audit evidence.
- Suppress narrowly. Fingerprint-level ignores and inline allow markers are safer than path or regex allowlists that silently cover future real secrets.
- Revocation, not deletion, ends the incident. Removing the file or rewriting history leaves the credential valid. The fix is revoke, rotate, check usage logs, then clean up.
What triage has to decide
Every secret alert needs four answers, in this order. Teams that skip straight to "who committed this" lose hours on alerts that did not matter while live credentials wait.
- Is it a real secret? True positive, false positive (a pattern match on something that is not a credential), or a deliberate test value.
- Is it still valid? Confirmed active, confirmed inactive, or unknown.
- How bad would misuse be? What the credential can reach and do, and how many people or systems could have read it.
- Who acts, and by when? The owner of the credential (not necessarily the committer) and a service level based on the answers above.
Scanner output helps with the first two. GitHub reports validity as active, inactive or unknown, where unknown means the token type is not supported for validation or the check failed. TruffleHog reports verified, unverified and unknown, the last meaning verification was attempted but hit a network or API error, and its --results flag filters on these values. GitLab records exposures in its vulnerability report and can automatically revoke some secret types. The other two answers need context the scanner does not have: an inventory of what each credential can do and who owns it.
A prioritisation model that works
Score three factors and combine them into a priority. Keep the model simple enough that a script can apply it to every new alert.
| Factor | High | Medium | Low |
|---|---|---|---|
| Validity | Verified active by scanner or provider | Unknown or unverified, credential type supports verification later | Confirmed inactive, expired or revoked |
| Privilege | Cloud admin, CI/CD, source control org tokens, signing keys, production databases, identity provider keys | Read access to production data, scoped service tokens, internal APIs | Sandbox, development-only or read-only to non-sensitive data |
| Exposure | Public repository, public gist, package, container image, paste site; GitHub public leak label | Private repository with broad internal access; same secret in many repositories (multi-repo label) | Private repository with a small team, never pushed beyond a feature branch |
| Priority | Typical combination | Response target | Who is paged |
|---|---|---|---|
| P1 | Active (or unknown) plus high privilege or public exposure | Revoke within 1 hour; usage review same day | Credential owner and on-call security; open an incident |
| P2 | Active with medium privilege in a private repository, or unknown with high privilege | Rotate within 1 business day | Credential owner via ticket; security reviews closure |
| P3 | Unverified, low privilege, private | Rotate or confirm invalid within 7 days | Team backlog with SLA tracking |
| P4 | Inactive or test value | Close with reason and comment within 30 days; check logs for inactive production credentials | Team backlog |
These targets are starting points; set your own and publish them so teams know what is expected. Two rules prevent common errors: unknown validity never lowers priority (it means you do not know), and age does not lower priority. The Toyota T-Connect key exposure sat in a public repository from December 2017 to September 2022; an old alert is an old exposure, not a stale one.
Handling false positives without creating blind spots
Classify before you suppress
- False positive: the match is not a credential at all, for example a hash, a UUID or a placeholder such as
EXAMPLE_KEY. Close and tune the rule if the pattern recurs. - Used in tests: a real-looking credential that is deliberately fake or only works against a local mock. Confirm it is not accepted by any real service. The OWASP Secrets Management Cheat Sheet recommends one standard set of test secrets across the organisation, which makes this class easy to recognise.
- Revoked: a real secret that has been revoked and replaced. Only use this after revocation is confirmed, not planned.
- Won't fix: a real secret accepted as a risk, for example a public client identifier that is designed to be exposed. Require security approval and an expiry date for the exception.
GitHub's REST API accepts exactly these resolution values (false_positive, used_in_tests, revoked, wont_fix) with an optional resolution_comment, so the classification can be automated and reported on. Make the comment mandatory in your process: what was checked, by whom, and the evidence (for example "provider console shows key deleted 2026-10-20").
Suppression mechanisms compared
| Mechanism | Scope | Risk | Use for |
|---|---|---|---|
| Close the alert with a reason | One finding | Low; recorded in alert history | Default for every decision |
Fingerprint ignore (Gitleaks .gitleaksignore) | One specific secret at one location | Low; new secrets still alert | Confirmed false positives in history |
Inline marker (gitleaks:allow comment) | One line | Medium; any developer can add it. Gitleaks offers --ignore-gitleaks-allow so central scans can disregard markers | Known test fixtures, with review in pull requests |
Stopwords and value regexes (stopwords, regexes with regexTarget) | All values matching | Medium; a real secret that happens to match is hidden | Placeholders such as changeme or example |
Path allowlist (paths) | Whole files or directories | High; any real secret later added there is invisible | Generated or vendored files only, reviewed quarterly |
Baseline (--baseline-path) | All findings in an earlier report | High if never revisited; the backlog disappears from view | Separating new findings from legacy, with a parallel plan to clear the baseline |
| Entropy threshold | A rule's matches below a randomness level | Medium; low-entropy human passwords escape | Tuning noisy generic rules |
Gitleaks allowlists can be global ([[allowlists]], optionally limited with targetRules) or per rule ([[rules.allowlists]]), and a finding is ignored if any matching allowlist applies. That makes it easy for one broad entry to hide a great deal; review allowlist changes as security-sensitive code. AI-based false positive scoring, such as the confidence scores GitLab offers on some tiers, can speed up review but should rank findings, not close them unseen. For tuning detection rules themselves, see the Secret Scanner Rule Coverage Guide.
Routing to the right owner
The committer is often the wrong person: they may have copied a credential someone else owns, or left the company. Route on the credential, not the commit.
- Use provider metadata: GitHub's extended metadata (in public preview for some token types such as OpenAI, Google OAuth, Slack and Replicate) can show owner, organisation and issue dates. Provider consoles show who created a key.
- Use your NHI inventory: map the credential to the service account, application and owning team. If no owner exists, that is a finding in its own right. See NHI Discovery Across Cloud, SaaS, CI/CD and On-Premises Environments.
- Use repository ownership: CODEOWNERS or service catalogue entries for the repository as a fallback.
- Deduplicate first: the same secret in ten repositories is one credential and one rotation. GitHub's
multi-repolabel helps; group by secret hash in other tools. - Escalate unowned P1s to security: security revokes, the business finds the owner afterwards. Waiting for an owner is how a P1 stays open for months.
The response workflow for a confirmed secret
- Revoke or disable: the OWASP cheat sheet sets out revoke, rotate, delete and log as the incident sequence. Revocation comes first because it ends the exposure. For tokens where revoking breaks production, rotate with an overlap window, but start within the service level.
- Rotate: issue a replacement through the secrets manager, with least privilege and, where possible, a short lifetime. Better still, replace the static secret with workload identity or OIDC federation so there is nothing to leak next time.
- Review usage: check the provider's audit logs from the first commit date to revocation for calls from unexpected IP addresses, user agents or regions. GitHub's guidance is to check security logs for unauthorised activity, and to do so even for inactive secrets.
- Clean up: remove the secret from code and, where justified, history. History rewriting has side effects and does not remove forks or clones, so it is a hygiene step, not a fix.
- Close with evidence: resolution reason, comment, revocation timestamp and log review result. Alerts are not closed automatically when the secret is removed.
- Prevent recurrence: push protection for the repository, a pre-commit hook for the team, and a note on why the secret was in code at all.
Provider-specific steps are in the leaked credential response guides, for example How to revoke a leaked GitHub personal access token and check what it touched and How to rotate an AWS IAM access key without downtime.
Implementing the triage process
- Measure the backlog: export all open alerts with validity, repository visibility and secret type. Most teams find that a small share is active.
- Clear the active set first: treat every active or unknown alert with high privilege or public exposure as P1 in a one-off sprint, regardless of age.
- Automate scoring: a script or workflow applies the priority model to every new alert, adds labels and opens a ticket with the owner and due date.
- Agree close reasons and evidence: publish the four reasons, the evidence each requires and who may approve won't fix.
- Pilot with two teams for a month: measure time to close and the share of closes that security disagrees with on review.
- Roll out with a sampled review: security reviews a random 10 percent of closed alerts each month, with all won't fix and used in tests decisions reviewed.
- Keep a rollback for suppressions: allowlist and baseline files live in version control, so a bad entry can be reverted and the hidden findings resurface on the next scan.
Small teams: use the platform's built-in validity checks and close reasons, a single priority rule (active and production equals today) and a weekly review. Large organisations: centralise alerts from several scanners, deduplicate by secret, integrate with the NHI inventory for ownership and report service levels by business unit. The Secret Sprawl Measurement Guide covers organisation-wide reporting.
Where identity fits
Almost every secret alert is a non-human identity problem: an API key, token, service account key or certificate that was issued to a workload and ended up in code. Triage works best when each alert links to that identity record, with its owner, permissions, last use and rotation method. Over time the goal is fewer secrets to triage at all, by moving workloads to federated, short-lived credentials. See the Secrets Rotation Runbook Template for rotation procedures.
Detection and measurement
- Mean time to revoke for P1 alerts: from alert creation to confirmed revocation. The headline metric.
- Active secrets open beyond SLA: count by team; target zero.
- Disputed closure rate: share of sampled closures that security reopens. Above 5 percent means close reasons are being misused.
- False positive rate by rule: drives rule tuning; a rule above 50 percent false positives needs work.
- Suppression growth: number of path allowlist entries and baselined findings over time; it should fall.
- Repeat offenders: credentials or repositories with recurring leaks, which point at a workflow that needs a secrets manager.
Also alert on use of a revoked credential at the provider, which the OWASP cheat sheet recommends logging, and on push protection bypasses, which should each create a triage item.
Framework mapping
| Framework | Reference | Triage activity |
|---|---|---|
| MITRE ATT&CK | T1552.001 Credentials In Files; T1078.004 Cloud Accounts | The technique exposed secrets enable, and the follow-on use of leaked cloud keys |
| NIST SP 800-53 Rev. 5 | IA-5 Authenticator Management; RA-5 Vulnerability Monitoring and Scanning; SA-11 Developer Testing and Evaluation | Rotation and revocation; scanning and remediation tracking; developer-side checks |
| NIST CSF 2.0 | PR.AA-01 identities and credentials managed; DE.CM-09 software and runtime monitored | Credential lifecycle; detection of exposures |
| CIS Controls v8.1 | Control 16 Application Software Security | Secure development process including secret handling |
| OWASP | Secrets Management Cheat Sheet sections on detection and incident response | Test secret standards; revoke, rotate, delete, log |
Common failure patterns
- Deleting the file and closing the alert: the credential stays valid. In the CISA Private-CISA GitHub leak, AWS keys reportedly remained valid for about 48 hours after the repository was taken down.
- Bypassed controls with no triage item: in the same case, a contractor had switched off push protection. Every bypass or disablement should raise an alert someone owns.
- No route for external reports: in the Home Depot token exposure, a researcher's reports reportedly went unanswered and the token was revoked only after press contact. External reports need the same P1 path as scanner alerts.
- Marking "used in tests" without checking: a real key placed in a test file is still a real key.
- Broad path allowlists: a
tests/orconfig/allowlist that later hides production credentials. - Treating unknown validity as low risk: custom and internal credential types are always unknown, and they are often the most privileged.
Lessons from incidents
The incidents above share a pattern: the secret was found, by a scanner, a researcher or a vendor, long before it was revoked. Detection was not the gap; triage, ownership and revocation were. Supplier and contractor repositories feature repeatedly, as in the Toyota case where a development subcontractor published the code, so triage scope must include repositories outside your organisation that hold your credentials, usually found by external monitoring.
Practitioner checklist
- Every alert scored on validity, privilege and exposure automatically.
- P1 definition and one-hour revocation target published and paged.
- Four close reasons in use, each with mandatory evidence comment.
- Won't fix requires security approval and an expiry date.
- Suppressions prefer fingerprints; path allowlists and baselines reviewed quarterly.
- Alerts routed to the credential owner via the NHI inventory, with security revoking unowned P1s.
- Usage logs reviewed for every confirmed secret, including inactive ones.
- Monthly sample review of closed alerts with disputed closure rate tracked.
- External researcher reports routed into the same P1 process.
- Recurring leaks fed into migration to short-lived, federated credentials.
Standards and references
- GitHub Docs: Evaluating alerts from secret scanning
- GitHub Docs: Resolving alerts from secret scanning
- GitHub Docs: REST API endpoints for secret scanning
- OWASP: Secrets Management Cheat Sheet
- GitLab Docs: Secret detection
- Gitleaks: project documentation
- TruffleHog: project documentation
- MITRE ATT&CK: Unsecured Credentials: Credentials In Files
Related NHI Mgmt Group resources: Git Secret Scanning Guide · SAST Triage Guide · Push Protection Guide · Secret Sprawl Measurement Guide · Secrets Rotation Runbook Template