Skip to content
KeyDrift
Scan for free
All posts

GitHub secret scanning: what it catches and what it misses

An honest coverage map of GitHub secret scanning: partner patterns, push protection, validity checks, and the surfaces outside its remit.

KeyDrift13 min read

GitHub secret scanning: what it catches and what it misses

Leaning on GitHub's native scanning as your secret defense is a defensible choice — provided you know where its jurisdiction ends. This covers precisely what each mechanism catches, where the documented gaps fall, and how to compose coverage without duplicating what GitHub already does well.

GitHub's secret scanning is the most widely deployed leak detector in software — enabled by default on public repositories, woven through organization security features, backed by partner integrations with the providers whose keys matter most. It also routinely gets credited with coverage it doesn't have, which produces a specific failure mode: teams discovering gaps during incidents that a half-hour of documentation reading would have surfaced calmly.

This map separates documented capability from comfortable assumption, citing GitHub's own materials throughout. Fairness first, because it's earned rather than diplomatic: for credentials entering GitHub-hosted git repositories, secret scanning is genuinely excellent — fast, free at its core tier for public repos, increasingly accurate, and uniquely able to block disclosure instead of merely reporting it. Every gap identified below follows from one structural fact: the mechanism's jurisdiction is git content on github.com. Objects that never enter a repository — which includes most surfaces this series covers — sit outside that jurisdiction by definition, not by oversight.

The mechanisms, precisely

"Secret scanning" bundles several capabilities with separate availability and behavior (About secret scanning):

CapabilityWhat it doesAvailability
Partner patterns & alertsDetects tokens from ~200 partner providers; alerts both you and the providerFree on all public repos; private repos need Advanced Security
Push protectionRejects the pushing commit outright when recognized patterns appearBroad availability; default-on for new public repos, org-configurable elsewhere
Validity checksMarks whether detected credentials appear activePattern-dependent; implemented per-partner
Non-provider patternsGeneric detections beyond the partner list (entropy, structure)Advanced Security territory
Management toolingDelegate bypass, allowlists, dry runs, alert triagePer the management documentation

Three properties follow worth internalizing before any gap analysis. First, it watches git events and repository content on GitHub — commits, pushes, existing trees. Second, detection precision concentrates on partnered formats: AWS keys, Stripe keys, OpenAI keys, Slack tokens, Google/Firebase keys, GitHub's own token families and dozens more carry exact patterns with provider feedback loops (secret scanning patterns); smaller providers rely on generic heuristics or escape detection entirely. Third, blocking beats alerting categorically: push protection turns a leak event into an interrupted workflow — prevented disclosure requires no rotation, no audit, no notification analysis, which makes it the single most valuable property any leak control offers.

What it catches, concretely

Within jurisdiction, performance is strong across four scenarios:

  • The accidental commit. Key pasted into source, pushed mid-flow: push protection intercepts supported patterns at push time; partner-pattern alerts fire retroactively for everything else — frequently reaching the provider faster than internal review would have.
  • Historical exposure. Scans cover existing repository content, surfacing old leaks when patterns improve or repositories newly qualify — not just future pushes.
  • Provider-loop remediation. Partner alerts let providers validate exposed credentials and sometimes auto-revoke live ones (AWS participation being the canonical example), compressing third-party response below anything self-detection achieves alone.
  • Organization-scale visibility. Enterprise views enumerate findings across repositories with APIs feeding existing SecOps tooling; delegate bypass and allowlist controls keep legitimate test fixtures from generating permanent noise.

For the classic threat model — a developer pastes a credential into code, and the code reaches GitHub — enabling every available tier is simply correct practice, and nothing in this article should talk an organization out of it. The critique begins where the jurisdiction ends.

What it misses — by construction

Each gap below follows from placement in the pipeline, not implementation quality. That distinction matters because it tells you exactly what complements the mechanism.

1. Everything that isn't a GitHub repository

The scanner's world model contains commits and repo trees. It cannot observe:

  • Built artifacts — the bundles, chunks, and payload scripts your CDN serves. A key substituted into client JavaScript at build time exists in no commit anywhere (the founding mechanism). Those bytes never transit GitHub; its scanner will never see them.
  • Published source maps, whose sourcesContent reconstructs original files including pre-build literals.
  • Container images, npm tarballs, mobile binaries — registry and store distribution paths that package complete artifacts assembled outside git.
  • Logs, chat threads, tickets — the retention-window surfaces catalogued in the CI-artifacts guide.
  • Other forges and self-hosted git entirely.

This list isn't hypothetical residue. For AI-era frontend development specifically, the client bundle is the primary leak surface — and it is invisible to repo-side scanning by construction, regardless of vendor.

