This week a security researcher found a GitHub token with admin rights to hundreds of a vendor's repositories sitting inside the login page of a Hanwha Vision security camera, shipped to every customer, in plain sight. Nobody at the company wrote code to leak it; the build pipeline did it automatically. If your team ships JavaScript through a bundler and a CI system, the same failure mode is one misconfigured line away, and the defenses take about an hour to put in place: check your build output, turn on the free scanning GitHub already offers, and shrink what any single leaked token can do. Here is the anatomy of the incident, why it is depressingly normal, and the hardening pass to run this week.
Anatomy of a shipped secret
The researcher's writeup reads like a checklist of ordinary tools: download the camera's publicly available firmware, unpack it, decrypt it, and run TruffleHog, an open-source secret scanner, over the extracted filesystem. Out came a GitHub token, duplicated across roughly 30 files of the camera's compiled web interface, with "admin privileges to hundreds of repositories in their github organization."
The root cause was not a careless developer pasting a key into source code. During the Vite build of the camera's web UI, the entire CI environment, process.env, every variable the build server held, was written into the compiled JavaScript. The CI system needed the GitHub token to do its job; the build config swept it into the product. To the company's credit, the response was fast: the token was revoked within 12 hours of disclosure.
This is normal, which is the problem
GitGuardian's State of Secrets Sprawl report (checked July 24, 2026) counted 23.77 million new secrets committed to public GitHub in 2024 alone, up 25% year over year, with at least 4.61% of the 69.6 million public repositories it scanned containing a secret. Two of its findings matter most for small teams: 35% of private repositories scanned contained at least one plaintext secret (privacy breeds complacency), and 70% of secrets leaked in 2022 were still valid years later. Leaks are common; revocation is what's rare.
Bundlers are explicit about the boundary. Vite's documentation exposes only variables prefixed VITE_ to client code, and warns in as many words: "VITE_* variables should not contain sensitive information such as API keys. The values of these variables are bundled into your source code at build time." The Hanwha bug was a build that ignored that boundary and serialized everything. Webpack's DefinePlugin, Next.js's NEXT_PUBLIC_ convention, and every equivalent have the same property: whatever crosses the line ships to everyone, forever, in a file nobody on your team will ever read again.
Defense one: treat build output as public
Your dist/ folder is a publication. Before release, ideally as a CI step, search it the way an attacker would: grep the compiled output for your secret formats. GitHub tokens start with ghp_ or github_pat_, Stripe keys with sk_live_, AWS access keys with AKIA. A match in anything that ships to a browser, an app store, or a firmware image is a release blocker. The deeper rule: never spread a whole environment object into your bundler's define step. Enumerate the public variables one by one, use the framework's public prefix as designed, and keep every credential server-side where the client asks your API to act on its behalf.
Defense two: turn on the scanning you already have
GitHub runs secret scanning automatically and free on public repositories, it sweeps the full git history, issues, PRs, and wikis, raises alerts on the repository's Security tab, and reports recognized partner tokens to the issuing provider for revocation. Push protection goes one better and blocks the push before the secret ever lands; it is on by default for personal accounts pushing to public repos, and a bypass requires stating a reason, gets audit-logged, and emails the owners. Private repositories are the gap: scanning there requires GitHub's paid Secret Protection on Team or Enterprise plans, which, given that private repos are where 35% of teams keep a plaintext secret, is worth the line item once real credentials are at stake. And because GitHub scans repositories, not artifacts, add one scanner run over your build output in CI, TruffleHog, the researcher's tool of choice in the Hanwha case, is open source and does exactly this.
Defense three: assume a leak, cap the damage
The Hanwha token's real sin was scope: one credential, admin over hundreds of repositories. GitHub's fine-grained personal access tokens let you scope a token to a single repository with minimal permissions and an expiry date, a CI job that clones one repo should hold a token that can clone that repo, nothing else, and die young. Rotation is the discipline the numbers say almost nobody has: if 70% of 2022's leaked secrets still worked years later, then most organizations' response plan is hope. Decide now, while calm: the moment a leak is suspected, revoke first and investigate second. The 12-hour revocation is the only flattering part of the camera story; make yours faster by having the runbook written.
The one-hour hardening pass
- Grep your latest build output for
ghp_,github_pat_,sk_live_,AKIA, and any internal key prefixes. (10 minutes, and you will know immediately if you have a Hanwha problem.) - Open your bundler config and look for any place a whole env object is defined into the client build. Replace it with an explicit allowlist of public variables. (15 minutes)
- Confirm push protection is active on your repos; on private repos, decide consciously whether the paid tier is warranted rather than defaulting to unprotected. (10 minutes)
- Add a secret-scanner pass over build artifacts to CI. (15 minutes)
- Inventory the tokens your CI holds; replace any org-wide or classic tokens with fine-grained, single-repo, expiring ones. (10 minutes to list, schedule the swaps)
- Write the two-line incident rule where the team will find it: revoke first, investigate second.
None of this is glamorous, which is exactly why it works, the same logic as choosing boring technology: the failure modes are known and the defenses are cheap. Like the email fraud that drains payables, shipped secrets are a process problem wearing a technology costume. The camera vendor had a security team and still published its own keys; the difference between them and a two-person SaaS is that you can read this and spend the hour today.
Leaked credentials are one door into your money; forged emails are another, see business email compromise, by the numbers. And capping the blast radius of any single exposed card number is exactly what virtual cards are for.
Discussion
Sign in with Google or just a name. No email link, no password to remember.