npm Security Checklist
This page is a practical distillation of the OWASP NPM Security Cheat Sheet, organized by workflow phase. For the why — attack vectors, incident history, and defense rationale — read npm Supply Chain Attacks first; this page is the what to do reference, with particular emphasis on publisher-side hygiene.
Before You Install — Vet the Package
Spend two minutes on the registry page before adding a dependency. Most recently compromised packages showed at least one red flag under cursory review.
Signals worth checking:
- Weekly downloads — a package with thousands of downloads is not automatically safe, but one with a few hundred downloads and no recognizable consumers deserves scrutiny.
- Last publish date — packages that haven't been updated in years may still be fine, but sudden activity after long dormancy is a known takeover pattern.
- Maintainer count — single-maintainer packages are single points of failure.
- Repository link — open it. Does the repo actually exist? Does the code match what's on npm? Watch for starjacking, where a package's
repositoryfield points at an unrelated popular repo to borrow its star count. - Dependency count — every transitive dependency is attack surface. Prefer lean packages.
- OpenSSF Scorecard — rates the project's security practices (branch protection, signed releases, CI tests, etc.).
- Socket report — analyzes actual package behavior (network calls, filesystem writes, install scripts) rather than just known CVEs.
Typosquatting (crossenv vs cross-env, electorn vs electron) is the most common way unvetted installs go wrong. See the attack-vector breakdown on the supply-chain page.
Consider npq as a drop-in wrapper for npm install — it prompts before installing with a risk summary (downloads, age, install scripts, known vulnerabilities).
Copy install commands from the package's official documentation page, not from a search-engine snippet or a Stack Overflow answer. Typosquats rank in search results too.
When You Install — Lock It Down
These practices are covered in depth on the supply-chain page and listed here for checklist completeness:
- Always commit
package-lock.jsonand usenpm ciin CI — deterministic installs that fail on drift. See Lock Files andnpm ci. - Set
ignore-scripts=truein.npmrcto neutralizepreinstall,install, andpostinstall— the most common payload location. See Ignore Install Scripts. - Publish internal packages under an npm scope (
@yourorg/name) to prevent dependency-confusion attacks. See Scoped Packages. - Set
min-release-age=7in.npmrcto quarantine newly published versions for 7 days — blocks compromised packages before they reach your project. Requires npm 11+. See npmmin-release-age.
Run a Local npm Proxy (For Teams)
A self-hosted registry proxy sits between your developers (and CI) and the public npm registry. Beyond security, it adds resilience:
- Cache against upstream outages — builds don't break when npmjs.com is down or a package is unpublished (remember
left-pad). - Allow-list enforcement — reject installs of packages not on your approved list.
- Audit trail — record every package that has entered your build environment.
- Private-package hosting — publish internal packages without exposing them publicly.
| Tool | Notes |
|---|---|
| Verdaccio | Lightweight, open source, easy to self-host. Good starting point. |
| Sonatype Nexus | Enterprise-grade, multi-format (npm, Maven, Docker, PyPI). |
| JFrog Artifactory | Enterprise, strong policy/compliance tooling. |
| GitHub Packages | Tight integration with GitHub Actions; npm-scope-based. |
Keep Your Project Healthy
npm outdated
Lists dependencies that have newer versions available, grouped by how far behind each one is.
$ npm outdated
Package Current Wanted Latest Location
eslint 8.57.0 8.57.0 9.15.0 my-app
react 18.2.0 18.3.1 19.0.0 my-app
npm doctor
Runs a diagnostic on your npm installation, registry connectivity, cache integrity, and permissions. Useful as a sanity check when npm install behaves strangely.
npm audit and npm audit signatures
npm audit checks installed packages against the GitHub Advisory Database. npm audit signatures (npm v9.5+) verifies that packages were published through npm's signing pipeline. See npm audit for the caveats.
Automate updates
Use Dependabot or Renovate to receive automated update PRs. Outdated dependencies accumulate known vulnerabilities. See Security Tooling.
If You Publish Packages
The rest of this page covers publisher responsibilities — where most projects get the defaults subtly wrong.
Use a files allow-list (don't rely on .npmignore)
The files field in package.json is an allow-list: only the paths listed are included in the published tarball. This is the opposite of .npmignore, which is a deny-list — and deny-lists fail open. Miss a pattern and secrets leak.
{
"files": ["dist/", "README.md", "LICENSE"]
}
Multiple real incidents have leaked AWS credentials, .env files, and entire .git/ directories to the public npm registry through a missing .npmignore entry. Once a version is published, assume it is compromised: rotate every credential that was in the tarball, publish a patch version, and — if needed — deprecate the leaked version. Unpublishing is restricted on npm and does not remove copies that have already been downloaded or mirrored.
Verify the contents of your tarball before publishing:
npm publish --dry-run
npm pack # creates the tarball locally; inspect with `tar tf <file>.tgz`
Never ship:
.env,.env.local, and any other.env*file.git/node_modules/- Test fixtures with real data
- CI configuration (
.github/,.circleci/, etc.) unless intentional - IDE settings (
.vscode/,.idea/) - Build and debug logs
Protect .npmrc and CI secrets
Reference tokens through environment variables, never literal strings:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
Never commit a literal publish token, even temporarily. Git history is forever, and bots scan public repositories for npm tokens within seconds. If a token is ever exposed, revoke it immediately on the npm tokens page and rotate the secret in your CI.
Keep NPM_TOKEN in your CI provider's secret store (GitHub Actions secrets, GitLab CI variables, etc.) and inject it only into publish jobs.
Enable 2FA on your npm account
npm supports two 2FA modes:
auth-only— 2FA is required to log in and change account settings, but not to publish or modify packages.auth-and-writes— 2FA is required for login and for every write action, includingnpm publish.
For any account that publishes packages used by others, use auth-and-writes. Prefer a WebAuthn security key (hardware token, Touch ID, platform authenticator) over TOTP apps — WebAuthn is phishing-resistant. npm now enforces 2FA for maintainers of high-impact packages, but you should enable it regardless of package size.
Use granular access tokens for automation
Classic npm tokens grant full account access. Granular access tokens let you scope what a token can do:
- Limit to specific packages or organization scopes
- Read-only vs read-and-publish
- Expiration date (recommended: ≤ 90 days)
- IP/CIDR allow-list
Use granular tokens for CI publishing, and rotate them on a schedule.
Do a quarterly token audit: open npm account settings, revoke anything you don't recognize or no longer need, and shorten expiries where possible.
Review your own npm scripts in PRs
A single malicious pull request can add a postinstall script that later ships to every one of your users on the next publish. Treat changes to the scripts block of package.json — and to any file those scripts invoke — with the same scrutiny you'd apply to a Dockerfile or a GitHub Actions workflow change.
Publish with provenance
npm provenance (npm v9.5+) links each published version to the source commit and build workflow that produced it, signed through Sigstore. Consumers can verify provenance with npm audit signatures. See the Security Tooling table for details.
Responsible Disclosure
For your own packages
- Publish a
SECURITY.mdwith a private contact or link to GitHub Security Advisories. - Coordinate privately via a GitHub Security Advisory — collaborate on a fix in a private fork, request a CVE, and publish the advisory with the fix release.
- Don't merge a public fix or push a descriptive commit message before the advisory is live.
For packages you depend on
- Contact the maintainer privately first (check
SECURITY.md,package.json, or npm). - Report outright malware via npm's reporting malware flow.
- 90 days is a reasonable disclosure window before publishing details.
Publicly filing a GitHub issue titled "RCE in some-package" with a working proof of concept is not responsible disclosure — it is a zero-day announcement. Give maintainers a chance to patch before you publish details.
Quick-Reference Checklist
Copy into your team's onboarding doc or PR template.
Before install
- Checked weekly downloads, last publish, maintainer count, repo link
- Inspected transitive dependency count
- Reviewed OpenSSF Scorecard or Socket report for anything unusual
- Copied the install command from official docs, not a search result
When installing
-
package-lock.jsonis committed - CI uses
npm ci, notnpm install -
ignore-scripts=truein.npmrc(or install scripts reviewed for exceptions) -
min-release-age=7in.npmrc(npm 11+) — quarantine window for new releases - Internal packages are published under an npm scope
Ongoing
-
npm auditruns in CI - Dependabot or Renovate is configured
-
npm outdatedreviewed at least monthly - Unused dependencies removed (
npx depcheck)
If publishing
-
filesallow-list inpackage.json -
npm publish --dry-runinspected before release - No literal tokens in
.npmrc;NPM_TOKENlives in CI secrets - 2FA is
auth-and-writeswith a WebAuthn key - CI uses a granular access token with expiry and package scope
- Provenance enabled on publish workflow
Disclosure
-
SECURITY.mdpublished with a private contact - Private coordination via GitHub Security Advisory, not public issues
Further Reading
- OWASP NPM Security Cheat Sheet
- About npm access tokens
- Configuring two-factor authentication
- The
filesfield inpackage.json npm audit- Verdaccio — lightweight self-hosted registry proxy
- npm Supply Chain Attacks — incident history and attack-vector rationale
- Web Security Essentials — overview of web security fundamentals