Every dependency you install is code running on your system or in your application. That code can have vulnerabilities. It can also be compromised directly. Understanding security helps you use packages responsibly.
Checking for vulnerabilities
Run this command to check your installed packages for known security issues:
npm audit
This scans your dependency tree against a database of known vulnerabilities and reports:
- Which packages are vulnerable
- The severity level (low, moderate, high, critical)
- A brief description of the vulnerability
- Whether a fix is available
pnpm has the same command:
pnpm audit
The output shows a detailed report. Review it carefully — not all findings require immediate action, but critical vulnerabilities should be addressed promptly.
Understanding audit output
A typical audit finding looks like:
lodash <=4.17.20
Severity: high
Remote code execution via template injection...
Fix: Update to version 4.17.21
This tells you:
- Package name:
lodash - Affected range:
<=4.17.20 - Severity:
high - Problem: template injection vulnerability
- Solution: update to
4.17.21
Critical and high-severity vulnerabilities often need immediate attention. Low and moderate severity might be acceptable if they’re in build tools or dev dependencies with limited exposure.
When audit is useful vs noisy
Audit is useful for:
- Finding known vulnerabilities in dependencies
- Checking before deploying to production
- Regular maintenance checks
- Meeting security compliance requirements
Audit can be noisy when:
- Vulnerabilities are in build tools or dev dependencies
- The vulnerability is not exploitable in your application
- You’re in the middle of development and plan to update soon
Use audit findings as information, not panic. Evaluate each based on:
- Severity level
- Whether the package runs in production
- Whether your code exercises the vulnerable part
- How difficult it is to upgrade
Transitive dependency risk
Your dependencies have dependencies. This is called the dependency tree or dependency graph.
If you run:
npm install express
Express might depend on:
- accepts
- array-flatten
- body-parser
- …and those packages depend on more packages
A vulnerability can be in a dependency many levels deep. You’re responsible for your entire dependency tree, not just the packages you installed directly.
This is why lockfiles are security-critical. Your lockfile locks down the entire dependency tree, not just your direct dependencies. Reviewing lockfile changes helps you spot unexpected transitive dependencies.
Fixing vulnerabilities
To automatically fix vulnerabilities when possible:
npm audit fix
This updates packages to non-vulnerable versions within your version ranges. It might:
- Update direct dependencies
- Update transitive dependencies
- Not be able to fix everything (if fixes aren’t available)
pnpm works the same way:
pnpm audit fix
Always test after running audit fix. Updates can change behavior.
Manual fixes with version overrides
Sometimes a fix isn’t available, or you need to force a specific version. Use version overrides:
In npm, add to package.json:
{
"overrides": {
"lodash": "4.17.21"
}
}
In pnpm, use pnpm.overrides:
{
"pnpm": {
"overrides": {
"lodash": "4.17.21"
}
}
}
This forces all packages using lodash to use version 4.17.21, even if they specify a different range. Use this carefully — it can break packages that aren’t compatible with the forced version.
Only use overrides when:
- A vulnerability has no other fix
- You’re confident the forced version is compatible
- You’ve tested thoroughly
Lockfile review habits
Every time a lockfile changes, review it:
git diff package-lock.json
Look for:
- Unexpected new packages (possible supply chain issue)
- Large version jumps (might break something)
- Packages you didn’t knowingly add
- Typosquatting attempts (packages with names similar to popular ones)
This takes seconds but catches many issues early.
Commit your lockfile to git. It locks your entire dependency tree. Without it, installs are not reproducible, and you lose the security benefit of knowing exactly what was installed.
Supply chain caution
Every package you install can run code during installation through:
preinstallscripts: run before package is installedpostinstallscripts: run after package is installedpreuninstallscripts: run before package is removed
These scripts can do anything: download files, run commands, access your network. Most are legitimate (compiling native code, downloading assets), but they create risk.
Package authors can be compromised. A maintainer’s credentials might be stolen. A popular package might be transferred to a malicious maintainer.
You are trusting:
- The package author
- The package author’s security practices
- The registry’s security
- Every maintainer who has publish access
Real example: axios incident (2026)
In early 2026, the popular HTTP library axios was compromised. An attacker gained publish access and released a version with malicious code that stole credentials from environment variables.
The attack:
- Attacker gained access to a maintainer’s npm account
- Published a patch version (1.6.6) with the malicious code
- The malicious version was live for several hours before discovery
- Projects that auto-updated or ran
npm installduring that window were affected
The clean-up:
- The malicious version was removed from npm
- A security advisory was issued
- Users were told to check if they installed the compromised version
- Those affected needed to rotate any exposed credentials
The axios incident shows that even popular, well-maintained packages can be compromised. Regular security practices help limit damage:
- Lockfiles prevent silent updates
- Audit catches known vulnerabilities
- Environment variable hygiene limits credential exposure
Package trust
Before installing a package, consider:
- Download count: Popular packages are more scrutinized
- Maintainer reputation: Established maintainers are more trustworthy
- Recent activity: Actively maintained packages fix vulnerabilities faster
- Scope packages:
@company/packageis tied to that company - Verification: Some packages are verified by npm
This isn’t perfect — popular packages have been compromised — but it’s a useful filter.
Practical security checklist
- Run
npm auditregularly - Review lockfile changes
- Update dependencies frequently (old versions have more known vulnerabilities)
- Use
npm ci/pnpm install --frozen-lockfilein CI - Pin dependency versions in critical applications
- Monitor for suspicious package behavior
- Keep credentials out of environment variables when possible
- Use private registries for truly private code
- Read security advisories for critical vulnerabilities
- Have a response plan for when a vulnerability is discovered
Security is ongoing
Security isn’t a one-time check. New vulnerabilities are discovered constantly. Keep dependencies updated, run audit regularly, and review changes carefully.
The JavaScript ecosystem moves fast. Stay informed, but don’t panic. Not every vulnerability affects your application. Use tools like audit for information, then make informed decisions about what to fix.