2. Patterns without partners or structure

Partner precision depends on provider cooperation. Database connection strings with embedded passwords, PEM private keys, tokens from unpartnered SaaS providers, and arbitrary high-entropy credentials fall to generic detection — which requires Advanced Security licensing on private repos and trades recall against noise in ways the false-positive analysis examines. On unpaid tiers, unpartnered formats effectively have no coverage at all: the pattern list, not the engine, defines the boundary.

3. Timing asymmetries between blocking and alerting

Push protection prevents; alerts describe. The difference determines response posture: a blocked push leaked nothing (treat reports as near-miss metrics), while a partner alert describes material that has been public since the original push landed — with provider-notification latency layered on top of your own discovery latency. Planning response capacity means knowing which capability produced which signal, because their clocks differ by hours-to-months.

4. Private-repo economics

Public repositories receive partner-pattern scanning free; private repositories — where commercial secrets actually live — require GitHub Advanced Security for equivalent coverage, licensed per committer. The resulting posture in many organizations is inverted: full protection on open-source repos containing no secrets, partial or zero coverage on private repos containing all of them. If your organization hasn't explicitly decided this question, it has decided by default — worth enumerating which private repos currently qualify for which capabilities.

The honest verdict table

Surface / scenarioCovered?Notes
Key committed to a GitHub repo — partnered formatYes — block or alertThe strongest case; enable push protection everywhere eligible
Key committed — unpartnered format (connection strings, PEMs)PartialGeneric patterns need GHAS; recall varies
Key inlined into a built bundle by framework conventionNoBytes exist only post-build; outside git entirely
Source maps published with embedded literalsNoStatic assets served alongside apps
Key inside container/npm/mobile artifactsNoRegistry distribution paths
Key echoed into CI or vendor logsNoRetention surfaces outside git
Historical repo content — partnered patternsYesRetroactive sweeps included
Repositories hosted anywhere but github.comNoJurisdiction boundary

Reading the "No" rows as one group yields the complement definition: artifact-level and post-git surfaces need observers positioned there. That's not a criticism of GitHub's layer — it's the specification of the next one.

Operating it well, whatever else you run

Enablement choices that determine whether the capable layer performs:

  1. Push protection org-wide, with documented delegate-bypass procedure so emergencies don't invent their own workarounds.
  2. An explicit GHAS decision for private repos — coverage mapped repo-by-repo, accepted-risk rows named rather than assumed.
  3. Alert routing with ownership: findings flowing to channels people read, triaged against severity tiers (the rules discipline), with SLAs distinguishing live-credential findings from stale ones (validity status informs priority; it doesn't close tickets — keys get reactivated, and "inactive" reflects check-time only).
  4. Allowlists maintained deliberately: documented sample keys and test fixtures suppressed by policy, not by alert-fatigue numbness (the placeholder paradox).

Composing full coverage

Mature setups treat GitHub's scanning as one layer among coordinated ones, each covering another's jurisdictional blindness:

  1. Git layer (GitHub): everything above — prevention plus detection where it lives.
  2. Pipeline layer: repo-independent scanning in CI covering non-GitHub forges and, critically, built output — placement guide.
  3. Artifact layer: continuous monitoring of deployed URLs and bundles — the jurisdiction where framework-convention leaks materialize. This is KeyDrift's home ground: a free first scan, then deploy-triggered and scheduled re-checks that distinguish expected-public credentials from leaked ones (tier design).
  4. Response layer: provider-native tripwires (spend caps, access policies) plus the rotation playbook for whenever any layer fires.

No layer substitutes for another because jurisdictions don't overlap. The failure pattern worth naming is coverage theater: pointing at enabled checkboxes in one column while the incident arrives through an uncovered row. The verdict table above is, practically, an incident-prediction instrument — incidents enter through whichever "No" row your architecture exercises most.

Reading alerts: a triage decision path

Alert value depends on what happens in the first five minutes after one arrives. Every alert carries the fields needed for fast disposition — pattern family, validity status where supported, location (repo, path, commit), and push context (alert documentation). Walk them in order:

Alert shapeDispositionFirst action
Partnered format, validity: activeLive exposureRotate immediately; audit usage window at provider
Partnered format, validity: inactive/staleProbably dead — verify anywayCheck provider-side last-used; rotate if any doubt
Test-mode variant (sk_test_, sandbox keys)Bounded severitySchedule rotation; fix source; don't page anyone
Matches documented sample keyFalse positive by identityAllowlist with note; fix docs if it's yours
Generic/entropy finding, no partnerUnknown until verifiedManual provider check before either rotation or dismissal

The table's discipline prevents both classic failures: rotating into the night over documented sample keys (the trust-burner that teaches teams to ignore alerts), and letting live findings sit in backlogs because they arrived alongside forty fixtures (the incident-maker). Validity status accelerates but never terminates analysis — keys get reactivated, "inactive" reflects check-time only, and test/live distinctions vary per provider (Stripe's tiers illustrate the range).

