← All articles

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.

  1. Audit the tarball before publishing. Run npm pack --dry-run to see exactly which files will be included. Review the list carefully — anything surprising deserves scrutiny.
  2. Extract and search published versions. Download any already-published version with:
    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 scan
    Then grep for known prefixes: sk-, AKIA (AWS access key IDs), ghp_, Bearer , and any internal key prefixes your platform uses.
  3. Check your .npmignore or files allowlist. The files field in package.json is an allowlist — only the paths you list are included. This is generally safer than relying on a .npmignore blocklist. Prefer an explicit allowlist:
    {
      "files": ["dist/", "src/", "README.md"]
    }
  4. Scan your repository and build outputs. Before running npm publish in 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.

  1. Rotate the credential immediately. Do not wait. Revoke the exposed key and issue a new one. Assume it has already been harvested.
  2. Deprecate (do not just unpublish) the affected version. Running npm deprecate <package>@<version> "Contains exposed credentials — do not use" warns existing consumers. Note that npm unpublish is restricted after 72 hours and does nothing to remove cached copies from mirrors.
  3. Publish a clean patch version immediately so consumers have an obvious upgrade path.
  4. 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.
  5. 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 .npmignore rules, bundler inlining, and local builds promoted to the registry.
  • Audit every published version with npm pack and 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, prepublishOnly hooks) beat manual diligence every time.

See what's exposed in your own code.

Run a free scan