Pre-deploy security guide
How to audit npm packages for security before deploying
Most teams run npm audit and stop there. That catches CVEs — but it misses abandoned packages that will never receive a patch, copyleft licenses that can create legal exposure, and typosquatted names that run attacker code at install time. This guide covers the complete pre-deploy audit workflow.
- What npm audit misses
- Step 1: CVE scan
- Step 2: Abandoned & deprecated packages
- Step 3: License risk
- Step 4: Typosquatting
- Step 5: Wire up CI
- Pre-deploy checklist
- FAQ
What npm audit misses — and why it matters
npm audit queries the npm advisory database for packages in your dependency tree that have a published CVE or security advisory. That is genuinely useful — but the advisory database only knows about known problems that have been formally reported. It tells you nothing about:
| Risk category | npm audit | Full audit |
|---|---|---|
| Published CVEs and security advisories | ✓ Yes | ✓ Yes |
| Packages with no recent releases (abandoned) | ✗ No | ✓ Yes |
| Packages deprecated by their own maintainer | ✗ No | ✓ Yes |
| Copyleft licenses (GPL, AGPL) incompatible with commercial use | ✗ No | ✓ Yes |
| Typosquatted package names | ✗ No | ✓ Yes |
| Packages with zero downloads or no documentation | ✗ No | ✓ Yes |
Each gap represents a real category of incident: supply-chain attacks via abandoned packages, open-source license disputes, and typosquat attacks that run malicious install scripts. The pre-deploy workflow below covers all of them.
Step 1: Run npm audit locally before pushing
Start with the built-in CVE scan. The goal here is to run it before pushing to your branch — not as an afterthought in a CI failure email.
# Full audit report
npm audit
# JSON output for programmatic processing
npm audit --json
# Fail only on high and critical (useful in CI, see Step 5)
npm audit --audit-level=high
Read the output before moving on. Each advisory includes a severity, the affected package and version range, the dependency chain that brought it in, and the fixed version (if one exists). Anything at high or critical severity warrants a fix before you deploy — see the npm audit remediation guide for every fix path.
npm ci instead of npm install in your deployment pipeline. npm ci installs exactly what is in your package-lock.json without resolving newer transitive versions — keeping your audit results consistent between local and CI environments.
Step 2: Check for abandoned and deprecated packages
This is the gap npm audit does not fill. A package that was last updated in 2020 and has no open issues being addressed will never receive a security patch — any future CVE is a permanent exposure. Similarly, a package marked deprecated in the npm registry is signaling that the maintainer considers it dead and unsafe to use.
Two ways to check:
Option A: npm info per-package (for spot checks)
# See when a package was last published
npm info lodash time.modified
# See if a package is deprecated
npm info some-package deprecated
This works for checking a specific package you're unsure about, but it doesn't scale across 50 or 100 dependencies.
Option B: Paste your package.json into DepCheck (full sweep)
Paste your package.json into DepCheck to get a severity-sorted report covering abandoned packages, deprecated packages, typosquatting risk, and copyleft licenses across all your dependencies at once — in the browser, without sending your code anywhere.
Scan your package.json with DepCheck →
Pay particular attention to packages with no release in 24+ months that are in your production dependency list (not just devDependencies). For each flagged abandoned package, check: (1) is there a maintained fork? (2) is there a modern equivalent in the ecosystem? (3) how much of the API do you actually use — could it be replaced with a few lines of native code?
Step 3: Check for license risk
The npm ecosystem contains packages under hundreds of different licenses. Most popular packages use MIT, BSD, Apache 2.0, or ISC — all permissive, all safe for commercial use. But some use copyleft licenses that impose obligations on your own code:
- GPL-2.0 / GPL-3.0 — if your software links against a GPL package and is distributed, your entire codebase must be released under GPL. "Distributed" typically means shipping a desktop/mobile app, not running a server.
- LGPL-2.1 / LGPL-3.0 — weaker copyleft; usually safe if you link dynamically and don't modify the library. Static linking is murkier.
- AGPL-3.0 — the strongest copyleft for server software. If you run an AGPL-licensed package as a network service (which includes almost every web application), you must release your entire application source under AGPL. This affects many AI/ML libraries.
- EUPL — a European Union copyleft license; similar obligations to LGPL, but interacts differently with other copyleft licenses.
To check a specific dependency:
npm info package-name license
To audit your full tree for copyleft licenses, paste your package.json into DepCheck — it flags GPL, LGPL, AGPL, and EUPL dependencies by severity.
npm info on your own package.json entries won't catch this — you need to audit the full resolved tree.
Step 4: Watch for typosquatting when installing new packages
Typosquatting attacks target developers who make a typo during npm install. A package named expres, lodahs, or crossenv may look harmless in the terminal output but runs a postinstall script that exfiltrates environment variables, SSH keys, or AWS credentials before you see any error — and the code runs during npm install, before you ever import the package.
Before installing any new package, especially one you heard about in a tutorial or Stack Overflow answer:
- Double-check the exact name on npmjs.com — look at the download count, the GitHub link, and the publisher account age.
- Check how many packages that publisher account has published. A single package on a brand-new account is a yellow flag.
- Look at the
package.jsonon the registry page and check thescripts.postinstallfield before installing.
DepCheck also flags packages whose names are suspiciously similar to popular packages in its typosquatting heuristic — worth running after any batch of new installs. For a deeper look at how the attack works and a complete verification workflow, see the npm typosquatting detection guide.
Step 5: Wire the audit into your CI/CD pipeline
Local audits are a safety net; CI enforcement is the gate. Add these checks to your workflow so no deployment bypasses them.
GitHub Actions example
name: Dependency security audit
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 9 * * 1' # Weekly Monday 9 AM UTC
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies (exact lockfile)
run: npm ci
- name: Run security audit
run: npm audit --audit-level=high
# Fails build on high/critical; low/moderate appear in log but don't block
schedule trigger matters. A dependency that was safe when you last deployed may have a CVE published tomorrow. The weekly cron job catches advisories that land between your own pushes — without it, a long-lived deploy can silently accumulate high-severity findings.
Pre-commit hook (optional second line of defence)
# .git/hooks/pre-push (make executable: chmod +x .git/hooks/pre-push)
#!/bin/sh
npm audit --audit-level=critical
if [ $? -ne 0 ]; then
echo "Critical npm audit vulnerability found. Push blocked."
echo "Run: npm audit fix (or see the remediation guide)"
exit 1
fi
This blocks a push to the remote if any critical vulnerability is in the tree at push time — a useful backstop for developers who forget to audit locally.
Pre-deploy dependency checklist
- Run
npm audit— no high or critical findings outstanding - Paste
package.jsoninto DepCheck — no abandoned or deprecated packages in production deps - No copyleft licenses (AGPL, GPL, LGPL) in the flagged list that conflict with your commercial use
- No typosquatting flags for recently installed packages
- CI runs
npm ci(notnpm install) andnpm audit --audit-level=highon every push to main - A weekly scheduled audit cron is active in CI
package-lock.jsoncommitted and up to date in the repo
Frequently asked questions
Does npm audit catch all security risks in my dependencies?
No. npm audit only reports vulnerabilities with a published advisory in the npm security database. It does not flag packages that are abandoned, deprecated, typosquatted, or carrying a copyleft license incompatible with your commercial project. A complete pre-deploy audit needs to cover all four categories — that is what this guide and DepCheck cover.
What is the real risk of using an abandoned npm package?
An abandoned package will never receive a security patch after the maintainer stops responding. If a vulnerability is discovered — and the npm ecosystem regularly surfaces new advisories for packages years after their last release — you will be permanently exposed until you replace the dependency. Abandoned packages also frequently see their linked documentation domains expire and get re-registered by attackers. npm audit won't warn you until an advisory is formally published; by then, active exploitation may already be happening in the wild.
What npm packages are considered typosquatting risks?
Packages published under names visually similar to popular packages — for example, lodahs instead of lodash, or cross-env2 instead of cross-env. Attackers publish these hoping developers make a typo during npm install. The package typically runs arbitrary code at install time via a postinstall script that exfiltrates environment variables. Always verify the exact package name on npmjs.com before installing unfamiliar utilities.
Which open-source licenses are risky for a commercial project?
GPL (v2 and v3), AGPL-3.0, LGPL, and EUPL all carry copyleft obligations. AGPL is particularly significant for web applications: it requires you to release your application's source code if you run the software as a network service — which covers virtually every web app. MIT, BSD-2-Clause, BSD-3-Clause, Apache-2.0, and ISC are permissive and safe for commercial use without source-release obligations.
How do I run a dependency audit in GitHub Actions?
Add npm ci (exact lockfile install) followed by npm audit --audit-level=high to your workflow. The --audit-level=high flag causes a non-zero exit only for high and critical findings, which fails the workflow step and blocks the deployment. Add a schedule: cron trigger to catch advisories that arrive between your own pushes. See the full workflow example in Step 5 above.
What is the difference between npm audit and npm outdated?
npm audit checks your installed tree against the advisory database for known CVEs. npm outdated shows which packages have newer versions available regardless of security. They complement each other: npm audit tells you what is dangerous now, npm outdated tells you what is stale. Running both gives you a fuller picture — a package with a fix in a new version will show up in npm outdated before the advisory is always published.
How often should I run a dependency security audit?
Before every production deployment, and on every pull request in CI. For active projects, also add a weekly scheduled audit job in CI — new advisories are published continuously and a long-lived deployment can accumulate findings between your own pushes. For high-risk applications (handling payments, PII, or authentication), daily scheduled audits are reasonable given the volume of advisories published each week.
Also in the Copper Bay Labs ship-safety suite
Dependencies are one layer of your security posture. These free tools cover the rest before you ship:
Paste code or a config file and see exposed API keys and secrets flagged before they reach the repo.
Post-deploy ExposureCheckScan your live URL for exposed .env files, .git directories, source maps, and bundled secrets.
Check your security headers — CSP, HSTS, X-Frame-Options — so your site isn't an exfiltration vector.
ADA / WCAG risk ShipSafeScan your site for accessibility failures that create ADA lawsuit risk before you launch.