Bypass governance belongs to this same discipline. Delegate bypass and allowlist entries are administrative powers with audit trails — grant them narrowly, review quarterly alongside the org token audit, and require a note on every bypass describing why the pattern was permitted through. Unreviewed bypass authority migrates from safety valve to standard workflow faster than almost any other security control; the quarterly look is what keeps it a valve.

Measuring your own coverage honestly

The verdict table becomes actionable as a worksheet. For your organization, fill in each row's current state — writing it down is the point:

RowYour state todayEvidence
Push protection enabled where eligible?☐ org-wide ☐ partial ☐ noOrg security settings screenshot
GHAS coverage decision for private repos?☐ decided ☐ default-by-inattentionLicensing record
Built artifacts scanned anywhere?☐ CI gate ☐ monitoring ☐ neverPipeline config
Source maps audited?☐ yes ☐ unknownSample .map fetch results
Registry artifacts (images/tarballs) checked?☐ yes ☐ noAudit command output
Log-retention surfaces inventoried?☐ yes ☐ noCI-artifacts review

Two properties make this worksheet worth maintaining: it converts vague assurance into named gaps ("we're covered" becomes "rows one and two are solid; three through five are open"), and it predicts incidents — the row matching your architecture's heaviest traffic is where discovery will happen if nothing changes. Revisit quarterly alongside token audits; coverage decays silently as architectures add surfaces faster than controls.

How a partner alert actually flows

Understanding the timeline clarifies what each capability does and doesn't buy:

push lands → pattern matches partner token
  → GitHub: alert created for repository owner (you)
  → GitHub: notification sent to the token's provider
  → provider: validates the credential against live state
      ├─ valid  → may notify you directly, restrict, or revoke per program
      └─ invalid → recorded; no emergency on their side

Latency expectations follow from the mechanics: your alert exists essentially at push time; provider-side action runs on their validation schedule and program terms — hours to days in practice, and some providers auto-revoke confirmed-live credentials while others only inform. Planning response capacity therefore never assumes provider action: your rotation clock starts at discovery regardless of whether AWS or Stripe or OpenAI has acted yet (the runbook's ordering rule).

The flow also explains why partnered formats dominate alert quality: everything upstream of "provider" is pattern matching GitHub can do alone, but validation and revocation require the relationship. For unpartnered formats the chain stops after step one — which is precisely where the complement layers take over.

Rollout sequencing determines whether blocking arrives trusted or resented: enable alerting first, gather weeks of findings volume, tune allowlists against the placeholder species that surface, then switch on blocking once operators trust the signal. Organizations skipping straight to blocking discover their legitimate fixtures at push time — maximum friction, maximum resentment, and a queue of bypass requests that erodes the governance the feature depends on. Dry-run modes exist for exactly this calibration window; use them like canary releases, because that is what they are.

FAQ

Does GitHub notify me about secrets found in my public repository? Yes — repository-owner alerts for partnered patterns, plus separate notification to the token-issuing provider where partnership applies. Both legs are deliberate: you learn what leaked, they can validate and sometimes revoke. Unpartnered formats may generate nothing at all.

If push protection blocked a key, do I still rotate? No rotation needed — a blocked push never reached the remote, so no disclosure occurred. Fix the local file, replace the value with a placeholder or vault reference, push again. Treat blocked-push reports as near-miss telemetry, not incidents.

We enabled secret scanning org-wide. Are we done? You've covered one column of the table thoroughly. Whether more is needed depends on whether your architecture moves secrets through builds into served artifacts — modern web apps usually do, and that motion is invisible to repo-side tooling of every vendor.

How should validity flags affect prioritization? Confirmed-active findings outrank unverifiable ones. But treat "invalid" as stale information, never safety: validity reflects check-time state, keys get reactivated or partially scoped, and mode distinctions matter per provider (Stripe's tiers).

What's the fastest way to find our coverage gaps? Walk the verdict table against your actual architecture: which artifact types ship, where they distribute, which logs persist, which repos are private without GHAS. One honest hour answers it — or the free scan answers the bundle row immediately.


GitHub watches the git surface superbly. Your deployed bundle needs its own watcher: run KeyDrift's free scan — no signup — and close the jurisdiction gap today.

KeyDrift reads the JavaScript your app actually serves and finds the Supabase, Stripe, OpenAI and AWS keys that should never have left your server. Run a free scan.

KeyDrift is a Veristria product. More about KeyDrift.