← All articles

Secrets in Dependency Lock Files: How package-lock.json and yarn.lock Expose More Than You Think

July 2, 2026

The Lock File You're Committing Every Day Might Contain Credentials

Lock files — package-lock.json, yarn.lock, pnpm-lock.yaml, Gemfile.lock, poetry.lock — are a routine part of every developer's workflow. You generate them, commit them, and rarely read them. That last habit is the problem.

Because lock files are auto-generated and rarely reviewed by humans, they're one of the most overlooked vectors for credential leakage in modern software projects. This article walks through exactly how secrets end up in these files, what attackers can extract, and how to close the gap before your next commit.

How Credentials End Up in Lock Files

1. Private Registry Authentication URLs

When you configure npm or Yarn to pull packages from a private registry — Artifactory, GitHub Packages, AWS CodeArtifact, or a self-hosted Verdaccio — your .npmrc or .yarnrc.yml often contains an auth token or encoded credentials. Lock files sometimes resolve and embed the full authenticated registry URL, including the token, directly into the resolved field.

Example of what can appear inside package-lock.json:

"resolved": "https://registry.npmjs.org/_authToken=npm_xxxxxxxxxxxxxxxxxxxx/lodash/-/lodash-4.17.21.tgz"

Or for a private registry:

"resolved": "https://user:ghp_ABCDEFxxxxxxxxxxxxxxxxxx@npm.pkg.github.com/@myorg/private-lib/-/private-lib-1.2.0.tgz"

This URL is now in your version history, visible to anyone with read access to the repository — including every contractor, auditor, or future employee you onboard.

2. Embedded Tokens in Integrity Hashes From Authenticated Sources

Yarn Berry (v2+) and pnpm resolve packages through authenticated endpoints and can store checksum or resolution metadata that references private endpoints by full URL, occasionally including session tokens generated at install time — especially when using custom fetchers or monorepo workspace protocols misconfigured to hit authenticated endpoints.

3. Git Dependencies With Embedded Credentials

It's surprisingly common to install a package directly from a private Git repository:

npm install git+https://user:TOKEN@github.com/myorg/private-repo.git

When npm resolves and locks this dependency, the full URL — token and all — is written into package-lock.json under the "version" or "resolved" field. This pattern shows up frequently in internal tooling and monorepo bootstrapping scripts.

4. Gemfile.lock and Python Lock Files

Bundler's Gemfile.lock can embed private gem server URLs specified in Gemfile source blocks. If those sources use HTTP Basic Auth encoded in the URL (a legacy but still-used pattern), the credentials travel with the lock file. poetry.lock is generally safer here, but pip's requirements.txt with --extra-index-url containing tokens is a close cousin of the same problem.

Why This Is Worse Than a Leaked .env

A developer who leaks a secret in a .env file often catches it quickly — linters, pre-commit hooks, and secret scanners are routinely tuned to catch .env patterns. Lock files, by contrast:

  • Are treated as machine-generated noise and are rarely reviewed in pull requests.
  • Contain thousands of lines, making a single embedded token trivially easy to miss.
  • Are almost always committed to version control, even in public repositories.
  • Are long-lived — a credential embedded in a lock file two years ago is still in your Git history today, even if the file has since been regenerated.

How to Check Your Lock Files for Embedded Secrets

Start with a targeted grep before reaching for heavier tooling. Look for common token patterns and authenticated URL shapes:

# Search for npm tokens in lock files
grep -rE "_authToken=|https://[^@]+:[^@]+@" package-lock.json yarn.lock pnpm-lock.yaml

# Search for GitHub PAT patterns
grep -rE "ghp_[A-Za-z0-9]{36}" package-lock.json yarn.lock

# Search for Base64-encoded Basic Auth in resolved URLs
grep -rE "resolved.*https://[A-Za-z0-9+/=]{20,}@" package-lock.json yarn.lock

These greps will surface obvious cases. For subtler patterns — tokens in non-standard formats, credentials embedded in checksum metadata, or private registry hostnames that shouldn't be public — automated scanning across your full repository is more reliable. You can run a free GhostCred scan to check your repo for exposed credentials across lock files, dotfiles, and the rest of your codebase in about 60 seconds.

How to Fix and Prevent This

Use Environment Variables for Registry Auth, Not Embedded URLs

The correct pattern for npm private registry auth is to set the token via an environment variable at install time, not by embedding it in the URL:

# .npmrc (safe — no token hardcoded)
@myorg:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}

The ${NODE_AUTH_TOKEN} is resolved at install time from the environment, not written into the lock file.

Audit Generated Lock Files Before Committing

Add a pre-commit hook (using pre-commit or Husky) that runs a quick grep over lock file diffs. You only need to scan the changed lines in CI, which keeps it fast:

git diff --staged -- package-lock.json yarn.lock pnpm-lock.yaml \
  | grep "^+" \
  | grep -E "_authToken=|:[^/]{8,}@"

If the command returns any output, fail the commit and require review.

Replace Git+HTTPS-With-Token Dependencies Immediately

If you have any dependencies installed via git+https://token@github.com/..., replace them with SSH-based URLs and use deploy keys, or move the package to your private registry so it's fetched through the standard (token-free URL) resolution path.

Purge the History If You Find a Credential

Regenerating the lock file doesn't remove the old credential from Git history. Use git filter-repo (the recommended replacement for git filter-branch) to rewrite history and remove the sensitive content, then force-push and rotate the exposed credential immediately — in that order, because the rotation stops active exploitation while the history rewrite is in progress.

Add Lock Files to Your Secret Scanning Scope

Many secret scanning tools default to scanning source files and skip auto-generated artifacts. Explicitly include *.lock, *-lock.json, and *-lock.yaml in your scanner's include patterns. If your tool doesn't support configuring this, consider whether it's actually covering your full attack surface.

Compliance Implications

Under SOC 2 (CC6.1, CC6.6) and HIPAA Security Rule safeguards for access control and transmission security, storing long-lived credentials in committed version control artifacts — even generated ones — represents a direct control failure. Auditors increasingly look at CI/CD pipeline hygiene, including what's stored in repositories, not just what's deployed to production. A lock file with an embedded GitHub token is a finding, full stop.

The Takeaway

Lock files deserve the same scrutiny you apply to any other committed file. They're generated by tools, but they live in your repo, travel through your CI, and accumulate in your Git history just like hand-written code. The credential that gets your private package registry compromised probably won't be in a file anyone thought to check — and that's exactly the point.

See what's exposed in your own code.

Run a free scan