npm Packages Are Leaking Your Secrets: How Credentials End Up in the Registry
June 20, 2026
The npm Registry Is a Public Secret Dump Nobody Talks About
Most secret-leak conversations focus on Git repos, CI/CD pipelines, or .env files. The npm registry rarely comes up — yet it is one of the most overlooked places credentials quietly go public. Once a package version is published, it is immediately crawled, cached, and mirrored by dozens of registries and tooling pipelines around the world. There is no reliable "undo."
This article walks through exactly how credentials end up in published packages, how to check whether yours are affected, and what you can do to prevent it from happening again.
How Credentials End Up Inside a Published Package
There are four common patterns — all of them mundane, none of them requiring carelessness.
1. Files Not Listed in .npmignore (or files in package.json)
If you do not explicitly control which files npm bundles, it falls back to a broad default that includes most of the project root. A .env file sitting next to package.json? Bundled. A config/secrets.json used only locally? Bundled. Developers are often surprised to learn that .gitignore does not automatically become .npmignore — they are independent files with independent rules.
2. Build Artifacts That Inline Environment Variables
Bundlers like webpack, esbuild, and Rollup can be configured to replace process.env.SOME_KEY with its literal value at build time. This is a useful optimization for browser bundles. It is a critical security failure when the resulting bundle is then included in the published package. The minified file ships with your API key baked in as a plain string.
3. Accidentally Published dist/ or build/ Directories
When a developer runs npm publish from a local machine (rather than CI), the build may have been generated against local environment variables. Even if the source file references process.env.API_KEY, the compiled output distributed to consumers may contain the resolved value.
4. Scoped / Internal Packages Promoted to the Public Registry
Teams sometimes develop packages internally under a private registry, then publish them publicly without a full audit. Private-registry packages frequently contain keys, tokens, and internal endpoint URLs that were never intended to be exposed.
How Attackers Find These Secrets
Several automated systems continuously scan the npm registry for high-entropy strings and known credential patterns. The window between publication and detection by a motivated attacker is measured in minutes, not hours. Beyond automated scanners, any package you publish is downloadable by anyone with npm install, which means a simple local search over the extracted tarball reveals everything.
How to Check Whether Your Published Packages Contain Secrets
These steps work for any package you own or maintain.
-
Audit the tarball before publishing. Run
npm pack --dry-runto see exactly which files will be included. Review the list carefully — anything surprising deserves scrutiny. -
Extract and search published versions. Download any already-published version with:
Then grep for known prefixes:npm pack <package-name>@<version> tar -tzf <package-name>-<version>.tgz # list contents tar -xzf <package-name>-<version>.tgz # extract grep -rE "[A-Za-z0-9_]{20,}" package/ # rough high-entropy scansk-,AKIA(AWS access key IDs),ghp_,Bearer, and any internal key prefixes your platform uses. -
Check your
.npmignoreorfilesallowlist. Thefilesfield inpackage.jsonis an allowlist — only the paths you list are included. This is generally safer than relying on a.npmignoreblocklist. Prefer an explicit allowlist:{ "files": ["dist/", "src/", "README.md"] } -
Scan your repository and build outputs. Before running
npm publishin CI, add a secret-scanning step. Run a free GhostCred scan against your repo to surface exposed credentials in source files and build artifacts before they ever reach the registry.
Immediate Remediation Steps If You've Already Published
If you discover a secret in a published package version, treat it as a confirmed exposure — because it is.
- Rotate the credential immediately. Do not wait. Revoke the exposed key and issue a new one. Assume it has already been harvested.
- Deprecate (do not just unpublish) the affected version. Running
npm deprecate <package>@<version> "Contains exposed credentials — do not use"warns existing consumers. Note thatnpm unpublishis restricted after 72 hours and does nothing to remove cached copies from mirrors. - Publish a clean patch version immediately so consumers have an obvious upgrade path.
- Audit access logs. Check your cloud provider, SaaS platform, or API dashboard for any usage of the exposed key between the publish timestamp and rotation time.
- Notify downstream consumers if the package has significant adoption, especially if the credential could grant access to shared infrastructure.
Prevention: Making It Structurally Hard to Ship Secrets
Checklists help, but structural controls are more reliable than human memory under deadline pressure.
Use an Allowlist, Not a Blocklist
As shown above, the files field in package.json explicitly declares what ships. Every file not in the list stays out. This is far safer than trying to enumerate everything that shouldn't ship.
Never Build Against Real Credentials Locally
Use placeholder values (e.g., REPLACE_ME) or a secrets manager during local builds. Real credentials should only be injected at runtime in production environments — not at compile time, and never on a developer's laptop where npm publish might accidentally run.
Publish Only From CI, Not Local Machines
Configure your CI pipeline as the sole publishing authority. CI environments should receive only scoped publish tokens (not full npm account tokens) and should run a secret scan as a pre-publish gate. If a scan finds credentials in the output directory, fail the build.
Add a prepublishOnly Hook
The prepublishOnly lifecycle script runs automatically before npm publish. Use it to run your scanner:
{
"scripts": {
"prepublishOnly": "npx secretlint . || exit 1"
}
}
This provides a last-resort safety net even when publishing from a local machine.
SOC 2 and Supply Chain Implications
If your organization is pursuing or maintaining SOC 2 Type II, credential exposure through published packages falls squarely under CC6 (Logical and Physical Access Controls) and CC8 (Change Management). Auditors are increasingly asking about supply chain hygiene — including outbound artifacts, not just inbound dependencies. Demonstrating that your publish pipeline includes automated secret scanning is concrete, documentable evidence of control.
Summary
- The npm registry is permanent and globally mirrored — a published secret is a public secret.
- The most common causes are missing
.npmignorerules, bundler inlining, and local builds promoted to the registry. - Audit every published version with
npm packand manual grep; add scanning to your CI publish pipeline. - If exposure has already occurred, rotate first, then deprecate, then investigate access logs.
- Structural controls (allowlists, CI-only publishing,
prepublishOnlyhooks) beat manual diligence every time.
See what's exposed in your own code.
Run a free scan